我在 JPA 中找不到执行此操作的方法,因此我使用了 Hibernate EJB3 事件侦听器。我骑过saveWithGeneratedId 使用反射来检查实体是否有@Id 注释,然后检查该字段的值。如果它有一个值,那么我改为调用saveWithRequestedId。否则,我让它生成 ID。这很有效,因为如果我需要一个 ID,我仍然可以使用为 Hibernate 设置的序列。反射可能会增加开销,因此我可能会对其进行一些更改。我正在考虑在所有实体中使用getId() 或getPK() 方法,因此我不必搜索哪个字段是@Id。
在我使用反射之前,我尝试调用 session.getIdentifier(entity) 来检查,但我得到了 TransientObjectException("The instance was not associated with this session")。我不知道如何在不先保存实体的情况下让实体进入会话,所以我放弃了。下面是我写的监听代码。
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"/>