Spring事务传播行为总结
1、概念
事务传播行为(propagation behavior):指的就是当一个事务方法被另一个事务方法调用时,这个事务与事务应该如何运行。
事务传播行为是Spring框架独有的事务增强特性。这是Spring为我们提供的强大的工具箱,使用事务传播行为可以为我们的开发工作提供许多便利。
2、Spring的七种事务传播行为类型
Spring在TransactionDefinition接口中规定了7种类型的事务传播行为:
事务注解源码枚举:
总结:
事务传播行为类型 | 对应说明 | 验证结果 |
---|---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。(spring 默认的类型) | 外围方法未开启事务:内部方法各自执行,走各自的事务,互不影响;在外围方法开启事务的情况下,内部方法会加入到外围方法的事务中,所有内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 | 外围开启SUPPORTS,且内部也是SUPPORTS情况下,相当于没有事务;外围开启SUPPORTS,内部方法开启别的事务,内部方法按开启的事务类型执行,互不影响; |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 | 外围方法开启MANDATORY事务或者没有开启事务,不管内部方法任何事务类型或者内部方法均不开启事务,都会抛出异常;外围开启别的事务,内部方法会加入到外围方法的事务中一起执行 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 | 内部方法声明REQUIRES_NEW类型后会新建独立事务执行,与外围方法不走同一个事务,内部方法之间事务互不影响 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 | 内部方法声明后就是以非事务执行,外围方法对其try catch的情况下,内部方法的异常不会导致外围方法回滚 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 | 以非事务执行,如果加入到的外围方法中包含事务就会报错 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 | 外围方法在对内部方法try catch的情况下,内部子事务可以单独回滚而不影响外围主事务和其他子事务 |
3、各种行为类型及对应的结果验证
声明:
user方法 person方法 为不同的两个表的方法,外围方法同时调用 user 和 person;
user方法:
@Override
public void addUser(User user) {
userMapper.addUser(user);
}
person方法:
@Override
public void addPerson(Person person) {
personMapper.addPerson(person);
}
person含异常方法:
@Override
public void addPersonException(Person person) {
personMapper.addPerson(person);
int i = 1/0;
}
3.1、PROPAGATION_REQUIRED类型
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
场景3.1.1:外围方法不开启事务,User方法,Person方法均开启事务,且外围方法包含异常
外围方法:
public void noTransactional() {
User user = new User();
user.setId(1);
user.setName("addUser1");
user.setPwd("123456");
userService.addUser(user);
Person person = new Person();
person.setId(1);
person.setName("addPerson1");
personService.addPerson(person);
//异常
int i = 1/0;
}
运行结果:
user表插入成功:
person表插入成功:
场景3.1.2:外围方法不开启事务,User方法,Person方法均开启事务,且Person方法包含异常
外围方法:
public void noTransactional() {
User user = new User();
user.setId(1);
user.setName("addUser1");
user.setPwd("123456");
userService.addUser(user);
Person person = new Person();
person.setId(1);
person.setName("addPerson1");
personService.addPersonException(person);
}
运行结果:
user表插入成功:
person表插入失败:
场景3.1.3 外围方法开启事务,User方法,Person方法均开启事务,且外围方法包含异常
外围方法:
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
@Override
public void transactional() {
User user = new User();
user.setId(1);
user.setName("addUser1");
user.setPwd("123456");
userService.addUser(user);
Person person = new Person();
person.setId(1);
person.setName("addPerson1");
personService.addPerson(person);
//异常
int i = 1/0;
}
运行结果:
user表插入失败:
person表插入失败:
场景3.1.4 外围方法开启事务,User方法,Person方法均开启事务,且Person方法包含异常
外围方法:
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
@Override
public void transactional() {
User user = new User();
user.setId(1);
user.setName("addUser1");
user.setPwd("123456");
userService.addUser(user);
Person person = new Person();
person.setId(1);
person.setName("addPerson1");
personService.addPersonException(person);
}
运行结果:
user表插入失败:
person表插入失败:
场景3.1.5 外围方法开启事务,User方法,Person方法均开启事务,且Person方法包含的异常被try catch捕获
外围方法:
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
@Override
public void transactional() {
User user = new User();
user.setId(1);
user.setName("addUser1");
user.setPwd("123456");
userService.addUser(user);
Person person = new Person();
person.setId(1);
person.setName("addPerson1");
try{
personService.addPersonException(person);
}catch (Exception e){
System.out.println("捕获person异常方法!");
}
}
运行结果:
user表插入失败:
person表插入失败:
PROPAGATION_REQUIRED类型事务总结:
对应场景 | 外围是否开启事务 | 外围方法是否包含异常 | 外围是否捕获异常 | person方法是否包含异常 | 最终执行结果 |
---|---|---|---|---|---|
3.1.1 | × | √ | × | × | 均成功 |
3.1.2 | × | × | × | √ | user成功,person失败 |
3.1.3 | √ | √ | × | × | 均失败 |
3.1.4 | √ | × | × | √ | 均失败 |
3.1.5 | √ | √ | 捕获person的异常 | √ | 均失败 |
PROPAGATION_REQUIRED类型事务结论:
在外围方法开启事务的情况下,内部方法会加入到外围方法的事务中,所有内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚。
3.2 PROPAGATION_SUPPORTS类型
@Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
PROPAGATION_SUPPORTS类型事务总结:
经验证:
外围开启SUPPORTS,且内部也是SUPPORTS情况下,相当于没有事务;
外围开启SUPPORTS,内部方法开启别的事务,内部方法按开启的事务类型执行,互不影响;
3.3 PROPAGATION_MANDATORY类型
@Transactional(propagation = Propagation.MANDATORY,rollbackFor = Exception.class)
- 如果外围方法和内部方法,均开启MANDATORY,则会报以下异常;
- 外围开启MANDATORY,内部其中一个开启SUPPORTS类型,另一个开启REQUIRED类型也会报以下异常,因为SUPPORTS默认就是无事务;
- 外围开启MANDATORY,内部均开启REQUIRED类型,也会抛如下异常;
- 外围开启MANDATORY,内部均不开启事务,也是抛如下异常;
- 外围开启别的事务,则内部方法都会加入到外围的事务中一起执行;
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:362) ~[spring-tx-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:572) ~[spring-tx-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:360) ~[spring-tx-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) ~[spring-tx-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749) ~[spring-aop-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691) ~[spring-aop-5.2.6.RELEASE.jar:5.2.6.RELEASE]
3.4 PROPAGATION_REQUIRES_NEW类型
3.4.1 外围不开启事务,user,person方法均开启REQUIRES_NEW类型事务,且外围方法有异常
外围方法:
@Override
public void noTransactional() {
User user = new User();
user.setId(1);
user.setName("addUser1");
user.setPwd("123456");
userService.addUser(user);
Person person = new Person();
person.setId(1);
person.setName("addPerson1");
personService.addPerson(person);
//异常
int i = 1/0;
}
运行结果:
user表插入成功:
person表插入成功:
3.4.2 外围不开启事务,user,person方法均开启REQUIRES_NEW类型事务,且person方法有异常
外围方法:
@Override
public void noTransactional() {
User user = new User();
user.setId(1);
user.setName("addUser1");
user.setPwd("123456");
userService.addUser(user);
Person person = new Person();
person.setId(1);
person.setName("addPerson1");
personService.addPersonException(person);
}
运行结果:
user表插入成功:
person表插入失败:
3.4.3 外围开启REQUIRED事务,user,person方法均开启REQUIRES_NEW类型事务,且外围方法有异常
外围代码:
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
@Override
public void transactional() {
User user = new User();
user.setId(1);
user.setName("addUser1");
user.setPwd("123456");
userService.addUser(user);
Person person = new Person();
person.setId(1);
person.setName("addPerson1");
personService.addPerson(person);
//异常
int i = 1/0;
}
运行结果:
user表插入成功:
person表插入成功:
3.4.4 外围开启REQUIRED事务,addPersonRequired方法开启REQUIRED事务,addPersonRequiredNew开启REQUIRES_NEW事务,且外围方法有异常
外围方法:
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
@Override
public void transactional() {
Person person1 = new Person();
person1.setId(1);
person1.setName("第一人");
personService.addPersonRequired(person1);
Person person2 = new Person();
person2.setId(2);
person2.setName("第二人");
personService.addPersonRequiredNew(person2);
Person person3 = new Person();
person3.setId(3);
person3.setName("第三人");
personService.addPersonRequiredNew(person3);
//异常
int i= 1/0;
}
运行结果:
第一条插入失败,其他成功
3.4.5 外围开启REQUIRED事务,addPersonRequired方法开启REQUIRED事务,addPersonRequiredNew开启REQUIRES_NEW事务,addPersonExceptionRequiredNew方法开启REQUIRES_NEW事务且有异常
外围代码:
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
@Override
public void transactional() {
Person person1 = new Person();
person1.setId(1);
person1.setName("第一人");
personService.addPersonRequired(person1);
Person person2 = new Person();
person2.setId(2);
person2.setName("第二人");
personService.addPersonRequiredNew(person2);
Person person3 = new Person();
person3.setId(3);
person3.setName("第三人");
personService.addPersonExceptionRequiredNew(person3);
}
运行结果:
第二条成功,其他失败
3.4.6 外围开启REQUIRED事务,addPersonRequired方法开启REQUIRED事务,addPersonRequiredNew开启REQUIRES_NEW事务,addPersonExceptionRequiredNew方法开启REQUIRES_NEW事务且有异常,被try catch
外围代码:
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
@Override
public void transactional() {
Person person1 = new Person();
person1.setId(1);
person1.setName("第一人");
personService.addPersonRequired(person1);
Person person2 = new Person();
person2.setId(2);
person2.setName("第二人");
personService.addPersonRequiredNew(person2);
Person person3 = new Person();
person3.setId(3);
person3.setName("第三人");
try{
personService.addPersonExceptionRequiredNew(person3);
}catch (Exception e){
System.out.println("存入第三人异常!");
}
}
运行结果:
第三条失败,其他成功
PROPAGATION_REQUIRES_NEW类型事务总结:
对应场景 | 外围方法是否开启事务及事务类型 | 内部方法事务类型 | 是否有异常 | 最终执行结果 |
---|---|---|---|---|
3.4.1 | × | 均为REQUIRES_NEW | 外围方法包含异常 | 均成功 |
3.4.2 | × | 均为REQUIRES_NEW | 内部person方法包含异常 | user成功,person失败 |
3.4.3 | REQUIRED | 均为REQUIRES_NEW | 外围方法包含异常 | 均成功 |
3.4.4 | REQUIRED | 1为REQUIRED, 2 3为REQUIRES_NEW | 外围方法包含异常 | 1失败,2 3成功 |
3.4.5 | REQUIRED | 1为REQUIRED, 2 3为REQUIRES_NEW | 3方法包含异常 | 2成功,1 3失败 |
3.4.6 | REQUIRED | 1为REQUIRED, 2 3为REQUIRES_NEW | 3方法包含异常且在外围方法中被try catch | 3失败,1 2成功 |
PROPAGATION_REQUIRES_NEW类型事务结论:
(1) 3.4.1,3.4.2和3.4.3说明:
REQUIRES_NEW类型修复的方法会新建事务,内部方法的事务各自执行,互不影响,且内部方法事务不会加入到外围方法事务;
(2) 3.4.4说明:
外围REQUIRED 1为REQUIRED ,所以1和外围方法会在同一个事务,外围方法有异常,所以1必然会回滚,2 3为REQUIRES_NEW,所以2 3会各自建立新的事务,不加入到外围方法事务,所以2 3成功;
(3) 3.4.5说明:
2 3为REQUIRES_NEW会新建事务,虽然不加到外围事务,但是外围是REQUIRED,内部的事务执行异常会传导到外围方法,外围方法发现异常直接回滚,3本身又包含异常,所以3失败,导致外围失败,1方法加入外围事务,所以1也失败,2是内部新建的独立事务,所以成功;
(4) 3.4.6说明:
2 3是独立新建事务,而3有异常,所以2会成功,3失败;而3的异常被外围方法捕获,所以外围不会回滚,正常执行,所以加入到外围事务的1也会成功;
3.5 PROPAGATION_NOT_SUPPORTED类型事务
略
3.6 PROPAGATION_NEVER类型事务
略
3.7 PROPAGATION_NESTED类型事务
3.7.1 外围不开启事务,user,person方法均开启NESTED类型事务,且外围方法有异常
外围代码:
@Override
public void noTransactional() {
User user = new User();
user.setId(1);
user.setName("addUser1");
user.setPwd("123456");
userService.addUser(user);
Person person = new Person();
person.setId(1);
person.setName("person");
personService.addPerson(person);
int i = 1/0;
}
执行结果:
user表插入成功:
person表插入成功:
3.7.2 外围不开启事务,user,person方法均开启NESTED类型事务,且person方法有异常
外围方法:
@Override
public void noTransactional() {
User user = new User();
user.setId(1);
user.setName("addUser1");
user.setPwd("123456");
userService.addUser(user);
Person person = new Person();
person.setId(1);
person.setName("person");
personService.addPersonException(person);
}
执行结果:
user表插入成功:
person表插入失败:
3.7.3 外围开启REQUIRED类型事务,user,person方法均开启NESTED类型事务,且外围方法有异常
外围方法:
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
@Override
public void transactional() {
User user = new User();
user.setId(1);
user.setName("addUser1");
user.setPwd("123456");
userService.addUser(user);
Person person = new Person();
person.setId(1);
person.setName("person");
personService.addPerson(person);
int i = 1/0;
}
运行结果:
user表插入失败:
person表插入失败:
3.7.4 外围开启REQUIRED类型事务,user,person方法均开启NESTED类型事务,且person方法有异常
外围方法:
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
@Override
public void transactional() {
User user = new User();
user.setId(1);
user.setName("addUser1");
user.setPwd("123456");
userService.addUser(user);
Person person = new Person();
person.setId(1);
person.setName("person");
personService.addPersonException(person);
}
运行结果:
user表插入失败:
person表插入失败:
3.7.5 外围开启REQUIRED类型事务,user,person方法均开启NESTED类型事务,且person方法有异常在外围方法中捕获
外围方法:
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
@Override
public void transactional() {
User user = new User();
user.setId(1);
user.setName("addUser1");
user.setPwd("123456");
userService.addUser(user);
Person person = new Person();
person.setId(1);
person.setName("person");
try{
personService.addPersonException(person);
}catch (Exception e){
System.out.println("异常捕获!");
}
}
运行结果:
user表插入成功:
person表插入失败:
PROPAGATION_NESTED类型事务总结:
对应场景 | 外围方法是否开启事务 | 内部方法事务类型 | 是否包含异常 | 最终执行结果 |
---|---|---|---|---|
3.7.1 | × | NESTED类型 | 外围方法包含异常 | 均成功 |
3.7.2 | × | NESTED类型 | person包含异常 | user成功,person失败 |
3.7.3 | REQUIRED | NESTED类型 | 外围方法包含异常 | 均失败 |
3.7.4 | REQUIRED | NESTED类型 | person包含异常 | 均失败 |
3.7.5 | REQUIRED | NESTED类型 | person包含异常且在外围方法捕获 | user成功,person失败 |
结论:
(1)在外围方法未开启事务的情况下NESTED和REQUIRED作用相同,修饰的内部方法都会新开启自己的事务,且开启的事务相互独立,互不干扰。
(2)在外围方法开启REQUIRED事务的情况下,NESTED修饰的内部方法属于外部事务的子事务,外围主事务回滚,子事务一定回滚,而内部子事务可以单独回滚而不影响外围主事务和其他子事务,这是与REQUIRED的区别;
4. 关于REQUIRED,REQUIRES_NEW,NESTED异同
- NESTED 和 REQUIRED 修饰的内部方法都属于外围方法事务,如果外围方法抛出异常,这两种方法的事务都会被回
滚。但是REQUIRED是加入外围方法事务,所以和外围事务同属于一个事务,一旦REQUIRED事务抛出异常被回滚,外围方法
事务也将被回滚。而NESTED是外围方法的子事务,有单独的保存点,所以NESTED方法抛出异常被回滚,不会影响到外围方
法的事务。
- NESTED 和 REQUIRES_NEW 都可以做到内部方法事务回滚而不影响外围方法事务。但是因为NESTED是嵌套事
务,所以外围方法回滚之后,作为外围方法事务的子事务也会被回滚。而REQUIRES_NEW是通过开启新的事务实现的,内部
事务和外围事务是两个事务,外围事务回滚不会影响内部事务。
5. 事务传播特性的应用场景
如: 系统在用户注册账号会赠送积分。
那我们是期望注册成功,才会赠送积分,注册失败的话,赠送的积分也要回滚;但是积分赠送失败,不能影响注册的执行流程;另外,假设我们要记录日志,那我们期望不管注册,还是赠送积分是否有异常,都不要回滚日志;
那结合事务传播特性,我们可以这样写:
注册方法:
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void register(User user){
//省略...
try {
//添加积分
pointService.addPoint(Point point);
} catch (Exception e) {
//省略...
}
//省略...
}
添加积分方法:
@Transactional(propagation = Propagation.NESTED,rollbackFor = Exception.class)
public void addPoint(Point point){
//省略...
try {
recordService.addLog(Record record);
} catch (Exception e) {
//省略...
}
//省略...
}
//省略...
记录日志方法:
@Transactional(propagation = Propagation.NOT_SUPPORTED,rollbackFor = Exception.class)
public void addLog(Record record){
//省略...
}
。
。
。