Как применить последовательность в атрибуте объекта JPA? [Дубликат]

Я просто сделал это на основе некоторого аналогичного кода, который у меня уже был, он работает:

Object.byString = function(o, s) {
    s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
    s = s.replace(/^\./, '');           // strip a leading dot
    var a = s.split('.');
    for (var i = 0, n = a.length; i < n; ++i) {
        var k = a[i];
        if (k in o) {
            o = o[k];
        } else {
            return;
        }
    }
    return o;
}

Использование ::

Object.byString(someObj, 'part3[0].name');

См. рабочую демонстрацию в http://jsfiddle.net/alnitak/hEsys/

ИЗМЕНИТЬ, некоторые заметили, что этот код выдает ошибку, если передается строка, где самые левые индексы не соответствуют к правильно вложенной записи внутри объекта. Это актуальная проблема, но IMHO лучше всего обращается с блоком try / catch при вызове, вместо того, чтобы эта функция молча возвращать undefined для недопустимого индекса.

98
задан Miguel Ping 13 November 2008 в 01:07
поделиться

12 ответов

Ищете ответы на эту проблему, я наткнулся на эту ссылку

Кажется, что Hibernate / JPA не может автоматически создать значение для вашего не-id- свойства. Аннотации @GeneratedValue используются только в сочетании с @Id для создания автоматических номеров.

Аннотация @GeneratedValue просто сообщает Hibernate, что база данных сама генерирует это значение.

Решение (или обход), предлагаемое на этом форуме, заключается в создании отдельного объекта с генерируемым идентификатором, что-то вроде этого:

@Entity
public class GeneralSequenceNumber {
  @Id
  @GeneratedValue(...)
  private Long number;
}

@Entity 
public class MyEntity {
  @Id ..
  private Long id;

  @OneToOne(...)
  private GeneralSequnceNumber myVal;
}
52
ответ дан Vlad Mihalcea 24 August 2018 в 01:24
поделиться
  • 1
    Из java-документа @GeneratedValue: «Аннотации GeneratedValue могут применяться к свойству первичного ключа или полю объекта или сопоставленного суперкласса в сочетании с аннотацией идентификатора». – Kariem 10 December 2009 в 15:50
  • 2
    Я обнаружил, что @Column (columnDefinition = «serial») работает отлично, но только для PostgreSQL. Для меня это было идеальным решением, потому что второй объект является «уродливым». вариант – Sergey Vedernikov 12 May 2012 в 12:51
  • 3
    @SergeyVedernikov, который был чрезвычайно полезным. Не могли бы вы опубликовать это как отдельный ответ? Она решила мою проблему очень просто и эффективно. – Matt Ball 17 May 2012 в 16:26
  • 4
    @MattBall я разместил это как отдельный ответ :) stackoverflow.com/a/10647933/620858 – Sergey Vedernikov 18 May 2012 в 07:45
  • 5
    Я открыл предложение разрешить @GeneratedValue по полям, которые не являются идентификаторами. Проголосовать за включение в 2.2 java.net/jira/browse/JPA_SPEC-113 – Petar Tahchiev 5 April 2016 в 11:40

Хотя это старый поток, я хочу поделиться своим решением и, надеюсь, получить от него некоторые отзывы. Будьте предупреждены, что я тестировал это решение только с моей локальной базой данных в каком-то тестовом сценарии JUnit. Таким образом, пока это не является продуктивной функцией.

Я решил эту проблему для себя, введя пользовательскую аннотацию, называемую Sequence без свойства.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Sequence
{
}

Используя эту аннотацию, я отметил свои сущности.

public class Area extends BaseEntity implements ClientAware, IssuerAware
{
    @Column(name = "areaNumber", updatable = false)
    @Sequence
    private Integer areaNumber;
....
}

Чтобы сохранить базу данных независимо от нее Я ввел объект под названием SequenceNumber, который содержит текущее значение последовательности и размер приращения. Я выбрал className как уникальный ключ, поэтому каждый класс сущности получит свою собственную последовательность.

@Entity
@Table(name = "SequenceNumber", uniqueConstraints = { @UniqueConstraint(columnNames = { "className" }) })
public class SequenceNumber
{
    @Id
    @Column(name = "className", updatable = false)
    private String className;

    @Column(name = "nextValue")
    private Integer nextValue = 1;

    @Column(name = "incrementValue")
    private Integer incrementValue = 10;

    ... some getters and setters ....
}

Последний шаг и самый сложный - это PreInsertListener, который обрабатывает присвоение порядкового номера. Обратите внимание, что я использовал весну в качестве контейнера для бобов.

@Component
public class SequenceListener implements PreInsertEventListener
{
    private static final long serialVersionUID = 7946581162328559098L;
    private final static Logger log = Logger.getLogger(SequenceListener.class);

    @Autowired
    private SessionFactoryImplementor sessionFactoryImpl;

