http://blog.51cto.com/winters1224/807500
Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。
首先我们知道,spring的事务管理有以下几种事务属性:
PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED 类似的操作。
=======================
针对这些事务属性,我做了几次试验。
使用的代码简单描述就是:
外层方法{
try{内层方法();}catch(RuntimeException e){}
}
内层方法(){
抛出 RuntimeException ;
}
=======================
对REQUIRED的试验时,配置是:
<tx:method name="外层方法" propagation="REQUIRED" />
<tx:method name="内层方法" propagation="REQUIRED" />
在日志中看到:
1、为外层方法创建事务
- Creating new transaction with name [外层方 法]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2、为内层方法配置事务
- Participating in existing transaction
可以看到,内层方法使用的是外层方法的事务上下文。然后,当内层方法抛出异常时,日志中记录:
Setting JDBC transaction [...] rollback-only
即要求该事务回滚。但是内层方法的异常被外层方法捕获并处理了, 外层方法仍然会继续执行,并在执行完毕后提交事务。这时日志中记录并抛出异常:
Global transaction is marked as rollback-only but transactional code requested commit
(执行事务回滚...)
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
所谓“支持当前事务”,就是在当前事务的上下文内执行目标方法。内层方法对事务的操作会影响到外层方法。
=========================
对NESTED的试验时,配置如下:
<tx:method name="外层方法" propagation="REQUIRED" />
<tx:method name="内层方法" propagation="NESTED" />
在日志中看到:
1、为外层方法创建事务
- Creating new transaction with name [外层方 法]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2、为内层方法创建嵌套事务
- Creating nested transaction with name [内层方法]
1、内层方法回滚、提交
- Rolling back transaction to savepoint
2、外层方法继续执行并提交
外层方法逻辑输出
- Triggering beforeCommit synchronization
……
可见,嵌套事务与当前事务可以认为是两个事务,只是它不会挂起当前事务。嵌套事务即使回滚,当前事务仍然可以正常提交。
=======================
对SUPPORTS试验时, 使用的配置是:
<tx:method name="外层方法" propagation="SUPPORTS" />
<tx:method name="内层方法" propagation="REQUIRED" />
另外内层方法改为了:
内层方法(){
return;
}
这种方案下,日志中只有为内层方法创建事务的记录,对外层方法基本是“不闻不问”的。而如果把配置反过来,改成
<tx:method name="外层方法" propagation="REQUIRED" />
<tx:method name="内层方法" propagation="SUPPORTS" />
那么,spring会为外层方法创建事务,而对内存方法则使用外层方法的事务上下文。
但是,如果配置为:
<tx:method name="外层方法" propagation="SUPPORTS" />
<tx:method name="内层方法" propagation="SUPPORTS" />
日志中会出现几行这样的记录:
Triggering beforeCommit synchronization
Triggering beforeCompletion synchronization
Triggering afterCommit synchronization
Triggering afterCompletion synchronization
这些是事务提交前后的日志;但是却找不到创建事务相关的日志。我不清楚为什么会出现这种事务前后行为不匹配的情况。以后再琢磨琢磨吧。
======================
其它的几种情况我没有再测试了。不过,事务相关配置的基本含义到这里差不多已经清楚了,其它的情况可以“同理可证”或者“由此推知”了吧。
************************************************************************************************
A方法之外加有事务管理拦截器,在A方法中做一系列操作,操作过程中捕获了一个异常,因为此异常不影响业务,捕获后需要正常向下运行,最终事务管理器提交事务时报了如下错误
Transaction has been rolled back because it has been marked as rollback
原因就是发生异常后当前的事务就被标记为rollback-only,外层事务管理器再commit时就会抛此异常,解决方法是
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
<property name="globalRollbackOnParticipationFailure" value="false" />
</bean>
在 org.springframework.transaction.support.AbstractPlatformTransactionManager 中有个叫
isGlobalRollbackOnParticipationFailure的参数,默认是true.
源码中说明:
Switch this to "false" to let the transaction originator make the rollback decision. If a participating transaction fails with an exception, the caller can still decide to continue with a different path within the transaction. However, note that this will only work as long as all participating resources are capable of continuing towards a transaction commit even after a data access failure: This is generally not the case for a Hibernate Session, for example; neither is it for a sequence of JDBC insert/update/delete operations.
大意是:如果isGlobalRollbackOnParticipationFailure为false,则会让主事务决定回滚,如果当遇到exception加入事务失败时,调用者能继续在事务内决定是回滚还是继续。然而,要注意是那样做仅仅适用于在数据访问失败的情况下且只要所有操作事务能提交。