Spring 声明事务中transactionAttributes属性 + - Exception 实现逻辑

下面是一段典型的Spring 声明事务的配置:

 

 1 <bean id=“baseTxProxy” lazy-init=“true”class=“org.springframework.transaction.interceptor.TransactionProxyFactoryBean” scope=“singleton” abstract=“true”>
 2   <property name=“transactionManager”>
 3    <ref local=“transactionManager” />
 4   </property>
 5   <property name=“transactionAttributes”>
 6    <props>
 7     <prop key=“register*”>PROPAGATION_REQUIRED</prop>
 8     <prop key=“trade*”>PROPAGATION_REQUIRED</prop>
 9     <prop key=“cancel*”>PROPAGATION_REQUIRED</prop>
10     <prop key="save*">PROPAGATION_REQUIRED,-ApplicationException,+BusinessException</prop> 
11     <prop key=“exe*”>PROPAGATION_REQUIRED</prop>
12     <prop key=“add*”>PROPAGATION_REQUIRED</prop>
13     <prop key=“persist*”>PROPAGATION_REQUIRED</prop>
14     <prop key=“remove*”>PROPAGATION_REQUIRED</prop>
15     <prop key=“del*”>PROPAGATION_REQUIRED</prop>
16     <prop key=“update*”>PROPAGATION_REQUIRED</prop>
17     <prop key=“gen*”>PROPAGATION_REQUIRED</prop>
18     <prop key=“finish*”>PROPAGATION_REQUIRED</prop>
19     <prop key=“get*”>PROPAGATION_REQUIRED,readOnly</prop>
20     <prop key=“find*”>PROPAGATION_REQUIRED,readOnly</prop>
21     <prop key=“query*”>PROPAGATION_REQUIRED,readOnly</prop>
22     <prop key=“select*”>PROPAGATION_REQUIRED,readOnly</prop>
23     <prop key=“is*”>PROPAGATION_REQUIRED,readOnly</prop>
24    </props>
25   </property>
26 </bean>

查阅相关spring 的资料后发现transactionAttributes的各种属性的意义如下:

PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

在Spring声明事务中,我们可以自定义方法的哪些Exception需要回滚,哪些Exception可以直接提交。

通过下面的配置:

<prop key="save*">PROPAGATION_REQUIRED,-aplicationException,+BusinessException</prop>  

 - 表示抛出该异常时需要回滚

+表示即使抛出该异常事务同样要提交

-ApplicationException :表示抛出ApplicationException 时,事务需要回滚。但不是说只抛出ApplicationException 异常时,事务才回滚,如果程序抛出RuntimeException和Error时,事务一样会回滚,即使这里没有配置。因为Spring中默认对所有的RuntimeException和Error都会回滚事务。

Spring中是如何实现这段逻辑的:

调用的是

org.springframework.transaction.interceptor.RuleBasedTransactionAttribute.rollbackOn(Throwable ex)

 

 1 public boolean rollbackOn(Throwable ex) {
 2         if (logger.isTraceEnabled()) {
 3             logger.trace("Applying rules to determine whether transaction should rollback on " + ex);
 4         }
 5 
 6         RollbackRuleAttribute winner = null;
 7         int deepest = Integer.MAX_VALUE;
 8 
 9         //配置文件中的回滚异常列表,当然去掉了-,只有name,commit的规则是另外一个对象
10         if (this.rollbackRules != null) {
11             for (RollbackRuleAttribute rule : this.rollbackRules) {
12                 //使用抛出exception的className(全路径className)进行indexOf match
13                 //如果没有match上会继续搜索superClass name进行match,到Throwable class为止
14                 int depth = rule.getDepth(ex);
15                 if (depth >= 0 && depth < deepest) {
16                     deepest = depth;
17                     winner = rule;
18                 }
19             }
20         }
21 
22         if (logger.isTraceEnabled()) {
23             logger.trace("Winning rollback rule is: " + winner);
24         }
25 
26         // User superclass behavior (rollback on unchecked) if no rule matches.
27         if (winner == null) {
28             logger.trace("No relevant rollback rule found: applying default rules");
29             //如果没有match上,调用此方法继续match,判断instance of RuntimeException or Error
30             return super.rollbackOn(ex);
31         }
32 
33         return !(winner instanceof NoRollbackRuleAttribute);
34     }

 

