Hibernate 保存对象出现 org.hibernate.NonUniqueObjectException 及解决

Hibernate save 在 session 中已存在相同 OID(主键) 的对象,会出现异常,详细内容如下:

Exception in thread "main" org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.unmi.LoanDetail#1]
 at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:168)
 at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:121)
 at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:187)
 at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:33)
 at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:172)
 at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:27)
 at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
 at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:535)
 at org.hibernate.impl.SessionImpl.save(SessionImpl.java:523)
 at org.hibernate.impl.SessionImpl.save(SessionImpl.java:519)
 at com.unmi.Test.main(Test.java:44)

重现以上错误的代码如下(去除了事物控制的代码行):

解决方法:
   1) 如果用的 hibernate 2, 需要在get/load/query到持久化对象,赋上新的属性值,再 save/update/saveOrupdate.
      对以上代码就是:不能 new 一个session中已存在OID的对象,直接
      detail.setSubjectId(1000L);
      session.save(detail);
      session.save()一个持久化对象时,会转化成update调用。

   2) 使用 hibernate 3 的 merge 方法. session.merge(newDetail)即可,它会在 session 缓存中找到持久化对象,把新对象的属性赋过去,再保存原session中的持久化对象。
      如果在session或数据库中没有的对象,用merge方法的话,它也能够帮你把记录 insert 到表中,相当于 save 方法。

上面是一个简单的例子,实际业务中可能是经过一番复杂的操作后自己也很难搞清楚 new 的一个新对象在 session/数据库中是否已存在。所以第一种方法你需要清楚你的每一个对象状态,第二种方法在 hibernate 3 中就比较通用一些。

附 hibernate javadoc 对 session.merge() 方法的注释:
Copy the state of the given object onto the persistent object with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. Return the persistent instance. If the given instance is unsaved, save a copy of and return it as a newly persistent instance. The given instance does not become associated with the session. This operation cascades to associated instances if the association is mapped with cascade="merge".

The semantics of this method are defined by JSR-220.

本文链接 https://yanbin.blog/hibernate-save-hibernate-nonuniqueobjectexception/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

7 Comments
Inline Feedbacks
View all comments
zhq
zhq
16 years ago

相同的问题,我试了一下,已解决。

zhq
zhq
16 years ago

呵呵。谢谢前辈分享的资料

student_cui
student_cui
16 years ago

真是太感谢了,这个问题调试了半天了,终于在这儿找到了答案。

JAVA
JAVA
16 years ago

有更简单的解决方案。。。

songyuliang
songyuliang
16 years ago

谢谢你!!

zilan
zilan
15 years ago

我并没有加载OID为1L的对象,放在session缓存中,我直接new一个User类和一个ContactInfo类,这两个类是一对一的cascade="all"的关联关系,我在save(User,ContactInfo)的时候出现的这个错误,不知道为什么;

游客
游客
14 years ago

我个人觉得用merge()方法不是很妥,有性能影响