兩種事務HibernateTransactionManager 和DataSourceTransactionManager

spring中常用的事務管理有DataSourceTransactionManager 和HibernateTransactionManager,他們服務的對象不同,下面來簡單說明一下:

1、DataSourceTransactionManager:此事務管理器是針對傳統的JDBC進行事務管理,在spring中是對JdbcTemplate進行事務管理

2、HibernateTransactionManager:是對hibernate進行事務管理,當在spring中使用HibernateTemplate時,要使用此管理器。

但是當在service的一個方法中同時使用了JdbcTemplate和HibernateTemplate時,就要使用HibernateTransactionManager了,因爲當使用DataSourceTransactionManager時,JdbcTemplate和HibernateTemplate獲得的connection並不是同一個,也就沒辦法對service的方法進行事務管理了。

如果一個方法中既用了HibernateTemplate,又用了JdbcTemplate,應該怎麼配單實例的db事務呢(多例免談)用 DataSouceTransactionManager是不行的,而用HibernateTransactionManager就可以保證 
原因的話看下它們源代碼,會發現HibernateTransactionManager中的處理可以保證SessionFactoryUtil和datasourceutil都能在一個事務裏取到同一個連接 

原文如下===================================================================== 

今天這邊報出一個問題,他在一個service方法裏面,用了jdbcdaosupport的dao又用了hibernateDaoSupport的dao,在spring裏面給service方法配上了事務, 

但是通過MySQL的bin log,發現這種不同的dao使用的連接id不是同一個,即jdbctemplate使用了一個鏈接,而hibernatetemplate使用了另外一個鏈接,這樣雖然兩種dao都是針對一個mysql實例,但卻沒法保證事務。 

之前xd提過使用hibernateTransaction manager,可以保證混用時候的事務,但是卻不知道爲啥會這樣。我們這邊就以爲datasourcetransactionmanager也是一樣,但發現事實上不一樣。確實我們換成hibernateTransaction manager,兩種dao使用的connection就歸一了,真好,但是爲啥呢? 

翻了半天spring的源代碼終於找到了。 

以下是datasourceTransactionManager的doGetTransaction和doBegin代碼 

Java代碼  收藏代碼
  1. protected Object doGetTransaction() {  
  2.   
  3. //只是設定一個dataSource爲key的存放connection的threadlcal  
  4.    DataSourceTransactionObject txObject = new DataSourceTransactionObject();  
  5.    txObject.setSavepointAllowed(isNestedTransactionAllowed());  
  6.    ConnectionHolder conHolder =  
  7.       (ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);  
  8.    txObject.setConnectionHolder(conHolder, false);  
  9.    return txObject;  
  10. }  
  11.   
  12. protected void doBegin(Object transaction, TransactionDefinition definition) {  
  13.      .....  
  14.   
  15.    try {  
  16.     if (txObject.getConnectionHolder() == null ||  
  17.       txObject.getConnectionHolder().isSynchronizedWithTransaction()) {  
  18.      Connection newCon = this.dataSource.getConnection();  
  19.     }  
  20.   
  21. ....  
  22. //從datasource拿一個連接,放入thread生命週期的holder  
  23.   
  24. }  


這就完了。 

然後jdbctemplate會通過datasourceutil去拿這個holder裏面的connection 

從而在一個事務裏使用這個連接。 

但是hibernateTransactionManager呢: 

Java代碼  收藏代碼
  1. protected Object doGetTransaction() {  
  2.    HibernateTransactionObject txObject = new HibernateTransactionObject();  
  3.    txObject.setSavepointAllowed(isNestedTransactionAllowed());  
  4.   
  5.    SessionHolder sessionHolder =  
  6.      (SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());  
  7.    if (sessionHolder != null) {  
  8.     if (logger.isDebugEnabled()) {  
  9.      logger.debug("Found thread-bound Session [" +  
  10.        SessionFactoryUtils.toString(sessionHolder.getSession()) + "] for Hibernate transaction");  
  11.     }  
  12.     txObject.setSessionHolder(sessionHolder, false);  
  13.    }  
  14.   
  15.    if (getDataSource() != null) {  
  16.     ConnectionHolder conHolder = (ConnectionHolder)  
  17.       TransactionSynchronizationManager.getResource(getDataSource());  
  18.     txObject.setConnectionHolder(conHolder);  
  19.    }  
  20.   
  21.    return txObject;  
  22. }  
  23.   
  24. //兩個holder都管!  
  25.   
  26. protected void doBegin(Object transaction, TransactionDefinition definition) {  
  27.      .....  
  28.   
  29.    try {  
  30.     if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) {  
  31.      Interceptor entityInterceptor = getEntityInterceptor();  
  32.      Session newSession = (entityInterceptor != null ?  
  33.        getSessionFactory().openSession(entityInterceptor) : getSessionFactory().openSession());  
  34.      if (logger.isDebugEnabled()) {  
  35.       logger.debug("Opened new Session [" + SessionFactoryUtils.toString(newSession) +  
  36.         "] for Hibernate transaction");  
  37.      }  
  38.      txObject.setSessionHolder(new SessionHolder(newSession), true);  
  39.     }  
  40.   
  41.     .....  
  42.     //從sessionFactory拿個新session,也會產生一個新連接  
  43.   
  44.     session = txObject.getSessionHolder().getSession();  
  45.   
  46.     if (this.prepareConnection && isSameConnectionForEntireSession(session)) {  
  47.      // We're allowed to change the transaction settings of the JDBC Connection.  
  48.      if (logger.isDebugEnabled()) {  
  49.       logger.debug(  
  50.         "Preparing JDBC Connection of Hibernate Session [" + SessionFactoryUtils.toString(session) + "]");  
  51.      }  
  52.   
  53.      //原來直接把session後面的connection也放入holder  
  54.      Connection con = session.connection();  
  55.      Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);  
  56.      txObject.setPreviousIsolationLevel(previousIsolationLevel);  
  57.     }  

所以如果使用hibernateTransactionManager的話,就完全可以保證SessionFactoryUtil和datasourceutil都能在一個事務裏取到同一個連接!所有的疑問煙消雲散了, 
所以大家還是使用hibernateTransactionManager從而隨心所欲的使用jdbctemplate和hibernatetemplate吧

原文來自:http://www.pinhuba.com/spring/101108.htm

http://bjyzxxds.iteye.com/blog/427309

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章