    private final Map<String, CacheEntry> cache = new HashMap<>();

    @PostConstruct
    public void selfRegister()
    {
        // As you might expect, an EventListenerRegistry is the place with which event listeners are registered
        // It is a service so we look it up using the service registry
        final EventListenerRegistry eventListenerRegistry = sessionFactoryImpl.getServiceRegistry().getService(EventListenerRegistry.class);

        // add the listener to the end of the listener chain
        eventListenerRegistry.appendListeners(EventType.PRE_INSERT, this);
    }

    @Override
    public boolean onPreInsert(PreInsertEvent p_event)
    {
        updateSequenceValue(p_event.getEntity(), p_event.getState(), p_event.getPersister().getPropertyNames());

        return false;
    }

    private void updateSequenceValue(Object p_entity, Object[] p_state, String[] p_propertyNames)
    {
        try
        {
            List<Field> fields = ReflectUtil.getFields(p_entity.getClass(), null, Sequence.class);

            if (!fields.isEmpty())
            {
                if (log.isDebugEnabled())
                {
                    log.debug("Intercepted custom sequence entity.");
                }

                for (Field field : fields)
                {
                    Integer value = getSequenceNumber(p_entity.getClass().getName());

                    field.setAccessible(true);
                    field.set(p_entity, value);
                    setPropertyState(p_state, p_propertyNames, field.getName(), value);

                    if (log.isDebugEnabled())
                    {
                        LogMF.debug(log, "Set {0} property to {1}.", new Object[] { field, value });
                    }
                }
            }
        }
        catch (Exception e)
        {
            log.error("Failed to set sequence property.", e);
        }
    }

    private Integer getSequenceNumber(String p_className)
    {
        synchronized (cache)
        {
            CacheEntry current = cache.get(p_className);

            // not in cache yet => load from database
            if ((current == null) || current.isEmpty())
            {
                boolean insert = false;
                StatelessSession session = sessionFactoryImpl.openStatelessSession();
                session.beginTransaction();

                SequenceNumber sequenceNumber = (SequenceNumber) session.get(SequenceNumber.class, p_className);

                // not in database yet => create new sequence
                if (sequenceNumber == null)
                {
                    sequenceNumber = new SequenceNumber();
                    sequenceNumber.setClassName(p_className);
                    insert = true;
                }

                current = new CacheEntry(sequenceNumber.getNextValue() + sequenceNumber.getIncrementValue(), sequenceNumber.getNextValue());
                cache.put(p_className, current);
                sequenceNumber.setNextValue(sequenceNumber.getNextValue() + sequenceNumber.getIncrementValue());

                if (insert)
                {
                    session.insert(sequenceNumber);
                }
                else
                {
                    session.update(sequenceNumber);
                }
                session.getTransaction().commit();
                session.close();
            }

            return current.next();
        }
    }

    private void setPropertyState(Object[] propertyStates, String[] propertyNames, String propertyName, Object propertyState)
    {
        for (int i = 0; i < propertyNames.length; i++)
        {
            if (propertyName.equals(propertyNames[i]))
            {
                propertyStates[i] = propertyState;
                return;
            }
        }
    }

    private static class CacheEntry
    {
        private int current;
        private final int limit;

        public CacheEntry(final int p_limit, final int p_current)
        {
            current = p_current;
            limit = p_limit;
        }

        public Integer next()
        {
            return current++;
        }

        public boolean isEmpty()
        {
            return current >= limit;
        }
    }
}

Как вы можете видеть из приведенного выше кода, слушатель использовал один экземпляр SequenceNumber для класса сущности и резервирует пару порядковых номеров, определяемых incrementValue последовательности SequenceNumber организация. Если он не соответствует порядковым номерам, он загружает объект SequenceNumber для целевого класса и резервирует значения incrementValue для следующих вызовов. Таким образом, мне не нужно запрашивать базу данных каждый раз, когда требуется значение последовательности. Обратите внимание на StatelessSession, который открывается для резервирования следующего набора порядковых номеров. Вы не можете использовать один и тот же сеанс, который в настоящее время сохраняется в целевом объекте, поскольку это приведет к исключению ConcurrentModificationException в EntityPersister.

Надеюсь, это поможет кому-то.

4
ответ дан Akhil Jain 24 August 2018 в 01:24
поделиться

«Я не хочу использовать триггер или любую другую вещь, кроме самого Hibernate, для генерации значения для моего свойства»

. В этом случае, как насчет создания реализации UserType, который генерирует требуемое значение и конфигурирование метаданных для использования этого UserType для сохранения свойства mySequenceVal?

12
ответ дан alasdairg 24 August 2018 в 01:24
поделиться

Я был в такой ситуации, как вы (последовательность JPA / Hibernate для поля non @Id), и я закончил создание триггера в моей схеме db, которая добавляет уникальный уникальный номер вставки. Я просто не получил его для работы с JPA / Hibernate

