前言:
Spring默认情况下会对运行期发生的异常(RunTimeException),即uncheck异常,进行事务回滚。如果遇到checked异常就不回滚。
不过也可以自定义:
1. 让checked也回滚:在整个方法前加上
@Transactional(rollbackFor=Exception.class)
2 .让unchecked不回滚:
@Transactional(notRollbackFor=RunTimeException.class)
3 .不需要事务管理的(只查询)方法:
@Transactional(propagation=Propagation.NOT_SUPPORTED)
正片:
在 spring的 TransactionDefinition接口中一共定义了六种事务传播属性:
TransactionDefinition中定义的->
Ⓐ :支持当前事务,如果当前没有事务
Ⓑ:以非事务方式执行操作,如果当前存在事务
样式:
甲方法{
乙方法;
}
Required :Ⓐ, 新建。
注:甲有事务,乙就不再起新的事务。发生异常,一起回滚。
甲无事务,乙就会自己建一个,发生异常,甲回滚,乙不会
Supports :Ⓐ, 非事务。
注:甲有事务,乙不会再起新的事务。发生异常,一起回滚。
甲无事务,乙就以非事务执行,发生异常,也不会回滚。
Mandatory: Ⓐ, 抛异常。
注:甲有事务,乙不会再起新的事务。发生异常,一起回滚。
甲无事务,乙马上抛异常。
requires_NEW :新建事务,如果当前存在事务,把当前事务挂起。
注:甲有事务,乙会新建事务,将甲的事务挂起。发生异常,只有乙回滚。
当乙事务结束时,甲会继续有事务。
甲无事务,乙会新建事务。发生异常,只有乙回滚。
例:甲是required,乙是requires_NEW
甲和乙不会因为对方的执行情况而影响事务的结果, 因为它们根本就是两个事务。
但注意一种情况。
如果乙已经提交,
如果乙抛出的异常未被甲的try-catch捕获,那么甲会回滚。
使用场景:一般日志记录行为不应影响主逻辑,可以用requires_NEW
日志服务的事务策略配置为propagation="REQUIRES_NEW",告诉Spring不管上下文是否有事务,Log Service被调用时都要求一个完全新的只属于Log Service自己的事务。通过该事务策略,Log Service可以独立的记录日志信息,不再受到业务逻辑事务的干扰。
NOT_supported :Ⓑ,挂起。
注:甲有事务,将甲挂起,乙以非事务执行,乙结束后,甲的事务继续。
甲无事务,乙以非事务执行。
Never :Ⓑ,抛异常。
注:甲只要有事务,就抛异常
甲无事务,乙以非事务执行。
下面的要求事务管理器支持事务嵌套行为。
在 spring 中使用 PROPAGATION_NESTED的前提:
1. 我们要设置 transactionManager 的 nestedTransactionAllowed 属性为 true, 注意, 此属性默认为 false!!!
2. java.sql.Savepoint 必须存在, 即 jdk 版本要 1.4+
3. Connection.getMetaData().supportsSavepoints() 必须为 true, 即 jdbc drive 必须支持 JDBC 3.0
Nested :如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与Required类似的操作。
例:甲是required,乙是requires_Nested
这时候乙是甲的事务的一部分
如果甲提交,乙也会被提交
如果甲回滚,乙也会被回滚
应用场景:
ServiceA {
//事务属性配置为 PROPAGATION_REQUIRED
void methodA() {
try {
ServiceB.methodB();
} catch (SomeException) {
// 执行其他业务, 如 ServiceC.methodC();
}
}
}
这样会有分支执行的效果
1.如果 ServiceB.methodB 失败, 那么执行 ServiceC.methodC(),
而 ServiceB.methodB 已经回滚到它执行之前的 SavePoint, 所以不会产生脏数据(相当于此方法从未执行过), 这种特性可以用在某些特殊的业务中。
2.当然methodA可以自由决定到底是提交还是回滚。
理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是:
PROPAGATION_REQUIRES_NEW 完全是一个新的事务,它与外部事务相互独立; 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 嵌套事务也会被 commit, 这个规则同样适用于 roll back。