事务传播行为(propagation behavior):指的就是当一个事务方法被另一个事务方法调用时,这个事务与事务应该如何运行。
事务传播行为是Spring框架独有的事务增强特性。这是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的情况下,内部子事务可以单独回滚而不影响外围主事务和其他子事务 |
声明:
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;
}
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
外围方法:
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;
}
运行结果:
外围方法:
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);
}
运行结果:
外围方法:
@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;
}
运行结果:
外围方法:
@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);
}
运行结果:
外围方法:
@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异常方法!");
}
}
运行结果:
PROPAGATION_REQUIRED类型事务总结:
对应场景 | 外围是否开启事务 | 外围方法是否包含异常 | 外围是否捕获异常 | person方法是否包含异常 | 最终执行结果 |
---|---|---|---|---|---|
3.1.1 | × | √ | × | × | 均成功 |
3.1.2 | × | × | × | √ | user成功,person失败 |
3.1.3 | √ | √ | × | × | 均失败 |
3.1.4 | √ | × | × | √ | 均失败 |
3.1.5 | √ | √ | 捕获person的异常 | √ | 均失败 |
PROPAGATION_REQUIRED类型事务结论:
在外围方法开启事务的情况下,内部方法会加入到外围方法的事务中,所有内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚。
@Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
PROPAGATION_SUPPORTS类型事务总结:
经验证:
外围开启SUPPORTS,且内部也是SUPPORTS情况下,相当于没有事务;
外围开启SUPPORTS,内部方法开启别的事务,内部方法按开启的事务类型执行,互不影响;
@Transactional(propagation = Propagation.MANDATORY,rollbackFor = Exception.class)
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]
外围方法:
@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;
}
运行结果:
外围方法:
@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);
}
运行结果:
外围代码:
@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;
}
运行结果:
外围方法:
@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;
}
运行结果:
第一条插入失败,其他成功
外围代码:
@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);
}
运行结果:
第二条成功,其他失败
外围代码:
@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也会成功;
略
略
外围代码:
@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表插入成功:
外围方法:
@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表插入成功:
外围方法:
@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表插入失败:
外围方法:
@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表插入失败:
外围方法:
@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表插入成功:
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的区别;
滚。但是REQUIRED是加入外围方法事务,所以和外围事务同属于一个事务,一旦REQUIRED事务抛出异常被回滚,外围方法
事务也将被回滚。而NESTED是外围方法的子事务,有单独的保存点,所以NESTED方法抛出异常被回滚,不会影响到外围方
法的事务。
务,所以外围方法回滚之后,作为外围方法事务的子事务也会被回滚。而REQUIRES_NEW是通过开启新的事务实现的,内部
事务和外围事务是两个事务,外围事务回滚不会影响内部事务。
如: 系统在用户注册账号会赠送积分。
那我们是期望注册成功,才会赠送积分,注册失败的话,赠送的积分也要回滚;但是积分赠送失败,不能影响注册的执行流程;另外,假设我们要记录日志,那我们期望不管注册,还是赠送积分是否有异常,都不要回滚日志;
那结合事务传播特性,我们可以这样写:
注册方法:
@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){
//省略...
}
。
。
。