org.hibernate.NonUniqueObjectException學習

本文源自:http://wdmcln.javaeye.com/blog/154066

Exception:a different object with the same identifier value was already associated with the session
在session中具有相同標識符的不同對象已經關聯

1、手動控制事務時,每次持久化操作後將session清除或關閉(衡量性能);
2、覆寫對象的hashCode、equals方法,增加部分字段,避免只比較主鍵來區分不同的對象(Eclipse插件);
3、手動確保需要持久化的對象主鍵不重複;

Java代碼 複製代碼
  1. org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.legion.km.mapping.hibernate.pojo.TRRResrpt#23878]   
  2.     at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:167)   
  3.     at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:121)   
  4.     at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:186)   
  5.     at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:33)   
  6.     at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:175)   
  7.     at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:27)   
  8.     at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)   
  9.     at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:534)   
  10.     at org.hibernate.impl.SessionImpl.save(SessionImpl.java:522)   
  11.     at org.hibernate.impl.SessionImpl.save(SessionImpl.java:518)   
  12.     at com.legion.km.mapping.kmdb.impl.TargetDBImpl.insertData(TargetDBImpl.java:223)   
  13.     at com.legion.km.mapping.kmdb.impl.KMDBMapping.main(KMDBMapping.java:83)  
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.legion.km.mapping.hibernate.pojo.TRRResrpt#23878]
	at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:167)
	at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:121)
	at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:186)
	at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:33)
	at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:175)
	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:534)
	at org.hibernate.impl.SessionImpl.save(SessionImpl.java:522)
	at org.hibernate.impl.SessionImpl.save(SessionImpl.java:518)
	at com.legion.km.mapping.kmdb.impl.TargetDBImpl.insertData(TargetDBImpl.java:223)
	at com.legion.km.mapping.kmdb.impl.KMDBMapping.main(KMDBMapping.java:83)


   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.

 

 

解決方法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中定義了一些方法來從緩存中清除一個實例、整個類、集合實例或者整個集合。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章