Как узнать идентификатор перед сохранением объекта в 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) {
}
}
это будет вам идентификатор, который был сгенерирован вашим генератором, и вы можете использовать его снова, когда это потребуется, это даст в одной транзакции.
надеюсь, это поможет кому-то еще