先大概說一下項目的需求:因為一個生產評估(Estimation)可能需要從一箇舊的版本的資料,因為他們都很想像,比如說這個工廠上次幫KFC生產了一批cake box,這是是麥當勞的訂單,也是需要生產一批蛋糕盒子,size和工藝和機器等都是差不多的,所以不比從新開始,而需要dulicate一下,然後進去稍微做些修改。所以我們需要從在後台做一個batch bob,從舊的資料複製些資料,大概是從9張表,先查詢,然後再insert。本來假如沒有使用hibernate的時候,邏輯很簡單,查詢,迴圈插入。但是後來卻發現一直由
“found shared references to a collection”!!!
困擾了大概一天時間,兩個人。後來發現是session和transaction這兩東東從中作梗。因為我們的資料是關聯的,設定了lazy=false。在service層先得到這個資料,方法是大概是這樣的
public void saveEstaimtionFromOldVersion(Integer oldVersionEstimationId){
List list =estimationDao.getEstiamtionById(oldVersionEstimationId);
for(Iterator it =list.iterator;it.hasNext();){
//processOneRecord
//save New data to 9 tables......
}
}
這個方法是寫在service層,我們的藉助spring的事務控制也是在這一層。
通過debug的時候,一切正常,但是當線程離開這個方法的時候,報了上面的這個錯誤。後來我們就懷疑是session沒有釋放從資料庫查詢出來的資料--官方成這個叫非託管的Bean,因為一直是在service層,session永遠都不會flush()注意這個flush()動作都是由spring來控制的;session不做flush()動作,所以裡面的哪些對象的reference一直存在,由於新的reference是在new出來的。後來我做了一個改動,就是把資料迴圈出來的時候,就把記憶體的哪些reference=null;這樣一切正常.但是這樣代碼裡充斥著很多這樣的跟業務無關的代碼,這些代碼就是使用hibernate的代價。
後來把程式改動了一下。
public void saveEstaimtionFromOldVersion(List oldEstimation){//這裡不是Integer,而是一個List
for(Iterator it =oldEstimation.iterator;it.hasNext();){
//processOneRecord
//save New data to 9 tables......
}
}
把這個查詢的資料的動作放在這個batchjob之前完成。。。
一切正常。。。
還有一個解決方案:假如不使用hibernate的級聯,關聯這些特性----remove all many2one,one2many,這樣通過hql或者sql查詢出來的資料也會正常。我猜想,通過sql來查詢的話,session會特殊處理,不會留有reference在session裡。
歡迎交流。