Spring事務不能回滾的深層次原因

擊水三千里發表於2019-03-14

開頭總述

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_5");
        user.setAge((byte)1);
        userMapper.insertSelective(user);
        
    }
    
    @Override
    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void insertChild(){
        user.setName("child_5");
        user.setAge((byte)1);
        userMapper.insertSelective(user);
        int i=1/0;
    }


執行結果


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

    Spring事務不能回滾,根源是類內部呼叫是不走代理物件Proxy的。this.child走的是真實物件(真實物件)。導致child上的事務無效,先執行insert語句,執行成功以後才丟擲異常。異常被parent中的try catch捕獲到。不影響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方法。
   

    @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;
    }

執行結果

 

相關文章