配置
以下验证部分的配置:
技术栈 | 版本 |
---|---|
MySQL | 5.7.42 |
SpringBoot | 2.6.13 |
简述
下面说到的当前是指父方法是否有事务传递下来。
- REQUIRED(默认):当前存在事务则加入当前事务,不存在则创建一个事务。因为是共用事务,所以父方法catch子方法的异常还是会全部回滚。
- SUPPORTS:当前存在事务则加入当前事务,不存在则以非事务方式运行。
- MANDATORY:当前存在事务则加入当前事务,不存在则抛出异常。
- REQUIRES_NEW:创建一个新事务,当前存在事务则挂起当前事务。
- NOT_SUPPORTED:始终以非事务方法运行,当前存在事务则挂起当前事务。
- NEVER:以非事务方法运行,当前存在事务则抛出异常。
- NESTED:当前存在事务则创建子事务,父事务回滚会导致子事务回滚。而子事务回滚,如果父事务catch了异常则父事务不会回滚。当前不存在事务则创建新事务。
各种事务传播机制,指的是子事务使用该传播机制子方法和父方法的事务有什么关系。
后面的验证,如果没有特殊说明,默认父方法都正确处理子方法抛出的异常。事务是否回滚要结合数据库看,因为其中一个方法抛异常就会导致几个方法都回滚。
一、验证REQUIRED的事务传播机制
1.子事务回滚
@Autowired
private IMyAppUserService myAppUserService;
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void testA() {
try {
MyAppUser myAppUser = new MyAppUser();
myAppUser.setUserNo("A" + UUID.randomUUID());
myAppUser.setNickName("A事务");
this.save(myAppUser);
myAppUserService.testB();
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
log.error("A事务回滚了");
}
}
@Override
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void testB() {
try {
MyAppUser myAppUser = new MyAppUser();
myAppUser.setUserNo("B" + UUID.randomUUID());
myAppUser.setNickName("B事务");
this.save(myAppUser);
int a=10/0;
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
log.error("B事务回滚了");
}
}
结果:父事务回滚了,子事务也回滚了。符合共用一个事务的说法。
这里其实还涉及到一个知识点,网上找资料显示,这里会接着抛出异常,是因为子事务先回滚了,但父方法并不知道,于是继续执行,到后面要提交父事务的时候才发现子事务回滚了,父事务也就没法提交了,跟着回滚并抛出异常提醒。(这属于是子事务回滚带这父事务回滚)
2.父事务回滚
@Autowired
private IMyAppUserService myAppUserService;
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void testA() {
try {
MyAppUser myAppUser = new MyAppUser();
myAppUser.setUserNo("A" + UUID.randomUUID());
myAppUser.setNickName("A事务");
this.save(myAppUser);
myAppUserService.testB();
int a=10/0;
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
log.error("A事务回滚了");
}
}
@Override
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void testB() {
try {
MyAppUser myAppUser = new MyAppUser();
myAppUser.setUserNo("B" + UUID.randomUUID());
myAppUser.setNickName("B事务");
this.save(myAppUser);
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
log.error("B事务回滚了");
}
}
结果:父事务回滚了,子事务也回滚了,符合共用一个事务的说法。
结论:REQUIRED事务传播级别下,父子共用事务,随便一个回滚都会导致整个事务回滚。
二、验证REQUIRES_NEW的事务传播机制
1.子事务回滚
@Autowired
private IMyAppUserService myAppUserService;
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void testA() {
try {
MyAppUser myAppUser = new MyAppUser();
myAppUser.setUserNo("A" + UUID.randomUUID());
myAppUser.setNickName("A事务");
this.save(myAppUser);
myAppUserService.testB();
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
log.error("A事务回滚了");
}
}
@Override
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
public void testB() {
try {
MyAppUser myAppUser = new MyAppUser();
myAppUser.setUserNo("B" + UUID.randomUUID());
myAppUser.setNickName("B事务");
this.save(myAppUser);
int a=10/0;
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
log.error("B事务回滚了");
}
}
结果:子事务回滚,父事务没有回滚。
2.父事务回滚
@Autowired
private IMyAppUserService myAppUserService;
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void testA() {
try {
MyAppUser myAppUser = new MyAppUser();
myAppUser.setUserNo("A" + UUID.randomUUID());
myAppUser.setNickName("A事务");
this.save(myAppUser);
myAppUserService.testB();
int a=10/0;
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
log.error("A事务回滚了");
}
}
@Override
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
public void testB() {
try {
MyAppUser myAppUser = new MyAppUser();
myAppUser.setUserNo("B" + UUID.randomUUID());
myAppUser.setNickName("B事务");
this.save(myAppUser);
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
log.error("B事务回滚了");
}
}
结果:父事务回滚,子事务没有回滚。
结论:REQUIRES_NEW事务传播级别下,父事务和子事务是完全互相独立的事务,不会互相影响。
三、验证NESTED的事务传播机制
提前说明一点,如果子事务的异常往外抛,父方法没有正确处理,就相当于父方法也发生了异常,就会导致都回滚,这和事务传播机制没有关系。
1.子事务回滚
现在测试子事务回滚,但是子事务处理了自己的异常(或者父方法额外处理了),看看父事务会怎么样。
@Autowired
private IMyAppUserService myAppUserService;
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void testA() {
try {
MyAppUser myAppUser = new MyAppUser();
myAppUser.setUserNo("A" + UUID.randomUUID());
myAppUser.setNickName("A事务");
this.save(myAppUser);
myAppUserService.testB();
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
log.error("A事务回滚了");
}
}
@Override
@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
public void testB() {
try {
MyAppUser myAppUser = new MyAppUser();
myAppUser.setUserNo("B" + UUID.randomUUID());
myAppUser.setNickName("B事务");
this.save(myAppUser);
int a=10/0;
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
log.error("B事务回滚了");
}
}
结果:结合日志和数据库,父事务的数据成功插入了。只有子事务回滚了,父事务不受影响。
2.父事务回滚
@Autowired
private IMyAppUserService myAppUserService;
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void testA() {
try {
MyAppUser myAppUser = new MyAppUser();
myAppUser.setUserNo("A" + UUID.randomUUID());
myAppUser.setNickName("A事务");
this.save(myAppUser);
myAppUserService.testB();
int a=10/0;
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
log.error("A事务回滚了");
}
}
@Override
@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
public void testB() {
try {
MyAppUser myAppUser = new MyAppUser();
myAppUser.setUserNo("B" + UUID.randomUUID());
myAppUser.setNickName("B事务");
this.save(myAppUser);
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
log.error("B事务回滚了");
}
}
结果:子事务的数据也没有存下来,说明在回滚父事务时,会把子事务也一起回滚(虽然没有进入子方法的catch里面)。
3.子事务回滚,但父事务未正确处理子事务异常
这个其实很好理解,子方法的异常抛到父方法,父方法没有额外用一个try catch专门处理调用子方法的异常,自然就导致父方法也回滚。
@Autowired
private IMyAppUserService myAppUserService;
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void testA() {
try {
MyAppUser myAppUser = new MyAppUser();
myAppUser.setUserNo("A" + UUID.randomUUID());
myAppUser.setNickName("A事务");
this.save(myAppUser);
myAppUserService.testB();
int a=10/0;
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
log.error("A事务回滚了");
}
}
@Override
@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
public void testB() {
try {
MyAppUser myAppUser = new MyAppUser();
myAppUser.setUserNo("B" + UUID.randomUUID());
myAppUser.setNickName("B事务");
this.save(myAppUser);
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
log.error("B事务回滚了");
throw new RuntimeException("把异常抛出去");
}
}
结果:两个事务都回滚了。
结论:NESTED事务传播级别下,父事务和子事务存在影响关系,父事务回滚,子事务一定也回滚;子事务回滚,如果父事务正确处理子方法的异常,则父事务不会回滚。
总结论
- REQUIRED事务传播级别下,父子共用事务,随便一个回滚都会导致整个事务回滚。
- REQUIRES_NEW事务传播级别下,父事务和子事务是完全互相独立的事务,不会互相影响。
- NESTED事务传播级别下,父事务和子事务存在影响关系,父事务回滚,子事务一定也回滚;子事务回滚,如果父事务正确处理子方法的异常,则父事务不会回滚。
在某些场景下,如果子方法是用来处理一些不太重要的内容,希望这些子方法抛异常不会影响主流程,主流程回滚会回滚这些子方法,则可以考虑给这些子方法使用NESTED传播机制,例如日志打印。