posts - 156,  comments - 601,  trackbacks - 0

          前言:
            如果大家使用過Spring事務管理,會發現Spring提供的事務分為“只讀”和“讀寫”事務兩類。這不免就會疑問這兩種事務會有什么不同?本文則通過對Spring和Hibernate源代碼的剖析來找出這兩種事務的區別。特別是運行性能方面的區別。
            解讀的源代碼版本為 Spring 2.5.6.SEC01 ,Hibernate 3.3.2.GA。

            Spring對事務的支持也分編程式和聲明式,本文以基于Annotation方式的聲明式事務為例:
           
            Spring的配置如下:
              <bean
                  
          class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
                  
          <property name="proxyTargetClass" value="true"></property>
              
          </bean>
              
          <bean id="entityManagerFactory"
                  class
          ="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
                  
          <property name="persistenceUnitName" value="entityManager" />
                  
          <property name="jpaProperties">
                      
          <props>
                      
          </props>
                  
          </property>
              
          </bean>

              
          <bean id="transactionManager"
                  class
          ="org.springframework.orm.jpa.JpaTransactionManager">
                  
          <property name="entityManagerFactory" ref="entityManagerFactory" />
              
          </bean>
              
          <bean id="transactionInterceptor"
                  class
          ="org.springframework.transaction.interceptor.TransactionInterceptor">
                  
          <property name="transactionManager" ref="transactionManager" />
                  
          <property name="transactionAttributeSource">
                      
          <bean
                          
          class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource" />
                  
          </property>
              
          </bean>
              
          <bean id="transactionAttributeSourceAdvisor"
                  class
          ="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
                  
          <property name="transactionInterceptor"    ref="transactionInterceptor" />
              
          </bean>

          從配置中,可以看到事務的攔截,都由 TransactionInterceptor 類進行處理
          下面是invoke方法的核心處理過程:

              public Object invoke(final MethodInvocation invocation) throws Throwable {
                  
          // Work out the target class: may be <code>null</code>.
                  
          // The TransactionAttributeSource should be passed the target class
                  
          // as well as the method, which may be from an interface.
                  Class targetClass = (invocation.getThis() != null ? invocation.getThis().getClass() : null);

                  
          // If the transaction attribute is null, the method is non-transactional.
                  final TransactionAttribute txAttr =
                          getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);
                  
          final String joinpointIdentification = methodIdentification(invocation.getMethod());

                  
          if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) {
                      
          // Standard transaction demarcation with getTransaction and commit/rollback calls.
                      TransactionInfo txInfo = createTransactionIfNecessary(txAttr, joinpointIdentification);
                      Object retVal 
          = null;
                      
          try {
                          
          // This is an around advice: Invoke the next interceptor in the chain.
                          
          // This will normally result in a target object being invoked.
                          retVal = invocation.proceed();
                      }
                      
          catch (Throwable ex) {
                          
          // target invocation exception
                          completeTransactionAfterThrowing(txInfo, ex);
                          
          throw ex;
                      }
                      
          finally {
                          cleanupTransactionInfo(txInfo);
                      }
                      
          //處理事務的操作
                      commitTransactionAfterReturning(txInfo);
                      
          return retVal;
                  }
                  .省略
              }

          針對事務的操作,就是調用 commitTransactionAfterReturning 方法進行事務的處理。
          該方法會調用AbstractPlatformTransactionManager類的commit和processCommit方法。processCommit方法是真正調用Hibernate事務處理的實現。

              private void processCommit(DefaultTransactionStatus status) throws TransactionException {
                  
          try {
                      
          boolean beforeCompletionInvoked = false;
                      
          try {
                          prepareForCommit(status);
                          triggerBeforeCommit(status);
                          triggerBeforeCompletion(status);
                          beforeCompletionInvoked 
          = true;
                          
          boolean globalRollbackOnly = false;
                          
          if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
                              globalRollbackOnly 
          = status.isGlobalRollbackOnly();
                          }
                          
          if (status.hasSavepoint()) {
                              
          if (status.isDebug()) {
                                  logger.debug(
          "Releasing transaction savepoint");
                              }
                              status.releaseHeldSavepoint();
                          }
                          
          else if (status.isNewTransaction()) { //如果是一個新啟的事務
                              if (status.isDebug()) {
                                  logger.debug(
          "Initiating transaction commit");
                              }
          //則進行事務的提交處理
                              doCommit(status);
                          }
                       代碼省略
              }

          doCommit 方法的調用 會觸發 Hibernate的JDBCTransaction的commit方法調用

              public void commit() throws HibernateException {
                  
          if (!begun) {
                      
          throw new TransactionException("Transaction not successfully started");
                  }

                  log.debug(
          "commit");
                  
          //如果是只讀事務,Spring會將transactionContext的 isFlushModeNever 設置為true
                  if ( !transactionContext.isFlushModeNever() && callback ) {
                    
          //刷新一級緩存中的持久對象,向數據庫發送sql語句  
                      transactionContext.managedFlush(); //if an exception occurs during flush, user must call rollback()
                  }

                  notifyLocalSynchsBeforeTransactionCompletion();
                  
          if ( callback ) {
                      jdbcContext.beforeTransactionCompletion( 
          this );
                  }

                  
          try {
                      commitAndResetAutoCommit();
                      log.debug(
          "committed JDBC Connection");
                      committed 
          = true;
                      
          if ( callback ) {
                          jdbcContext.afterTransactionCompletion( 
          truethis );
                      }
                      notifyLocalSynchsAfterTransactionCompletion( Status.STATUS_COMMITTED );
                  }
                  
          catch (SQLException e) {
                      log.error(
          "JDBC commit failed", e);
                      commitFailed 
          = true;
                      
          if ( callback ) {
                          jdbcContext.afterTransactionCompletion( 
          falsethis );
                      }
                      notifyLocalSynchsAfterTransactionCompletion( Status.STATUS_UNKNOWN );
                      
          throw new TransactionException("JDBC commit failed", e);
                  }
                  
          finally {
                      closeIfRequired();
                  }
              }   

          關鍵點已經在上面的注釋中說明。
          當事務被標識為只讀事務時,Spring可以對某些可以針對只讀事務進行優化的資源就可以執行相應的優化措施,上面Spring告之hibernate的session在只讀事務模式下不用嘗試檢測和同步持久對象的狀態的更新。


          總結:
            如果在使用事務的情況下,所有操作都是讀操作,那建議把事務設置成只讀事務,或者事務的傳播途徑最好能設置為 supports (運行在當前的事務范圍內,如果當前沒有啟動事務,那么就不在事務范圍內運行)或者 not supports (不在事務范圍內執行,如果當前啟動了事務,那么掛起當前事務),這樣不在事務下,就不會調用transactionContext.managedFlush(); 方法。
          所有只讀事務與讀寫事務的比較大的運行性能區別就是只讀事務避免了Hibernate的檢測和同步持久對象的狀態的更新,提升了運行性能。

          Good Luck!
          Yours Matthew!



          posted on 2010-05-06 19:31 x.matthew 閱讀(6062) 評論(1)  編輯  收藏 所屬分類: Spring|Hibernate|Other framework
          主站蜘蛛池模板: 林周县| 荣成市| 玉树县| 独山县| 河间市| 屏边| 青川县| 静海县| 洛川县| 光山县| 新源县| 淮滨县| 云梦县| 子洲县| 余姚市| 琼结县| 育儿| 尼勒克县| 新沂市| 贵定县| 崇州市| 同仁县| 乌海市| 盖州市| 鄱阳县| 德安县| 怀远县| 柳河县| 石林| 双柏县| 庆城县| 乐安县| 亳州市| 东乡| 盐边县| 广宗县| 灵宝市| 昆山市| 资兴市| 芒康县| 西藏|