rule.getDepth方法代码

        因为使用的是className的全路径进行indexOf匹配,所以如果自定义异常是:com.abc.ApplicationException,你在xml配置文件中定义为:-abc,同样会match上,事务也会回滚,这一点需要注意。

       另外一点,如何在xml中定义的是-Exception,这样只要class的全路径中包含Exception字段,如包名,也会匹配上。

 

 1   public int getDepth(Throwable ex) {  
 2     return getDepth(ex.getClass(), 0);  
 3 }  
 4   
 5   
 6  private int getDepth(Class exceptionClass, int depth) {  
 7     if (exceptionClass.getName().indexOf(this.exceptionName) != -1) {  
 8         // Found it!  
 9         return depth;  
10     }  
11     // If we've gone as far as we can go and haven't found it...  
12     if (exceptionClass.equals(Throwable.class)) {  
13         return -1;  
14     }  
15     return getDepth(exceptionClass.getSuperclass(), depth + 1);  
16 }  

super.rollbackOn(Throwable ex) 方法代码

很简单的一行代码,这就是为什么RuntimeException和Error也会回滚啦。

 

1 public boolean rollbackOn(Throwable ex) {  
2         return (ex instanceof RuntimeException || ex instanceof Error);  
3     } 

 

几次测试输出的debug日志

    [08/24 03:35:39] [DEBUG] RuleBasedTransactionAttribute: Applying rules to determine whether transaction should rollback on usertest.exception.BusinessException: Error  
    [08/24 03:35:39] [DEBUG] RuleBasedTransactionAttribute: Winning rollback rule is: RollbackRuleAttribute with pattern [BusinessException]  
    [08/24 03:35:39] [DEBUG] DataSourceTransactionManager: Triggering beforeCompletion synchronization  
    [08/24 03:35:39] [DEBUG] DataSourceTransactionManager: Initiating transaction rollback  
    [08/24 03:35:39] [DEBUG] DataSourceTransactionManager: Rolling back JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection@1db05b2]  
    [08/24 03:35:39] [DEBUG] DataSourceTransactionManager: Triggering afterCompletion synchronization  
    [08/24 03:35:39] [DEBUG] TransactionSynchronizationManager: Clearing transaction synchronization  
    [08/24 03:35:39] [DEBUG] TransactionSynchronizationManager: Removed value [org.springframework.jdbc.datasource.ConnectionHolder@1833eca] for key [org.apache.commons.dbcp.BasicDataSource@4aa0ce] from thread [main]  
    [08/24 03:35:39] [DEBUG] DataSourceTransactionManager: Releasing JDBC Connection [org.apache.commons.dbcp.PoolableConnection@1db05b2] after transaction  
    [08/24 03:35:39] [DEBUG] DataSourceUtils: Returning JDBC Connection to DataSource  
      
      
      
      
    [08/24 03:39:16] [DEBUG] TransactionInterceptor: Completing transaction for [usertest.dao.UsersDAO.testInsertAndUpdate] after exception: java.lang.Exception: Error  
    [08/24 03:39:16] [DEBUG] RuleBasedTransactionAttribute: Applying rules to determine whether transaction should rollback on java.lang.Exception: Error  
    [08/24 03:39:16] [DEBUG] RuleBasedTransactionAttribute: Winning rollback rule is: null  
    [08/24 03:39:16] [DEBUG] RuleBasedTransactionAttribute: No relevant rollback rule found: applying superclass default  
    [08/24 03:39:16] [DEBUG] DataSourceTransactionManager: Triggering beforeCommit synchronization  
    [08/24 03:39:16] [DEBUG] DataSourceTransactionManager: Triggering beforeCompletion synchronization  
    [08/24 03:39:16] [DEBUG] DataSourceTransactionManager: Initiating transaction commit  
    [08/24 03:39:16] [DEBUG] DataSourceTransactionManager: Committing JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection@1db05b2]  
    [08/24 03:39:16] [DEBUG] DataSourceTransactionManager: Triggering afterCommit synchronization  
      
      
    [08/24 03:41:40] [DEBUG] TransactionInterceptor: Completing transaction for [usertest.dao.UsersDAO.testInsertAndUpdate] after exception: usertest.exception.BusinessException: Error  
    [08/24 03:41:40] [DEBUG] RuleBasedTransactionAttribute: Applying rules to determine whether transaction should rollback on usertest.exception.BusinessException: Error  
    [08/24 03:41:40] [DEBUG] RuleBasedTransactionAttribute: Winning rollback rule is: RollbackRuleAttribute with pattern [Exception]  
    [08/24 03:41:40] [DEBUG] DataSourceTransactionManager: Triggering beforeCompletion synchronization  
    [08/24 03:41:40] [DEBUG] DataSourceTransactionManager: Initiating transaction rollback  
    [08/24 03:41:40] [DEBUG] DataSourceTransactionManager: Rolling back JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection@1db05b2]  
    [08/24 03:41:40] [DEBUG] DataSourceTransactionManager: Triggering afterCompletion synchronization  
    [08/24 03:41:40] [DEBUG] TransactionSynchronizationManager: Clearing transaction synchronization  

 

posted @ 2016-11-09 09:59  神探维拉  阅读(794)  评论(0编辑  收藏  举报