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: 透過問題,看本質是最重要的。
遇到問題,別急着去網上找;最好先理理思路,對問題進行較精確的歸類,然後再去查找,“有的放矢”效率會更高。