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(){
}
}

浙公网安备 33010602011771号