spring事务的传播行为
一、事务的传播行为分类
根据 spring 源码中 org.springframework.transaction.annotation.Propagation 枚举类中的定义,总共有 7 中传播行为:
传播行为分类 | 说明 |
---|---|
REQUIRED(默认的传播行为) | 1、使用当前事务,如果当前没有事务,会新建一个事务 2、子方法也会运行在父方法的事务中,如果子方法存在事务,则会加入父方法的事务中 |
SUPPORTS | 当前存在事务,就使用事务;当前不存在事务,就不使用 |
MANDATORY | 当方法的事务传播行为设置为 MANDATORY 时,其它任何方法调用该方法时,都必须存在事务,否则抛出异常 |
REQUIRES_NEW | 1、如果当前有事务,挂起当前事务,新建一个事务给自己使用 2、如果当前没有事务,则新建一个事务 |
NOT_SUPPORTED | 不管当前有无事务,都不使用事务 |
NEVER | 不使用事务,如果调用方当前存在事务,抛出异常 |
NESTED | 如果当前有事务,则开启子事务(属于嵌套事务),嵌套事务是独立提交或者回滚的 |
二、简要示例
下面通过一些伪代码,来描述几种常用的传播行为:
REQUIRED
1、父方法中不存在事务,子方法中也不存在事务
class parentClass {
// 父方法没有事务
void parentMethod(){
// 父方法中调用子方法
subClass.subMethod();
...
}
}
class subClass {
// 子方法中也没有事务
void subMethod(){
...
}
}
这种情况下,出现异常之后,子父方法中的数据都不会回滚
2、父方法中存在事务,子方法中不存在事务
class parentClass {
// 父方法存在事务
@Transactional(propagation = Propagation.REQUIRED)
void parentMethod(){
// 父方法中调用子方法
subClass.subMethod();
...
}
}
class subClass {
// 子方法中没有事务
void subMethod(){
...
}
}
这种情况下,父方法的事务会传递到子方法中,所以子父方法中只要出现异常,都会回滚数据
3、父方法中不存在事务,子方法中存在事务
class parentClass {
// 父方法中不存在事务
void parentMethod(){
// 父方法中调用子方法
subClass.subMethod();
...
}
}
class subClass {
// 子方法中存在事务
@Transactional(propagation = Propagation.REQUIRED)
void subMethod(){
...
}
}
- 这种情况下,如果子方法中出现异常,子方法中的数据会回滚,但是父方法中的数据不会回滚
- 如果是子方法执行完成后,父方法出现异常,这时子方法的数据不会回滚,因为事务已经提交。父方法的数据也不会回滚,因为父方法没有事务
4、父方法中存在事务,子方法中也存在事务
class parentClass {
// 父方法中存在事务
@Transactional(propagation = Propagation.REQUIRED)
void parentMethod(){
// 父方法中调用子方法
subClass.subMethod();
...
}
}
class subClass {
// 子方法中也存在事务
@Transactional(propagation = Propagation.REQUIRED)
void subMethod(){
...
}
}
这种情况下,子方法会加入到父方法的事务中,所以子父方法中只要出现异常,都会回滚数据
MANDATORY
1、父方法不存在事务
class parentClass {
// 父方法中不存在事务
void parentMethod(){
// 父方法中调用子方法
subClass.subMethod();
...
}
}
class subClass {
// 子方法中存在事务
@Transactional(propagation = Propagation.MANDATORY)
void subMethod(){
...
}
}
这种情况下,父方法在调用子方法时,会直接抛出异常,因为父方法没有事务
2、父方法存在事务
class parentClass {
// 父方法中存在事务
@Transactional(propagation = Propagation.REQUIRED)
void parentMethod(){
// 父方法中调用子方法
subClass.subMethod();
...
}
}
class subClass {
// 子方法中也存在事务
@Transactional(propagation = Propagation.MANDATORY)
void subMethod(){
...
}
}
这种情况下,父方法调用子方法时,是存在事务的,所以不会抛出异常。此时父方法的事务会传递到子方法中,所以只要是子父方法中出现异常,都会回滚数据
REQUIRES_NEW
1、父方法中不存在事务
class parentClass {
// 父方法中不存在事务
void parentMethod(){
// 父方法中调用子方法
subClass.subMethod();
...
}
}
class subClass {
// 子方法中存在事务
@Transactional(propagation = Propagation.REQUIRES_NEW)
void subMethod(){
...
}
}
这种情况下,如果子方法出现异常,子方法的数据会回滚。父方法中的数据不会回滚
2、父方法中存在事务
class parentClass {
// 父方法中存在事务
@Transactional(propagation = Propagation.REQUIRES)
void parentMethod(){
// 父方法中调用子方法
subClass.subMethod();
...
}
}
class subClass {
// 子方法中存在事务
@Transactional(propagation = Propagation.REQUIRES_NEW)
void subMethod(){
...
}
}
这种情况下,需要注意:
- 如果子方法中出现了异常,子方法的数据会回滚,子方法的异常会传递到父方法中,父方法的数据也会回滚掉
- 如果子方法中没有出现异常,但是父方法在执行完子方法后,出现了异常,这时只会回滚父方法的数据,子方法的数据不会回滚,因为子方法的事务已经提交
NESTED
重点说下 NESTED 和 REQUIRED 的区别:
伪代码演示如下:
1、子父方法都是 Propagation.REQUIRED
class ParentClass {
@Transactional(propagation = Propagation.REQUIRED)
public void parentMethod() {
// 父方法插入数据
stuMapper.insertSelective("父方法插入数据");
// 调用子方法
subClass.subMethod();
}
}
class SubClass {
@Transactional(propagation = Propagation.REQUIRED)
public void subMethod() {
// 插入数据
stuMapper.insertSelective("子方法插入数据");
int i = 1 / 0;
}
}
这种情况下,subMethod() 会加入到 parentMethod() 的事务中去,如果 subMethod() 出现异常,则会直接回滚掉整个事务
2、子方法是 Propagation.NESTED
class ParentClass {
@Transactional(propagation = Propagation.REQUIRED)
public void parentMethod() {
// 父方法插入数据
stuMapper.insertSelective("父方法插入数据");
// 调用子方法
try {
// 调用subMethod()方法时,开启了一个嵌套事务,如果subMethod()出现异常,不会回滚parentMethod()的事务,嵌套事务独立回滚
subClass.subMethod();
} catch (Exception e) {
// 打印日志
}
// 继续执行其它方法
}
}
class SubClass {
@Transactional(propagation = Propagation.NESTED)
public void subMethod() {
// 插入数据
stuMapper.insertSelective("子方法插入数据");
int i = 1 / 0;
}
}
这种情况下,subMethod()开启了一个嵌套事务,如果subMethod()出现异常,不会回滚parentMethod()的事务,因为嵌套事务会独立回滚。