Spring :事务传播行为
一.相关定义
事务传播行为(propagation):当前方法的事务如何传播下去(里面的方法如果用事务,是否与它公用一个事务)
spring中有其中有7种事务传播行为,默认是Reqiured级别。
public enum Propagation { /** * Support a current transaction, create a new one if none exists. 如果以前有事务,就用以前的事务,如果没有,就创建一个新的事务。 */ REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED), /** * Support a current transaction, execute non-transactionally if none exists. 支持新的事务,如果没有事务,也可以执行 */ SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS), /** * Support a current transaction, throw an exception if none exists. 当前必须存在事务,否则抛异常 */ MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY), /** * Create a new transaction, and suspend the current transaction if one exists. 创建一个新的事务,并且挂起以前旧的事务 */ REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW), /** * Execute non-transactionally, suspend the current transaction if one exists. 不支持当前存在事务,如果有就挂起当前事务 */ NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED), /** * Execute non-transactionally, throw an exception if a transaction exists. 不支持当前存在事务,如果有就抛异常。 */ NEVER(TransactionDefinition.PROPAGATION_NEVER), /** * Execute within a nested transaction if a current transaction exists,
开启一个子事务,mysql不支持,只有支持还原点功能的数据库才行(oracle) */ NESTED(TransactionDefinition.PROPAGATION_NESTED); private final int value; Propagation(int value) { this.value = value; } public int value() { return this.value; } }
二.相关场景(常用的只有required和requires_new)
下面针对这两个构建相关场景:
外事务 {
A()REQUIRED
B()REQUIRES_NEW
C()REQUIRED
D()REQUIRES_NEW
//外----
}
场景1:A方法出现异常,由于异常机制,导致代码停止,数据库没有任何新的数据进入
场景2:C方法出现异常,A回滚,B成功,C回滚,D和外无法执行
场景3:外执行成功后,又抛出异常。外事务感知到异常,A,C,外回滚,B,D成功。
场景4:D异常,A,C回滚,外执行不了,B成功,D自己回滚
场景5:C用try-catch执行,C出现异常回滚,外界没有感知到异常,A,B,D,外都成功。
总结:传播行为过程中,只要requires_new被执行过,就一定成功,required感知到异常就一定会回滚。
(运行的异常默认是一定回滚的,编译时的异常默认时不会滚的,但是可以用rollbackFor去指定哪些异常一定回滚,noRollbackFor去指定哪些异常不回滚)
传播行为总是来定义:当一个事务存在时,它内部的事务该怎样执行。
三.Spring事务失效的一种场景
看下面一处典型代码
@Service public class TestService { @Transactional(propagation = Propagation.REQUIRED) public void a(){ b(); c(); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void b(){ } @Transactional(propagation = Propagation.REQUIRES_NEW) public void c(){ } }
本来的想法是,b和c自己执行自己,互不干扰,谁抛异常,谁就回滚自己,按事务的传播行为来说,应该是这样,没毛病。
但是经过测试,你会发现,c如果有异常,那么执行成功的b仍然会回滚,为什么?
当我们用Controller去调用testService时,发现testService是一个Cglib代理类。
原因:spring只有被动态代理时才会产生事务,而我们在类的内部,直接调用本类的方法,是不需要经过代理对象的,调用的是TestService本类。
所以我们只需要由TestService的代理对象去执行相关方法。
1.导入相关starter
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
2.在springboot主应用上开启EnableAspectJAutoProxy注解,并申明可暴露代理对象
3.在对应的方法中获取代理对象,并使用代理对象去执行方法
@Service public class TestService { @Transactional(propagation = Propagation.REQUIRED) public void a(){ TestService proxy = (TestService)AopContext.currentProxy(); proxy.b(); proxy.c(); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void b(){ } @Transactional(propagation = Propagation.REQUIRES_NEW) public void c(){ } }