[原理][源代碼解析]spring中@Transactional,Propagation.SUPPORTS,以及 Hibernate Session,以及jdbc Connection關係---轉載

問題:web

一.spring

1. Spring 如何處理propagation=Propagation.SUPPORTS?sql

2. Spring 什麼時候生成HibernateSession ?數據庫

3. propagation=Propagation.SUPPORTS 和propagation=Propagation.require對生成Session有何影響 ?編程

        共同點:都會進入aspect切面處理, 試圖新建Session,開啓Transaction ,都能得到.TransactionStatus session

區別: 前者成功開啓事務,後者未開啓,但會設置一個容許懶惰加載Session的配置.詳情見下面代碼.app

3.1. 未配置@Transaction 和 配置@Transaction(propagation=Propagation.SUPPORTS)的區別?工具

         共同點: 都沒有起事務.ui

不一樣點: 前者不會進入aspect切面處理, 後者進入試圖新建Session,開啓事務但未成功,會設置一個容許懶惰加載Session的配置. 詳情見下面代碼spa

4. Spring 什麼時候從線程池中獲取到 jdbcconnection 設置 setAutoCommit(false)?

 

  正文解析:

spring是在TransactionAspectSupport(切面編程)的createTransactionIfNecessary方法中完成對@Transactional註解的處理. 而後委託給PlatformTransactionManager實現類的getTransaction()方法( 實現

類是HibernateTransactionManager被咱們配置在application.xml中)對propagation=Propagation.SUPPORTS的處理也是在這個方法內實現,具體是其父類AbstractPlatformTransactionManager.getTransaction()中.

代碼片斷以下:

AbstractPlatformTransactionManager.getTransaction(TransactionDefinitiondefinition){

      Object transaction = doGetTransaction(); //注1: springframework的HibernateTransactionObject 實例,內部持有的是 HibernateSession ,一開始是Null;

         . . otherCode . .

    // No existing transaction found -> check propagationbehavior to find out how to proceed.

       if (definition.getPropagationBehavior()== TransactionDefinition.PROPAGATION_MANDATORY) {

           throw new IllegalTransactionStateException(

                  "No existing transaction found for transactionmarked with propagation 'mandatory'");

       }

       else if (definition.getPropagationBehavior()== TransactionDefinition.PROPAGATION_REQUIRED ||

             definition.getPropagationBehavior() ==TransactionDefinition.PROPAGATION_REQUIRES_NEW ||

          definition.getPropagationBehavior() ==TransactionDefinition.PROPAGATION_NESTED) {

          SuspendedResourcesHolder suspendedResources = suspend(null);

           if (debugEnabled) {

             logger.debug("Creating new transaction withname [" + definition.getName() + "]:" + definition);

          }

           try {

              boolean newSynchronization =(getTransactionSynchronization() != SYNCHRONIZATION_NEVER);

             DefaultTransactionStatus status = newTransactionStatus(

                    definition, transaction, true,newSynchronization, debugEnabled, suspendedResources);

             doBegin(transaction, definition); // 注2: 生成 HibernateSession ,而後獲取jdbcconnection , 而且Connection,setAutoCommit(false),開啓咱們理解上的sql事務.具體是調用Session持有的HibernateJDBCTransaction中的.begin()方法中,            

prepareSynchronization(status,definition);  //設置SpringTransactionSynchronizationManager.initSynchronization().

              return status;

          }

           catch (RuntimeException ex) {

             resume(null, suspendedResources);

              throw ex;

          }

           catch (Error err) {

             resume(null, suspendedResources);

              throw err;

          }

       }

       else {

              // 注3  這個流程就是當」線程中無事務,且配置是propagation= Propagation.SUPPORTS」時執行的路徑.這一步未生成Hibernate Session, jdbc Connection.但 prepareTransactionStatus()中作了一步操做,設置SpringTransactionSynchronizationManager.initSynchronization().正是這個操做在以後,咱們 才能在後來Hibernate save or update時,容許Hibernate 回調初始化時註冊的 Spring SpringSessionContext 生成 HibernateSession ,進一步從鏈接池獲取 jdbcConnection

           // Create "empty" transaction: no actualtransaction, but potentially synchronization.

           boolean newSynchronization = (getTransactionSynchronization()== SYNCHRONIZATION_ALWAYS);

           return prepareTransactionStatus(definition, nulltrue,newSynchronization, debugEnabled, null);

       }

. . .

}

   注2和注3解答了2,3問題.

對以上代碼中命名的一點見解:

l       getTransaction返回TransactioStaus, 感受用 getTransactioStaus比 spring目前的命名getTransaction更合適 或者取名 getTransactionIfNecessary比較好的,根據注3可知,此分支並無真正的開啓數據庫事務.更沒有獲取jdbc Connection.和任何生成 Hibernate Session.

