Как узнать идентификатор перед сохранением объекта в jpa

У меня есть новый объект. Я хочу знать идентификатор, прежде чем сохранять его. Является ли это возможным? Или есть другой способ для этого? Я использую jpa как orm и oracle как базу данных.

@Id
@Basic(optional = false)
@Column(name = "ID", nullable = false)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "woTypeSeq")
private Long id;

У меня есть поле кода в моей сущности. Если пользователь не вводит значение для поля code, я хочу установить код как идентификатор объекта. Если я сохраню объект, конечно, я могу получить id и установить значение кода как id, но это дополнительная база запросов.

Я хочу сделать что-то подобное

if(entity.getCode()==null) {
   entity.setCode(entity.getId);
   jpaDao.saveOrUpdate(entity);
}

Ответ 1

После долгих исследований об этом, наконец, я нашел решение.

Фактически, если вы используете генератор последовательности в jpa, вы не сможете получить идентификатор объекта перед сохранением его в базе данных, потому что следующий идентификатор будет назначен последовательностью базы данных.

Существует один способ получить идентификатор, если вы используете настраиваемый генератор, вы можете получить идентификатор перед сохранением. Вот простая реализация:

public class CustomGenerator extends IdentityGenerator implements Configurable {

    private IdentifierGenerator defaultGenerator;

    public Serializable generate(SessionImplementor session, Object object) throws HibernateException {
        Long idValue = (Long)defaultGenerator.generate(session, object);
        //idValue will be assigned your entity id
        return idValue;
    }

    @Override
    public void configure(Type type, Properties params, Dialect d) throws MappingException {
        DefaultIdentifierGeneratorFactory dd = new DefaultIdentifierGeneratorFactory();
        dd.setDialect(d);
        defaultGenerator = dd.createIdentifierGenerator("sequence", type, params);
    }
}

Использование CustomGenerator для поля id:

@Id
@Basic(optional = false)
@Column(name = "ID", nullable = false)
@GenericGenerator(name = "seq_id", strategy = "com.yoncabt.abys.core.listener.CustomGenerator", parameters = { @Parameter(name = "sequence", value = "II_FIRM_DOC_PRM_SEQ") })
@GeneratedValue(generator = "seq_id")
private Long id;

Ответ 2

С идентификатором типа @GeneratedValue вы не можете заранее знать это значение (до его написания). Однако, как только вы сохраняете свой Bean, поле id будет заполнено в этом экземпляре bean, и вы можете получить его, не требуя для этого дополнительного запроса. Другими словами:

MyEntiry myEnt = new MyEntity(); //the id field is null now
entityManager.persist(myEnt);//the id field is populated in myEnt now
Long id = myEnt.getId();

Кроме того, в зависимости от того, как настроен ваш EntityManager, вам может понадобиться также сначала совершить транзакцию (вручную), прежде чем вы сможете получить этот идентификатор.

Обновить как комментарий

Если вы хотите перехватить и сделать что-то для сущности до того, как он будет сохранен и/или обновлен, вы можете использовать прослушиватели JPA LifeCycle Listeners (если вы используете JPA-версию 2): Обработка события жизненного цикла JPA с использованием прослушивателей и обратных вызовов.

В принципе, вы можете сделать метод validate() в Bean, аннотировать его с помощью @PrePersist и @PreUpdate и выполнить проверку в нем (если код пуст, установите его на значение id)

Обновление за 2-й комментарий

Да, я, честно говоря, просто подумал об этом только сейчас: что, если идентификатор сгенерирован автоматически, он может заселиться ПОСЛЕ ПРЕДСТОЯЩЕГО события, так что когда ваш код сохраняется до конца, вы все равно не знаете, что id (вы можете заметить также, что в примере, который вы ссылаетесь на идентификатор, НЕ автогенерируется, а устанавливается вручную). Что вы можете сделать в этом случае - добавить логическое поле в вашу сущность (аннотируется с помощью @Transient, чтобы он не сохранялся), называемый isCodeEmpty (который по умолчанию неверен, если не специально инициализирован). Затем в вашем аннотированном методе @PrePersist вы проверяете, пусто ли значение для поля кода, и если да, установите для boolean значение true. Затем вы реорганизуете свой метод setId(...) таким образом, чтобы (помимо установки поля id) он проверял это логическое значение, и если true, установите значение поля кода в поле поля id:

public class YourEntity {

@Transient
private boolean isCodeEmpty;

public void setId(Whatever id) {
 this.id = id;
 if(isCodeEmpty) {
  this.code = id;
  //if necessary:
  //this.isCodeEmpty = false;
 }
}

@PrePersist
public void validate() {
 if(code == null || code.isEmpty()) {
  isCodeEmpty = true;
 }

}


}

Ответ 3

Просто выполните следующие действия, после чего вы получите идентификатор до завершения транзакции. 1-й шаг добавляет прослушиватель сущности на уровне вашей сущности с помощью аннотации "@EntityListeners".

@Id
@Basic(optional = false)
@Column(name = "ID", nullable = false)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "woTypeSeq")
@EntityListeners(SomeLinstener.class)
private Long id;

2-й шаг

public class SomeLinstener{

    @PostPersist
    public void userPostPersist(YourEntity obj) {

        try {
            obj.getId();
        }catch(Exception e) {

        }
    } 

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

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