hibernate-事務管理

 Hibernate 是JDBC 的輕量級封裝,本身並不具備事務管理能力。在事務管理層, 
Hibernate將其委託給底層的JDBC或者JTA,以實現事務管理和調度功能。 
Hibernate的默認事務處理機制基於JDBC Transaction。我們也可以通過配置文 
件設定採用JTA作爲事務管理實現: 

Java代碼 
  1. <hibernate-configuration>  
  2. <session-factory>  
  3. ……  
  4. <property name="hibernate.transaction.factory_class">  
  5. net.sf.hibernate.transaction.JTATransactionFactory  
  6. <!--net.sf.hibernate.transaction.JDBCTransactionFactory-->  
  7. </property>  
  8. ……  
  9. </session-factory>  
  10. </hibernate-configuration>  


基於JDBC的事務管理將事務管理委託給JDBC 進行處理無疑是最簡單的實現方式,Hibernate 對於JDBC事務的封裝也極爲簡單。 
我們來看下面這段代碼: 

Java代碼 
  1. session = sessionFactory.openSession();  
  2. Transaction tx = session.beginTransaction();  
  3. ……  
  4. tx.commit();  


從JDBC層面而言,上面的代碼實際上對應着: 

Java代碼 
  1. Connection dbconn = getConnection();  
  2. dbconn.setAutoCommit(false);  
  3. ……  
  4. dbconn.commit();  


就是這麼簡單,Hibernate並沒有做更多的事情(實際上也沒法做更多的事情),只是將這樣的JDBC代碼進行了封裝而已。 
這裏要注意的是,在sessionFactory.openSession()中,hibernate會初始化數據庫連接,與此同時,將其AutoCommit 設爲關閉狀態(false)。而其後,在Session.beginTransaction 方法中,Hibernate 會再次確認Connection 的AutoCommit 屬性被設爲關閉狀態( 爲了防止用戶代碼對session 的Connection.AutoCommit屬性進行修改)。 
這也就是說,我們一開始從SessionFactory獲得的session,其自動提交屬性就已經被關閉(AutoCommit=false),下面的代碼將不會對數據庫產生任何效果: 

Java代碼 
  1. session = sessionFactory.openSession();  
  2. session.save(user);  
  3. session.close();  


這實際上相當於 JDBC Connection的AutoCommit屬性被設爲false,執行了若干JDBC操作之後,沒有調用commit操作即將Connection關閉。如果要使代碼真正作用到數據庫,我們必須顯式的調用Transaction指令: 

Java代碼 
  1. session = sessionFactory.openSession();  
  2. Transaction tx = session.beginTransaction();  
  3. session.save(user);  
  4. tx.commit();  
  5. session.close();  


基於JTA的事務管理 
JTA 提供了跨Session 的事務管理能力。這一點是與JDBC Transaction 最大的差異。 
JDBC事務由Connnection管理,也就是說,事務管理實際上是在JDBC Connection中實現。事務週期限於Connection的生命週期之類。同樣,對於基於JDBC Transaction的Hibernate 事務管理機制而言,事務管理在Session 所依託的JDBC Connection中實現,事務週期限於Session的生命週期。 
JTA 事務管理則由 JTA 容器實現,JTA 容器對當前加入事務的衆多Connection 進 
行調度,實現其事務性要求。JTA的事務週期可橫跨多個JDBC Connection生命週期。 
同樣對於基於JTA事務的Hibernate而言,JTA事務橫跨可橫跨多個Session。 
JTA 事務是由JTA Container 維護,而參與事務的Connection無需對事務管理進行干涉。這也就是說,如果採用JTA Transaction,我們不應該再調用HibernateTransaction功能。 
上面基於JDBC Transaction的正確代碼,這裏就會產生問題: 

Java代碼 
  1. public class ClassA{  
  2. public void saveUser(User user){  
  3. session = sessionFactory.openSession();  
  4. Transaction tx = session.beginTransaction();  
  5. session.save(user);  
  6. tx.commit();  
  7. session.close();  
  8. }  
  9. }  
  10. public class ClassB{  
  11. public void saveOrder(Order order){  
  12. session = sessionFactory.openSession();  
  13. Transaction tx = session.beginTransaction();  
  14. session.save(order);  
  15. tx.commit();  
  16. session.close();  
  17. }  
  18. }  
  19. public class ClassC{  
  20. public void save(){  
  21. ……  
  22. UserTransaction tx = new InitialContext().lookup(“……”);  
  23. ClassA.save(user);  
  24. ClassB.save(order);  
  25. tx.commit();  
  26. ……  
  27. }  
  28. }  

這裏有兩個類ClassA和ClassB,分別提供了兩個方法:saveUsersaveOrder, 
用於保存用戶信息和訂單信息。在ClassC中,我們接連調用了ClassA.saveUser方法和ClassB.saveOrder 方法,同時引入了JTA 中的UserTransaction 以實現ClassC.save方法中的事務性。問題出現了,ClassA 和ClassB 中分別都調用了Hibernate 的Transaction 功能。在Hibernate 的JTA 封裝中,Session.beginTransaction 同樣也執行了InitialContext.lookup方法獲取UserTransaction實例,Transaction.commit方法同樣也調用了UserTransaction.commit方法。實際上,這就形成了兩個嵌套式的JTA Transaction:ClassC 申明瞭一個事務,而在ClassC 事務週期內,ClassA 和ClassB也企圖申明自己的事務,這將導致運行期錯誤。因此,如果決定採用JTA Transaction,應避免再重複調用Hibernate 的 
Transaction功能,上面的代碼修改如下: 

Java代碼 
  1. public class ClassA{  
  2. public void save(TUser user){  
  3. session = sessionFactory.openSession();  
  4. session.save(user);  
  5. session.close();  
  6. }  
  7. ……  
  8. }  
  9. public class ClassB{  
  10. public void save (Order order){  
  11. session = sessionFactory.openSession();  
  12. session.save(order);  
  13. session.close();  
  14. }  
  15. ……  
  16. }  
  17. public class ClassC{  
  18. public void save(){  
  19. ……  
  20. UserTransaction tx = new InitialContext().lookup(“……”);  
  21. classA.save(user);  
  22. classB.save(order);  
  23. tx.commit();  
  24. ……  
  25. }  
  26. }  


上面代碼中的ClassC.save方法,也可以改成這樣: 

Java代碼 
  1. public class ClassC{  
  2. public void save(){  
  3. ……  
  4. session = sessionFactory.openSession();  
  5. Transaction tx = session.beginTransaction();  
  6. classA.save(user);  
  7. classB.save(order);  
  8. tx.commit();  
  9. ……  
  10. }  
  11. }  


實際上,這是利用Hibernate來完成啓動和提交UserTransaction的功能,但這樣的做法比原本直接通過InitialContext獲取UserTransaction 的做法消耗了更多的資源,得不償失。 
在EJB 中使用JTA Transaction 無疑最爲簡便,我們只需要將save 方法配置爲JTA事務支持即可,無需顯式申明任何事務,下面是一個Session Bean的save方法,它的事務屬性被申明爲“Required”,EJB容器將自動維護此方法執行過程中的事務: 

Java代碼 
  1. /** 
  2. * @ejb.interface-method 
  3. * view-type="remote" 
  4. * 
  5. * @ejb.transaction type = "Required" 
  6. **/  
  7. public void save(){  
  8. //EJB環境中,通過部署配置即可實現事務申明,而無需顯式調用事務  
  9. classA.save(user);  
  10. classB.save(log);  
  11. }//方法結束時,如果沒有異常發生,則事務由EJB容器自動提交。 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章