l   比較重要的幾個類Springframework的HibernateTransactionObject,Hibernate的Session,Hibernate的JDBCTransaction,jdbc的Connection. Springframework HibernateTransactionObject持有一個 Hibernate Session, Hibernate Session持有一個Hibernate JDBCTransaction , 最終持有 jdbc Connection.

                   由於 數據庫事務Connection.setAutoCommit(false).事務開啓是附屬於Connection. Connection應該是先於事務開啓. 因此感受用Springframework HibernateTransactionObject 和Hibernate JDBCTransaction 持有 jdbc Connection不一樣合理.

取名爲 Springframework  HibernateTransactionStatusObject ,  Hibernate  JDBCTransactionStaus更合理.

 

 

對於第4個問題.

對於@Transaction(propagation=Propagation.REQUIRE)時 Session新建和Session獲取都是在AbstractPlatformTransactionManager.getTransaction方法中完成的

對於@Transaction(propagation=Propagation.SUPPORTS) , 當懶惰獲取完Session後,當且僅當真正嘗試數據庫操做時纔會懶惰獲取Connection.

獲取Connection的簡單堆棧以下.

ConnectionManager.openConnection()line: 446   //注:發現沒有鏈接,從線程池獲取一個鏈接

ConnectionManager.getConnection()line: 167   

BatchingBatcher(AbstractBatcher).prepareQueryStatement(String,boolean, ScrollMode) line: 161   

EntityLoader(Loader).prepareQueryStatement(QueryParameters,boolean, SessionImplementor) line: 1700   

 

 

問題:

二. 對於上面第3個小問題的延伸,注3代表此處並無生成Session. 而後get or save 正常不報錯. 咱們定義爲A狀況.

    A.當處於propagation=Propagation.SUPPORTS時,未生成Session和獲取到jdbc Connection.當咱們 save or get 時候正常不報錯.

下面看下無Session狀況下獲取Session的另一種狀況:

    B.咱們可能遇到過這樣的錯誤:No Hibernate Session bound to thread,and configuration does not allow creationof non-transactional one here.錯誤緣由是咱們save or get的時候獲取不到Session,不正常拋錯

     A和B都是在無Session狀況下獲取Session, 爲何一個正常,一個不正常?

 

正文解析:

具體代碼片斷以下:

        SpringSessionFactoryUtils.doGetSession(SessionFactory sessionFactory,InterceptorentityInterceptor,SQLExceptionTranslator jdbcExceptionTranslator,booleanallowCreate){

                    if (TransactionSynchronizationManager.isSynchronizationActive()){  // 注4:A狀況下返回是true,緣由見注3,B狀況下返回是false.

                             // 注: 此部分代碼實現了」若是目前沒有Session那麼就生成Session」的邏輯

                            }

                   if ((!(allowCreate)) && (!(isSessionTransactional(session,sessionFactory)))) { // 注5: 兩個開關判斷.allowCreate 對於HibernateManager是強制設置爲false(關閉)的,後者正是前面HibernateManager處理@Transaction時開啓(返回true).  有一個容許就正常執行返回了Session. 兩個有一個開啓(ture)就不拋錯,正常執行返回Session.

什麼時候allowCreateture?見下面 三. 那麼什麼時候能夠獲取非事務類型的Session? .

                               closeSession(session);     

                                                throw new IllegalStateException("No Hibernate Session bound to thread, andconfiguration does not allow creation of non-transactional one here");

}

return session;     //注6, 若是沒有被注5禁止,那麼就返回生成的Session

}

注4,注5解答了上述問題.

因此出現B問題, 檢查下本身的代碼,是否處於事務環境中. 因爲getTransaction()會把Session綁定到ThreadLocal中,若是新建了獨立線程, 注意內部調用的代碼是否加上@Transaction,否則該線程中是沒法獲取到Threadlocal的Session變量.

對於這個Exception.後半部分」configuration does notallow creation of non-transactional one here」的具體語義.是指」configuration不容許生成一個非事務類型的Session」.

   

三. 那麼什麼時候能夠獲取非事務類型的Session?

1.       查看源代碼能夠發現OpenSessionInViewFilter中能夠 (配置在 web.xml中),正是這個allow=true ,才使其在無事務環境下先生成一個Session,並延遲關閉.保證了view層可以獲取到懶惰加載的實體.

2.       還有就是Spring 提供的HibernateTemplate 模板工具類中也是allow=true,方便咱們執行Hibernate方法.

 

其餘如propagation= Propagation.require_new也是在上述的代碼中,進行原connection掛起和恢復的操做.具體詳見代碼.

 

 

 其餘如propagation =Propagation.require_new也是在上述的代碼中,進行原connection掛起和恢復的操做.具體詳見代碼.

 

原文地址:http://blog.csdn.net/fei33423/article/details/32346821

相關文章
相關標籤/搜索