-1
ответ дан Frederic Morin 24 August 2018 в 01:24
поделиться

Я нашел эту конкретную заметку в сеансе 9.1.9 Аннотацию GeneratedValue из спецификации JPA: «[43] Портативные приложения не должны использовать аннотацию GeneratedValue для других постоянных полей или свойств». Поэтому я предполагаю, что невозможно автоматически генерировать значение для значений не первичного ключа, по крайней мере, используя просто JPA.

1
ответ дан Gustavo Orair 24 August 2018 в 01:24
поделиться

Это не то же самое, что использовать последовательность. При использовании последовательности вы ничего не вставляете и не обновляете. Вы просто извлекаете следующее значение последовательности. Похоже, что hibernate не поддерживает его.

0
ответ дан kammy 24 August 2018 в 01:24
поделиться

Я установил генерацию UUID (или последовательностей) с помощью Hibernate с использованием аннотации @PrePersist:

@PrePersist
public void initializeUUID() {
    if (uuid == null) {
        uuid = UUID.randomUUID().toString();
    }
}
2
ответ дан Matroska 24 August 2018 в 01:24
поделиться

Как показано ниже, вот как я заработал:

@Override public Long getNextExternalId() {
    BigDecimal seq =
        (BigDecimal)((List)em.createNativeQuery("select col_msd_external_id_seq.nextval from dual").getResultList()).get(0);
    return seq.longValue();
}
6
ответ дан Matt Ball 24 August 2018 в 01:24
поделиться
  • 1
    Вариант с Hibernate 4.2.19 и оракул: SQLQuery sqlQuery = getSession().createSQLQuery("select NAMED_SEQ.nextval seq from dual"); sqlQuery.addScalar("seq", LongType.INSTANCE); return (Long) sqlQuery.uniqueResult(); – Aaron 20 July 2015 в 10:47

Я знаю, что это очень старый вопрос, но он сначала проявляется в результатах, и jpa сильно изменился со времени вопроса.

Правильный способ сделать это теперь с помощью аннотации @Generated , Вы можете определить последовательность, установить значение по умолчанию в столбце этой последовательности и затем сопоставить столбец как:

@Generated(GenerationTime.INSERT)
@Column(name = "column_name", insertable = false)
15
ответ дан Rumal 24 August 2018 в 01:24
поделиться
  • 1
    Это все еще требует, чтобы значение генерировалось базой данных, что на самом деле не отвечает на вопрос. Для баз данных Oracle до 12c вам все равно нужно написать триггер базы данных для генерации значения. – Bernie 12 June 2014 в 02:15
  • 2
    Кроме того, это аннотация Hibernate, а не JPA. – caarlos0 8 September 2014 в 13:19

Я обнаружил, что @Column(columnDefinition="serial") работает идеально, но только для PostgreSQL. Для меня это было идеальным решением, потому что второй объект является «уродливым» вариантом.

29
ответ дан Sergey Vedernikov 24 August 2018 в 01:24
поделиться
  • 1
    Привет, мне нужно объяснение. Не могли бы вы рассказать мне больше, пожалуйста? – Emaborsa 13 May 2014 в 10:00
  • 2
    @Emaborsa columnDefinition= бит в основном говорит Hiberate не пытаться генерировать определение столбца и вместо этого использовать текст, который вы указали. По сути, ваш DDL для столбца будет буквально просто name + columnDefinition. В этом случае (PostgreSQL) mycolumn serial является допустимым столбцом в таблице. – Patrick 23 May 2014 в 19:15
  • 3
    Для MySQL эквивалент @Column(columnDefinition = "integer auto_increment") – Richard Kennard 28 September 2014 в 11:38
  • 4
    Это автоматически генерирует свою ценность? Я попытался сохранить объект с таким полем, как это, но он не генерировал значение. он выбрал нулевое значение в столбце & lt; column & gt; нарушает ненулевое ограничение – KyelJmD 22 August 2015 в 07:29
  • 5
    @KyelJmD для меня это сработало и возвращало сгенерированное значение в PostgreDB. Попробуйте очистить сеанс – Sergey Vedernikov 26 August 2015 в 11:36

После частых часов это помогло мне решить мою проблему:

Для Oracle 12c:

ID NUMBER GENERATED as IDENTITY

Для H2:

ID BIGINT GENERATED as auto_increment

Также сделайте:

@Column(insertable = false)
-1
ответ дан Spring 24 August 2018 в 01:24
поделиться

Я работаю в той же ситуации, что и вы и я также не нашли серьезных ответов, если в принципе можно генерировать свойства без id с JPA или нет.

Мое решение - вызвать последовательность с собственным запросом JPA, чтобы установить свойство вручную, прежде чем его перенести.

Это не удовлетворяет, но оно работает как обходной момент на данный момент.

Mario

3
ответ дан user 24 August 2018 в 01:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: