Spring事务不能回滚的深层次原因

开头总述

Spring在同一个类中调用function,事务会失效。

Spring事务是基于AOP代理来实现的。而AOP是使用JDK动态代理来实现的。


第一次试验

    /**
     * 父类调用子类
     * 子类失败,不能影响父类
     * 
     * 预期效果:child回滚,parent插入成功
     * 第一次试验 真实效果:都插入成功,child方法因为try catch导致事务未起作用。
     */  
    @Override
    @Transactional
    public void insertParent(){
        try {
            insertChild();
        } catch (Exception e) {
            // TODO: handle exception
        }
        
        user.setName("parent_6");
        user.setAge((byte)1);
        userMapper.insertSelective(user);
        
    }
    
    @Override
    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void insertChild(){
        user.setName("child_6");
        user.setAge((byte)1);
        userMapper.insertSelective(user);
        int i=1/0;
    }


执行结果


原因分析
    child方法被try catch住了,同时事务未生效。

   在SpringIoC容器中返回的调用的对象是代理对象而不是真实的对象,只有被动态代理直接调用的才会产生事务。this调用并非代理对象。this.insertChild走的是真实对象(真实对象)。导致child上的事务无效,先执行insert语句,执行成功以后才抛出异常。异常被parent中的try catch捕获到。不影响parent方法的执行。

第二次试验   

   /**
    * 预期效果:child回滚,parent插入成功
    * 第二试验 真实效果:child插入成功,parent插入回滚
    */

    @Override
    @Transactional
    public void insertParent(){        
        user.setName("parent_7");
        user.setAge((byte)1);
        userMapper.insertSelective(user);
        insertChild();        
    }
    
    @Override
    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void insertChild(){
        user.setName("child_7");
        user.setAge((byte)1);
        userMapper.insertSelective(user);
        int i=1/0;
    }


执行结果
    

原因分析
    去掉try catch,child方法抛出异常,导致回滚。同时异常向上抛,导致parent方法也回滚。

    child方法的事务失效,相当于child方法中的code挪到了parent方法中,也类似于在parent中抛出异常。

解决方案

    AOP需要先暴露出来。

<!-- 开启切面-->
<!-- 暴露AOP代理到ThreadLocal -->
<aop:aspectj-autoproxy expose-proxy="true"></aop:aspectj-autoproxy>

获取AOP代理需要从AOP的上下文来获取。得到当前AopProxy,然后通过AOP代理来调用child方法。

   /**
    * 预期效果:child回滚,parent插入成功
    * 第三试验真实效果:child回滚,parent插入成功
    */   
    @Override
    @Transactional
    public void insertParent(){        
            
        try {
            /**
             * AopContext.currentProxy()就类似于JDK代理中的
             * Proxy.newInstance得到的Proxy对象
             * UserService proxy=(UserService) AopContext.currentProxy();
             * proxy.insertChild();
             */
            ((UserService)AopContext.currentProxy()).insertChild();
        } catch (Exception e) {
            // TODO: handle exception
        }        
        user.setName("parent_7");
        user.setAge((byte)1);
        userMapper.insertSelective(user);    
    }
    
    @Override
    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void insertChild(){
        user.setName("child_7");
        user.setAge((byte)1);
        userMapper.insertSelective(user);
        int i=1/0;
    }

执行结果

思考:如果最后一段代码 @Transactional(propagation=Propagation.REQUIRES_NEW)改为 @Transactional(propagation=Propagation. PROPAGATION_NESTED)会是什么结果

答案可以参考

Java中高级面试题总览(三)_依赖注入面试题-CSDN博客  里1.16有详细阐述事务的传播属性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数据与算法架构提升之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值