Hibernate 是JDBC 的輕量級封裝,本身并不具備事務管理能力。在事務管理層,
Hibernate將其委托給底層的JDBC或者JTA,以實現(xiàn)事務管理和調度功能。
Hibernate的默認事務處理機制基于JDBC Transaction。我們也可以通過配置文
件設定采用JTA作為事務管理實現(xiàn):
- <hibernate-configuration>
- <session-factory>
- ……
- <property name="hibernate.transaction.factory_class">
- net.sf.hibernate.transaction.JTATransactionFactory
- <!--net.sf.hibernate.transaction.JDBCTransactionFactory-->
- </property>
- ……
- </session-factory>
- </hibernate-configuration>
<hibernate-configuration><session-factory>……<property name="hibernate.transaction.factory_class">net.sf.hibernate.transaction.JTATransactionFactory<!--net.sf.hibernate.transaction.JDBCTransactionFactory--></property>……</session-factory></hibernate-configuration>
基于JDBC的事務管理將事務管理委托給JDBC 進行處理無疑是最簡單的實現(xiàn)方式,Hibernate 對于JDBC事務的封裝也極為簡單。
我們來看下面這段代碼:
- session = sessionFactory.openSession();
- Transaction tx = session.beginTransaction();
- ……
- tx.commit();
session = sessionFactory.openSession();Transaction tx = session.beginTransaction();……tx.commit();
從JDBC層面而言,上面的代碼實際上對應著:
- Connection dbconn = getConnection();
- dbconn.setAutoCommit(false);
- ……
- dbconn.commit();
Connection dbconn = getConnection();dbconn.setAutoCommit(false);……dbconn.commit();
就是這么簡單,Hibernate并沒有做更多的事情(實際上也沒法做更多的事情),只是將這樣的JDBC代碼進行了封裝而已。
這里要注意的是,在sessionFactory.openSession()中,hibernate會初始化數(shù)據(jù)庫連接,與此同時,將其AutoCommit 設為關閉狀態(tài)(false)。而其后,在Session.beginTransaction 方法中,Hibernate 會再次確認Connection 的AutoCommit 屬性被設為關閉狀態(tài)( 為了防止用戶代碼對session 的Connection.AutoCommit屬性進行修改)。
這也就是說,我們一開始從SessionFactory獲得的session,其自動提交屬性就已經(jīng)被關閉(AutoCommit=false),下面的代碼將不會對數(shù)據(jù)庫產(chǎn)生任何效果:
- session = sessionFactory.openSession();
- session.save(user);
- session.close();
session = sessionFactory.openSession();session.save(user);session.close();
這實際上相當于 JDBC Connection的AutoCommit屬性被設為false,執(zhí)行了若干JDBC操作之后,沒有調用commit操作即將Connection關閉。如果要使代碼真正作用到數(shù)據(jù)庫,我們必須顯式的調用Transaction指令:
- session = sessionFactory.openSession();
- Transaction tx = session.beginTransaction();
- session.save(user);
- tx.commit();
- session.close();
session = sessionFactory.openSession();Transaction tx = session.beginTransaction();session.save(user);tx.commit();session.close();
基于JTA的事務管理 JTA 提供了跨Session 的事務管理能力。這一點是與JDBC Transaction 最大的差異。
JDBC事務由Connnection管理,也就是說,事務管理實際上是在JDBC Connection中實現(xiàn)。事務周期限于Connection的生命周期之類。同樣,對于基于JDBC Transaction的Hibernate 事務管理機制而言,事務管理在Session 所依托的JDBC Connection中實現(xiàn),事務周期限于Session的生命周期。
JTA 事務管理則由 JTA 容器實現(xiàn),JTA 容器對當前加入事務的眾多Connection 進
行調度,實現(xiàn)其事務性要求。JTA的事務周期可橫跨多個JDBC Connection生命周期。
同樣對于基于JTA事務的Hibernate而言,JTA事務橫跨可橫跨多個Session。
JTA 事務是由JTA Container 維護,而參與事務的Connection無需對事務管理進行干涉。這也就是說,如果采用JTA Transaction,我們不應該再調用HibernateTransaction功能。
上面基于JDBC Transaction的正確代碼,這里就會產(chǎn)生問題:
- public class ClassA{
- public void saveUser(User user){
- session = sessionFactory.openSession();
- Transaction tx = session.beginTransaction();
- session.save(user);
- tx.commit();
- session.close();
- }
- }
- public class ClassB{
- public void saveOrder(Order order){
- session = sessionFactory.openSession();
- Transaction tx = session.beginTransaction();
- session.save(order);
- tx.commit();
- session.close();
- }
- }
- public class ClassC{
- public void save(){
- ……
- UserTransaction tx = new InitialContext().lookup(“……”);
- ClassA.save(user);
- ClassB.save(order);
- tx.commit();
- ……
- }
- }
public class ClassA{public void saveUser(User user){session = sessionFactory.openSession();Transaction tx = session.beginTransaction();session.save(user);tx.commit();session.close();}}public class ClassB{public void saveOrder(Order order){session = sessionFactory.openSession();Transaction tx = session.beginTransaction();session.save(order);tx.commit();session.close();}}public class ClassC{public void save(){……UserTransaction tx = new InitialContext().lookup(“……”);ClassA.save(user);ClassB.save(order);tx.commit();……}}
這里有兩個類ClassA和ClassB,分別提供了兩個方法:saveUsersaveOrder,
用于保存用戶信息和訂單信息。在ClassC中,我們接連調用了ClassA.saveUser方法和ClassB.saveOrder 方法,同時引入了JTA 中的UserTransaction 以實現(xiàn)ClassC.save方法中的事務性。問題出現(xiàn)了,ClassA 和ClassB 中分別都調用了Hibernate 的Transaction 功能。在Hibernate 的JTA 封裝中,Session.beginTransaction 同樣也執(zhí)行了InitialContext.lookup方法獲取UserTransaction實例,Transaction.commit方法同樣也調用了UserTransaction.commit方法。實際上,這就形成了兩個嵌套式的JTA Transaction:ClassC 申明了一個事務,而在ClassC 事務周期內,ClassA 和ClassB也企圖申明自己的事務,這將導致運行期錯誤。因此,如果決定采用JTA Transaction,應避免再重復調用Hibernate 的
Transaction功能,上面的代碼修改如下:
- public class ClassA{
- public void save(TUser user){
- session = sessionFactory.openSession();
- session.save(user);
- session.close();
- }
- ……
- }
- public class ClassB{
- public void save (Order order){
- session = sessionFactory.openSession();
- session.save(order);
- session.close();
- }
- ……
- }
- public class ClassC{
- public void save(){
- ……
- UserTransaction tx = new InitialContext().lookup(“……”);
- classA.save(user);
- classB.save(order);
- tx.commit();
- ……
- }
- }
public class ClassA{public void save(TUser user){session = sessionFactory.openSession();session.save(user);session.close();}……}public class ClassB{public void save (Order order){session = sessionFactory.openSession();session.save(order);session.close();}……}public class ClassC{public void save(){……UserTransaction tx = new InitialContext().lookup(“……”);classA.save(user);classB.save(order);tx.commit();……}}
上面代碼中的ClassC.save方法,也可以改成這樣:
- public class ClassC{
- public void save(){
- ……
- session = sessionFactory.openSession();
- Transaction tx = session.beginTransaction();
- classA.save(user);
- classB.save(order);
- tx.commit();
- ……
- }
- }
public class ClassC{public void save(){……session = sessionFactory.openSession();Transaction tx = session.beginTransaction();classA.save(user);classB.save(order);tx.commit();……}}
實際上,這是利用Hibernate來完成啟動和提交UserTransaction的功能,但這樣的做法比原本直接通過InitialContext獲取UserTransaction 的做法消耗了更多的資源,得不償失。
在EJB 中使用JTA Transaction 無疑最為簡便,我們只需要將save 方法配置為JTA事務支持即可,無需顯式申明任何事務,下面是一個Session Bean的save方法,它的事務屬性被申明為“Required”,EJB容器將自動維護此方法執(zhí)行過程中的事務:
-
-
-
-
-
-
- public void save(){
-
- classA.save(user);
- classB.save(log);
- }
/*** @ejb.interface-method* view-type="remote"** @ejb.transaction type = "Required"**/public void save(){//EJB環(huán)境中,通過部署配置即可實現(xiàn)事務申明,而無需顯式調用事務classA.save(user);classB.save(log);}//方法結束時,如果沒有異常發(fā)生,則事務由EJB容器自動提交。