Spring事务多个业务场景分析

事务

业务逻辑上的一组操作,要么全部成功,要么全部失败,比如下面的伪代码:

public class MyService {
    
    //外界调用
    public void doService(){
        
        //A逻辑
        serviceA();
        
        //B逻辑
        serviceB();
        
        //C逻辑
        serviceC();
        
        //其他
        otherService();
    }
}

当外界调用doService方法,该方法里面的各个子逻辑单元,要么全部成功,要么全部失败

事务转账场景分析(事务的传播行为)

事务传播行为:同一个类中相互调用

同一个类AClass中,有两个函数aFunction、aInnerFunction。aFunction调用aInnerFunction。而且aFunction函数会被外部调用。

情况1: aFunction添加了@Transactional注解,aInnerFunction函数没有添加。aInnerFunction抛异常。
结果:两个方法都回滚,aInnerFunction依附在aFunction事务,aInnerFunction抛异常也间接等于aFunction抛异常
public class AClass {

    @Transactional(rollbackFor = Exception.class)
    public void aFunction() {
        //todo: 数据库操作A(增,删,该)
        aInnerFunction(); // 调用内部没有添加@Transactional注解的函数
    }

    private void aInnerFunction() {
        //todo: 操作数据B(做了增,删,改 操作)
        throw new RuntimeException("函数执行有异常!");
    }
}
情况2:两个函数都添加了@Transactional注解。aInnerFunction抛异常。
结果:同第一种情况一样,两个函数对数据库操作都会回滚。因为同一个类中函数相互调用的时候,内部函数添加@Transactional注解无效,也会依附于外部调用的方法事务。@Transactional注解只有外部调用才有效。
public class AClass {

    @Transactional(rollbackFor = Exception.class)
    public void aFunction() {
        //todo: 数据库操作A(增,删,该)
        aInnerFunction(); // 调用内部添加@Transactional注解的函数
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void aInnerFunction() {
        //todo: 操作数据B(做了增,删,改 操作)
        throw new RuntimeException("函数执行有异常!");
    }
}
情况3: aFunction不添加注解,aInnerFunction添加注解。aInnerFunction抛异常。
结果:两个函数对数据库的操作都不会回滚。因为内部函数@Transactional注解添加和没添加一样(内部函数添加@Transactional注解无效),两个方法都没事务
public class AClass {

    public void aFunction() {
        //todo: 数据库操作A(增,删,该)
        aInnerFunction(); // 调用内部添加@Transactional注解的函数
    }

    @Transactional(rollbackFor = Exception.class)
    protected void aInnerFunction() {
        //todo: 操作数据B(做了增,删,改 操作)
        throw new RuntimeException("函数执行有异常!");
    }
}
情况4:aFunction添加了@Transactional注解,aInnerFunction函数没有添加。aInnerFunction抛异常,不过在aFunction里面把异常抓出来了。aFunction能够正常往下执行,并没有抛出异常
结果:两个函数里面的数据库操作都成功。事务回滚的动作发生在当有@Transactional注解函数有对应异常抛出时才会回滚。
如果此时aFunction显示抛出异常,就会整体回滚
public class AClass {

