1 问题描述:
用hibernate做修改和删除的时候,有时候会遇到 org.hibernate.NonUniqueObjectException 异常,
a different object with the same identifier value was already associated with the session
可以解释为 有一个具有相同值的不同对象已经与和 session 相关联。
google了哈,http://www.blogjava.net/Unmi/archive/2007/08/10/135771.html 的办法是:
重现以上错误的代码如下(去除了事物控制的代码行):
1. Session session = HibernateSessionFactory.getSession();
2.
3. // 加载OID为1L的对象,会被放在session缓存中
4. LoanDetail detail = (LoanDetail)session.get(LoanDetail.class,1L);
5.
6. // new 一个OID也为1L的临时对象
7. LoanDetail newDetail = new LoanDetail(1L);
8. ewDetail.setSubjectId(1000L);
9.
10. // 持久化一个临时对象,试图放在session的缓存中,因OID冲突出现异常
11. session.save(newDetail);
12.
13. // 执行saveOrUpdate同样会出现以上的异常
14. // session.saveOrUpdate(newDetail);
2 解决方法:
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 方法。
3)原作者的办法:
a. 在 session.update/delete/saveOrUpdate 前,先 session.clear();
b. 在 session.update/delete/saveOrUpdate 后,关闭session:session.close();
4) evict()
不管何时你传递一个对象给save(), update()或者 saveOrUpdate() ,或者不管何时你使用load(), find(), iterate()或者filter()取得一个对象的时候,该对象被加入到Session的内部缓存中。当后继的flush()被调用时,对象的状态会和数据库进行同步。如果你在处理大量对象并且需要有效的管理内存的时候,你可能不希望发生这种同步,evict()方法可以从缓存中去掉对象和它的集合。
Iterator cats = sess.iterate("from eg.Cat as cat"); //a huge result set
while ( cats.hasNext() ) {
Cat cat = (Cat) iter.next();
doSomethingWithACat(cat);
sess.evict(cat);
}
Session也提供了一个contains()方法来判断是否一个实例处于这个session的缓存中。
要把所有的对象从session缓存中完全清除,请调用Session.clear()。
For the JVM-level JCS cache, there are methods defined on SessionFactory for evicting the cached state of an instance, entire class, collection instance or entire collection role.
对于第二层缓存来说,在SessionFactory中定义了一些方法来从缓存中清除一个实例、整个类、集合实例或者整个集合。
3 原因分析
这个异常困扰了我一个星期,冥思不得其解,且在网上搜的方法都是“点到为止”,所以看了依然一头雾水。
今天突然灵机一动,该问题实质上是Hibernate Session机制管理的问题,于是搜到了一篇文章(见前一篇),着重讲 Hibernate缓存机制 以及 Hibernate对象的状态。看了以后,终于明白了缘由!
PS: 透过问题,看本质是最重要的。
遇到问题,别急着去网上找;最好先理理思路,对问题进行较精确的归类,然后再去查找,“有的放矢”效率会更高。