Я не смог найти способ сделать это в JPA, поэтому я использовал прослушиватели событий Hibernate EJB3. Я перебрал saveWithGeneratedId
, чтобы использовать отражение, чтобы проверить сущность для аннотации @Id
, а затем проверить это поле на предмет значения. Если оно имеет значение, я звоню saveWithRequestedId
. В противном случае я позволю ему генерировать Id. Это работало хорошо, потому что я все еще могу использовать последовательность для Hibernate, которая настроена, если мне нужен Id. Отражение может добавить накладные расходы, поэтому я могу немного изменить его. Я думал о том, чтобы иметь метод getId()
или getPK()
во всех сущностях, поэтому мне не нужно искать, какое поле является @Id
.
Прежде чем использовать отражение, я пытался вызвать session.getIdentifier (entity) для проверки, но я получал TransientObjectException («Экземпляр не был связан с этим сеансом»). Я не мог понять, как получить сущность в сеанс, не сохранив ее сначала, поэтому я сдался. Ниже приведен код слушателя, который я написал.
public class MergeListener extends org.hibernate.ejb.event.EJB3MergeEventListener
{
@Override
protected Serializable saveWithGeneratedId(Object entity, String entityName, Object anything, EventSource source, boolean requiresImmediateIdAccess) {
Integer id = null;
Field[] declaredFields = entity.getClass().getDeclaredFields();
for (Field field : declaredFields) {
Id annotation = field.getAnnotation(javax.persistence.Id.class);
if(annotation!=null) {
try {
Method method = entity.getClass().getMethod("get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1));
Object invoke = method.invoke(entity);
id = (Integer)invoke;
} catch (Exception ex) {
//something failed (method not found..etc) , keep going anyway
}
break;
}
}
if(id == null ||
id == 0) {
return super.saveWithGeneratedId(entity, entityName, anything, source, requiresImmediateIdAccess);
} else {
return super.saveWithRequestedId(entity, id, entityName, anything, source);
}
}
}
Затем мне пришлось добавить слушателя в мой файл persistence.xml
<property name="hibernate.ejb.event.merge" value="my.package.MergeListener"/>