Fork me on GitHub

Spring service本类中方法互相调用事物失效问题

简介

Spring事物利用的是AOP,动态代理采用CGLIB代理(默认,也可以用Proxy代理,但是Proxy代理效率低于CGLIB代理)。故只要弄懂Spring的AOP实现,就知道为什么service本类中方法互相调用会导致事物失效。

失效案例

service层代码

 public void  moneyTestOne(){
        //1.查询病人基本信息
        List<TBICXX> tbicxx = basisDao.getTBICXX();
        //2.根据CMZH更新TBMZFYHZ
        Integer TBMZFYHZCount = tbmzfyhzDao.updateByCMZH("王五", "1903003643");
        log.info("更新TBMZFYHZ影响行数{}",TBMZFYHZCount);
        this.moneyTestTwo();

    }

    @Transactional(rollbackFor = Exception.class,propagation=Propagation.REQUIRED)
    public void  moneyTestTwo(){
        //3.根据CMZH更新TBMZFYMXGH
        Integer integer = tbmzfymxghDao.updateByCMZH("王五", "2107000224");
        log.info("更新TBMZFYMXGH影响行数{}",integer);
        int i=2/0;
    }

Test层代码

/**
     * 测试其AOP事物(CGLIB代理):
     *   如moneyTestOne不添加事物,则moneyTestTwo事物及其一切传播行为将失效,这是由于spring的AOP的Cglib代理造成
     *   同理: moneyTestOne添加事物,moneyTestTwo事物及其一切传播行为也将失效,moneyTestTwo运行在moneyTestOne事物中。
     *   $$EnhancerBySpringCGLIB$$2364c0b7 代理
     **/
    @Test
    public void testTransationalPropagation(){
        log.info("测试开始,代理类{}",springTransactionSqlserverServiceImpl.getClass());
        springTransactionSqlserverServiceImpl.moneyTestOne();
    }

运行前
image
运行后
image
结论
moneyTestTwo方法事物并不生效,要想事物生效,只需要在moneyTestOne方法上加上事物注解(此方法同理会导致moneyTestTwo方法传播行为失效),

失效分析

Spring通过AopProxy接口,抽象了这两种实现,实现了一致的AOP方式:
image
现在看来,这种抽象同样带了一个缺陷,那就是抹杀了Cglib能够直接创建普通类的增强子类的能力。下图显示了Spring的AOP代理类的实际调用过程:
image

解决办法

事物声明避免在一个类中互相调用,即moneyTestOne和moneyTestTwo要么都使用事物,要么直接分成两个类。

Gitee代码地址

https://gitee.com/zhuayng/foundation-study/tree/develop/SpringBootDemo/src/main/java/com/yxkj/springbootdemo/service/impl

posted @ 2021-11-06 13:43  晨度  阅读(1116)  评论(0编辑  收藏  举报