2012-02-09 26 views
0

我爲我的web應用程序使用每個請求模型的hibernate會話。我的jdbc事務從每個Web請求的開始處開始並在最後提交。hibernate多線程防止多重保存(),JTA有必要嗎?

//非託管環境成語

Session sess = factory.openSession(); 
Transaction tx = null; 
try { 
    tx = sess.beginTransaction(); 

    // do some work 
    ... 

    tx.commit(); 
} 
catch (RuntimeException e) { 
    if (tx != null) tx.rollback(); 
    throw e; // or display error message 
} 
finally { 
    sess.close(); 
} 

我面對,我正在測試基於幾個參數實體(A)的存在和執行INSERT只有當它不」的問題不存在。

public synchronized myMethod(param1, param2) { 
    MyEntityA entity = MyEntityADAO.findEntity(param1, param2) 
    if (entity == null) { 
     entity = .../create entity 
     MyEntityADAO.save(entity); 
    } 
} 

問題是同步沒有幫助,因爲調用MyEntityADAO.save()實際上並沒有寫入到數據庫時,當前正在運行的線程退出的方法和釋放鎖,寫入到數據庫時在交易完成之後,除了一些場景之外,這通常是我的應用程序所需要的。上面的代碼會導致在多線程環境中使用相同參數保存多個記錄。

我試着在自己的新的會話和交易執行保存代碼:以上

public synchronized myMethod(param1, param2) { 
    MyEntityA entity = MyEntityADAO.findEntity(param1, param2) 
    if (entity == null) { 
     entity = .../create entity 
     Session session = HibernateUtil.createSession(); 
     MyEntityADAO.save(entity); 
     Transaction t = session.beginTransaction(); 

    } 
} 

導致與2個打開的會話加載同一個集合,在某些情況下,休眠的問題。

我是否應該將每個DAO調用放在自己的事務中,並使用與JTA的事務傳播?有沒有辦法避免JTA?在調用MyEntityADAO.save()後立即調用與主會話相關的事務,並在主會話之後立即調用beginTransaction,並在請求結束時讓事務處理提交,就像現在一樣?

回答

0

解決方案適用於我和我的特定應用程序要求,試圖避免JTA和嵌套事務:

使用ManagedSessionContext,因爲org.hibe rnate.context.ThreadLocalSessionContext將關閉併爲每個事務創建一個新會話。如果您在多個打開的會話中加載這些實體(當您爲一個請求創建多個事務時),您將遇到與具有集合關聯的實體的問題。

  • 我打開一個Hibernate會話,並將其綁定到我的web請求開始的背景下
  • 需要事先存在插入測試任何服務層方法被標記同步,全球交易將提交與insert語句和一個新的事務啓動
  • 在結束會話綁定該交易將提交

    公共同步myMethod的(參數1,參數2){

    0123請求

    }

我知道它的醜陋,不會對每個人在每一個之情況的工作,但做事務管理,隔離級別,鎖定一個非常激烈的搜索,版本是我唯一的解決方案後,發現爲我工作。我不使用Spring,而我沒有使用Java EE容器,使用Tomcat 6

0

數據庫中數據的一致性不應該通過在其自己的事務中進行原子更改的某些部分來降低。儘管某些同步可能適用於您的環境,但如果您需要羣集您的應用程序,或者有多個應用程序訪問數據庫,則無法解決問題。

你應該做的是在[param1 - param2]的數據庫中加入一個唯一的約束。如果存在競爭條件,這將導致兩個事務之一回滾。

如果您選擇仍然在自己的事務中隔離檢查/插入代碼(因爲如果成功並且外部事務失敗,這不是問題),我不明白JTA會是一個問題。假設您正在使用EJB或Spring,只需將此方法放入其自己的EJB/bean中,並使用REQUIRES_NEW傳播將該方法標記爲事務性。

這樣的代碼應該是這樣的:

// some code 
Long id = myBean.checkIfExistOrCreate(param1, param2); // this methos call starts a new transaction 
// now we're sure that the entity exists. Load it in the current session. 
MyEntity e = em.find(MyEntity.class, id); 

如果您不能同步checkIfExistOrCreate,然後再嘗試調用它,捕捉任何異常,它可以拋出,然後重試稱之爲:

Long id = null; 
try { 
    id = myBean.checkIfExistOrCreate(param1, param2); 
} 
catch (Exception e) { // a well-defined exception would be better 
    // the transaction roled back: retry 
    id = myBean.checkIfExistOrCreate(param1, param2); 
} 
// now we're sure that the entity exists. Load it in the current session. 
MyEntity e = em.find(MyEntity.class, id); 
+0

感謝您的輸入。我確實設置了約束,但是我沒有捕獲dao/service層中的任何hibernate/jdbc異常。似乎是一個好主意。謝謝。 – user979051 2012-02-09 22:52:31

+0

我捕捉到ConstraintViolationException並嘗試獲取catch塊中的現有實體並獲取null..is有一些中間階段,當事務標記爲底層數據庫(mysql)中的「completed」和表約束是「更新」,但數據庫不會從表中返回新行? – user979051 2012-02-09 23:10:48

+0

我已經嘗試刷新會話,而不是在save()之後的同步方法/塊內獲得異常但不是仍然沒有解決問題 – user979051 2012-02-09 23:26:10