    @Transactional(rollbackFor = Exception.class)
    public void aFunction() {
        //todo: 数据库操作A(增,删,该)
        try {
            aInnerFunction(); // 调用内部没有添加@Transactional注解的函数
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void aInnerFunction() {
        //todo: 操作数据B(做了增,删,改 操作)
        throw new RuntimeException("函数执行有异常!");
    }

}
事务传播行为:不同类中函数相互调用

两个类AClass、BClass。AClass类有aFunction、BClass类有bFunction。AClass类aFunction调用BClass类bFunction。最终会在外部调用AClass类的aFunction。

情况1:aFunction添加注解,bFunction不添加注解。bFunction抛异常。
结果:两个方法都会回滚,可类比在同类中相互调用的情况1
//类AClass 
@Service("aClass")
public class AClass {

    @Autowired
    private AccountMapperImpl accountMapper;


    @Autowired
    private BClass bClass;

    @Transactional
    public void aFunction(){
        accountMapper.addMoney();
        bClass.bFunction();
    }
}

//类BClass
@Service("bClass")
public class BClass {
    @Autowired
    private AccountMapperImpl accountMapper ;

    public void bFunction(){
        accountMapper.reduceMoney();
        throw new RuntimeException("bFunction函数方法调用异常");
    }
}
情况2:aFunction、bFunction两个函数都添加注解,bFunction抛异常。
结果:两个函数对数据库的操作都回滚了。两个函数里面用的还是同一个事务。这种情况下,你可以认为事务rollback了两次。
两个函数都有异常。
//类AClass 
@Service("aClass")
public class AClass {

    @Autowired
    private AccountMapperImpl accountMapper;


    @Autowired
    private BClass bClass;

    @Transactional
    public void aFunction(){
        accountMapper.addMoney();
        bClass.bFunction();
    }
}

//类BClass
@Service("bClass")
public class BClass {
    @Autowired
    private AccountMapperImpl accountMapper ;
	
	@Transactional
    public void bFunction(){
        accountMapper.reduceMoney();
        throw new RuntimeException("bFunction函数方法调用异常");
    }
}
情况3:aFunctio不添加注解、bFunction添加注解,bFunction抛异常。
结果:aFunctio方法不回滚,bFunction方法回滚,当前只存在bFunction方法的事务
//类AClass 
@Service("aClass")
public class AClass {

    @Autowired
    private AccountMapperImpl accountMapper;


    @Autowired
    private BClass bClass;

    public void aFunction(){
        accountMapper.addMoney();
        bClass.bFunction();
    }
}

//类BClass
@Service("bClass")
public class BClass {
    @Autowired
    private AccountMapperImpl accountMapper ;
	
	@Transactional
    public void bFunction(){
        accountMapper.reduceMoney();
        throw new RuntimeException("bFunction函数方法调用异常");
    }
}
情况4:aFunction、bFunction两个函数都添加注解,bFunction抛异常。aFunction抓出异常。
结果:两个方法执行不成功,还抛出了org.springframework.transaction.UnexpectedRollbackException: 
Transaction rolled back because it has been marked as rollback-only,两个方法处于同一事务,但是bFunction抛出异常,调了事务的rollback函数。
事务被标记了只能rollback了,程序继续执行,aFunction函数里面把异常给抓出来了,
这个时候aFunction函数没有抛出异常,既然你没有异常那事务就需要提交,会调事务的commit函数。
而之前已经标记了事务只能rollback-only(以为是同一个事务)。直接就抛异常了,不让调了。综上,两个方法都会回滚
//类AClass 
@Service("aClass")
public class AClass {

    @Autowired
    private AccountMapperImpl accountMapper;


    @Autowired
    private BClass bClass;

    @Transactional
    public void aFunction(){
     	accountMapper.addMoney();
        try {
            bClass.bFunction();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

//类BClass
@Service("bClass")
public class BClass {
    @Autowired
    private AccountMapperImpl accountMapper ;
	
	@Transactional
    public void bFunction(){
        accountMapper.reduceMoney();
        throw new RuntimeException("bFunction函数方法调用异常");
    }
}
情况5:aFunction、bFunction两个函数都添加注解,bFunction抛异常。aFunction抓出异常。
这里要注意bFunction函数@Transactional注解我们是有变化的,加了一个参数
propagation = Propagation.REQUIRES_NEW,控制事务的传播行为。表明是一个新的事务。
结果:bFunction函数里面的操作回滚了,aFunction里面的操作成功了。因为两者不是在同一个事务中了
//类AClass 
@Service("aClass")
public class AClass {

    @Autowired
    private AccountMapperImpl accountMapper;


    @Autowired
    private BClass bClass;

    @Transactional
    public void aFunction(){
     	//accountMapper.addMoney();如果当前存在事务写操作,会引起当前事务的挂起
        try {
            bClass.bFunction();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

//类BClass
@Service("bClass")
public class BClass {
    @Autowired
    private AccountMapperImpl accountMapper ;
	
	@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
    public void bFunction(){
        accountMapper.reduceMoney();
        throw new RuntimeException("bFunction函数方法调用异常");
    }
}

总结

Spring事务实际业务场景应该就是以上总结,首先要弄清楚事务注解中的各个属性,然后判断是在同一个类中方法相互调用呢还是不同类的方法调用,因为同一个类中函数相互调用的时候,内部函数添加@Transactional注解无效,会依附于外部调用的方法事务。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值