事务嵌套和捕获异常的关系以及问题解决方案

捕捉异常使用的一些规则笔记如下:(拓展)
    
    异常配套使用一:
    throw new RuntimeException();
    throws RuntimeException
    
    异常配套使用二:
    try {
        testTwoMapper.updateStatus(userCode, userName, id, status);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    对于配套使用一和二外层/上层只需要用try catch 捕捉就可以了

 

 

 

特别场景一:A 和 B 2个方法,B中用@Transactional管理事务,A调用B
如果在同一个类C中,B事务将失效。

eg:
public class C{
    public void a(){
        b();
    }
    @Transactional
    public void b(){
         
    }
}


其实是spring aop动态代理机制给当前类C生成代理类PolicyC,然后A调用B还是在C类中调用,代理类PolicyC中的B方法并没有被调用到,
只有在不同的类C和C1中,C类中的A方法调用C1类中的B方法,这样B中的事务就会生效。

eg:
public class C{
    @Autowired 
    private C1 c1;

    //注入C1类调用c1类中的B()方法
    public void A(){
       c1.B();
    }
}
public class C1{
    @Transactional
    public void B(){
    }
}




特别场景二:针对处事务中的方法,加入try catch后,事务的回滚和不会滚情况处理。

@Transactional
//todo 本地事务已处理(无自己类调用)
@Override
public UserLoginResEntity tokenValidate(String token) {
    User user = null;
    String usercode = "";
    try {
         usercode = tokenService.validate(token);
    }catch (Exception e){
        System.out.println("ddddddd" + e);
        /**
         * 如果必须捕捉异常,就扔出runtime异常,不然aop捕捉不到异常,事务会失效。
         * 或者扔出TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
         * 手动回滚事务
         */
        throw new RuntimeException();
    }

 

以下是各个博客找的概念觉得对事务理解比较清楚的:

默认spring事务只在发生未被捕获的 runtimeexcetpion时才回滚。  
spring aop  异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚, 默认情况下aop只捕获runtimeexception的异常,但可以通过配置来捕获特定的异常并回滚 。

换句话说在service的方法中不使用try catch 或者在catch中最后加上throw new runtimeexcetpion(),这样程序异常时才能被aop捕获进而回滚
解决方案: 
方案1:例如service层处理事务,那么service中的方法中不做异常捕获,或者在catch语句中最后增加throw new RuntimeException()语句,以便让aop捕获异常再去回滚,并且在service上层(webservice客户端,view层action)要继续捕获这个异常并处理。
方案2:在service层方法的catch语句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句,手动回滚,这样上层就无需去处理异常(现在项目的做法提现)。

让事务起作用,遇到错误进行回滚,应该注意的事项

第一种情况:同一个类中 一个方法无嵌套方法

 1、如果方法名上加上@Transactional注解,方法内不要用try catch ;如果必须要用try catch ,则catch中必须用throw new RuntimeException()。否则事务不起作用。

第二种情况:同一个类中 方法A嵌套方法B

1、方法A有@Transactional,方法内都没有try catch,事务起作用。

2、方法A有@Transactional和try catch,并且catch中用throw new RuntimeException(),事务起作用。

第三种情况:不同类中,方法C嵌套方法B

1、方法B上加上@Transactional注解,方法内不要用try catch ;如果必须要用try catch ,则catch中必须用throw new RuntimeException()。否则方法B的事务不起作用。

2、方法C上加上@Transactional注解,方法内不要用try catch ;如果必须要用try catch ,则catch中必须用throw new RuntimeException(),此时方法B怎么写都行。否则方法C的事务不起作用。

 

总结:
1、要想事务起作用,必须是主方法名上有@Transactional注解,方法体内不能用try catch;如果用try catch,则catch中必须用throw new RuntimeException();

2、@Transactional注解应该只被应用到public方法上,不要用在protected、private等方法上,即使用了也将被忽略,不起作用。这是由Spring AOP决定的。

3、只有来自外部的方法调用才会呗AOP代理捕捉,类内部方法调用类内部的其他方法,子方法并会不引起事务行为,即使被调用的方法上使用有@Transactional注解。

4、类内部方法调用内部的其他方法,被调用的方法体中如果有try catch,则catch中必须用throw new RuntimeException(),否则即使主方法上加上@Transactional注解,如果被调用的子方法出错也不会抛出异常,不会引起事务起作用。

下篇研究事务的传播机制,并且举例证明。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值