天天看點

a different object with the same identifier value was already associated with the session 産生原因分析

今天做WOSS項目的時候修改那一子產品突然給我抛了個org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.briup.dao.bean.Product#1008] 一看一頭2個大 ,這什麼破異常 ,從沒遇到過嘛

遇到該異常感覺無從下手

 從字面上的意思來看是2個不同的對象關聯到了同一個标志位,也就是說在一個session中存在2個對象但這

 2個對象的标志位是相同的比如2個對象的 id值是一樣的 而這又是資料庫的唯一主鍵

 在做更新操作的時候就出現沖突了,因為Hibernate不知道到底要去更新哪個對象

 出現這樣的問題頭都大了,後來 仔細分析了下,做前面子產品的時候測試是通過了的,那為什麼後面就不行了呢?

 沒辦法隻好将原來的版本拿過來重新部署測試,發現在頁面送出修改資料背景做儲存的時候不在抛這個異常了

 将資料庫發生變化導緻異常的情況排出,唯一的可能就是前面做的代碼被修改過,是以導緻後面在去回歸測試的時候

 出現異常,通過比較2個版本更新子產品的不 同之處後發現問題暴露出來了

 DAO層方法

 public void modifyProduct(Product product) throws Exception {

  session = HibernateSessionFactory.getSession();

  session.update(product);

 }

 public Product findProductByName(String name) throws Exception {

  session = HibernateSessionFactory.getSession();

  query = session.createQuery("from Product where name='" + name + "'");

  return (Product) query.uniqueResult();

 }

 看下面的業務邏輯方法

 public void modifyProduct(Product product) throws AdminServiceException {

  HibernateTransaction tran = new HibernateTransaction();

  tran.beginTransaction();

  try {

   dao.modifyProduct(product);

   tran.commit();

  } catch (Exception e) {

   tran.rollback();

   e.printStackTrace();

   throw new AdminServiceException(

     FinalErrorMsg.DATA_BASE_UPDATE_ERROR);

  }

 }

 public void modifyProduct(Product product) throws AdminServiceException {

  Product pro = null;

  pro = findProductByName(product.getName());

  if (pro == null) {

   throw new AdminServiceException(

     FinalErrorMsg.MODIFY_PRODUCT_FAIL_BECAUSE_NOT_EXISTED);

  }

  HibernateTransaction tran = new HibernateTransaction();

  tran.beginTransaction();

  try {

   dao.modifyProduct(product);

   tran.commit();

  } catch (Exception e) {

   tran.rollback();

   e.printStackTrace();

   throw new AdminServiceException(

     FinalErrorMsg.DATA_BASE_UPDATE_ERROR);

  }

 }

 當Struts架構在調業務邏輯方法修改頁面傳過來的時候資料的時候 會去調modifyProduct方法進行修改

 在新版本中就有個非常隐蔽的錯誤了,注意看在同一個業務邏輯方法中調了2次DAO層的方法,如果要修改的對象在

 資料庫中還存在就有個問題産生了,該方法獲得的session管理了一個名為pro的對象而這個對象的id和方法傳入的

 product的id是一樣的是以 當在本方法中再去調DAO層的modifyProduct方法的時候獲得的session是和調

 findProductByName這個業務方法獲得的一個session是同一個 ,因為我把session的關閉操作放到頁面請求

 處理結束response回去的時候通過那個字元編碼轉化的filter過濾的是執行完doFilter方法後做的關閉

 Hibernate Session操作來實作送出資料到資料庫的 是以最終在這個修改的業務邏輯中使用2次Dao層的方法

 而獲得的是同一個session 是以要修改一個和該session管理的一個對象一樣的identifier的對象會出現沖突.

解決辦法有在該方法中執行dao.modifyProduct(product);前先将session關閉 調用Session的 close方法

Session的緩存被清空,其中的所有持久化對象都變為遊離對象,

調用Session的evict()方法能夠從緩存中删除一個持久化對象,使其變為遊離狀态,這樣也能達到解決同一個

session中關聯2個同一個identifer的對象更新的時候産生的沖突

 問題原因找到解決就好辦了,隻要把要考慮并發情況出現的使用者要修改的資料可能在送出後是否還存在的資料驗證放到struts調用該方法的Action中去判斷 就行了

從設計思想上看 修改的業務邏輯方法 還是做了它不該做的事就是去判斷被修改的對象是否還存在,這個應該交給調用它的上層邏輯去判斷

還有看來還是要老實的做單元測試,寫好某層的某方法就先做單元測試免得讓錯誤影響到後面的實作,最終導緻一時半會找不到錯誤的源頭