关于@Transactional注解失效的场景详解

关于@Transactional注解失效的几种场景


前言
声明式事务注解@Transactional 是我们在开发过程当中碰到需要处理事务问题时经常使用到的注解,但在使用时需要注意在某些场景下@Transactional 可能会失效,网上搜索过一些但是感觉说的不全面,为此希望将@Transactional 事务失效的场景详细罗列以供大家参考


首先是@Transactional 事务生效的情况

    /**
     * 加入事务、不调用其他方法(事务生效)
     */
    @Transactional
    public void addMpUserToDB1() {
        MpUser user = getMpUser(1);
        mpUserMapper.insert(user);
    }

    /**
     * 加入事务、最后抛出【运行时异常】(事务生效:出现异常会回滚)
     */
    @Transactional
    public void addMpUserToDB2() {
        MpUser user = getMpUser(2);
        this.mpUserMapper.insert(user);
        int i = 1 / 0;
    }
    
    /**
     * 当前类有事务方法调用当前类的事务方法(事务生效,下游的事务方法中出现异常会进行全面回滚)
     */
    @Transactional
    public void addMpUserToDB10() {
        MpUser user = getMpUser(10);
        this.mpUserMapper.insert(user);
        addMpUserToDB3();
    }

事务失效场景一:异常被try catch捕获

    /**
     * 【事务失效场景1 : 异常被try catch捕获】
     *  加入事务、最后抛出【运行时异常】、自行try catch
     * (事务失效1:异常会被try catch吞掉,不会进行回滚)
     */
    @Transactional
    public void addMpUserToDB3() {
        try {
            MpUser user = getMpUser(3);
            this.mpUserMapper.insert(user);
            int i = 1 / 0;
        } catch (Exception e) {
            log.error("保存用户信息出错:{}", e);
        }
    }

事务失效场景二、抛出的异常没有在 rollbackFor中指定

    /**
     * 【事务失效场景2:抛出的异常没有在 rollbackFor中指定】
     *  加入事务、最后抛出【检查型异常】(事务失效2:出现异常不会回滚)
     *  注意:手动指定rollback = ParseException.class后会进行回滚
     */
    @Transactional //(rollbackFor = {ParseException.class})
    public void addMpUserToDB4() throws ParseException {
        MpUser user = getMpUser(4);
        mpUserMapper.insert(user);
        new SimpleDateFormat("yyyy-MM-dd").parse("123123");
    }

事务失效场景三:本类无事务方法调用本类有事务方法

    /**
     * 【事务失效场景3:本类无事务方法调用本类有事务方法】
     * 当前方法无事务,使用this调用当前类的事务方法(事务失效3:事务方法中出现异常不会回滚)
     * 原理:发生自调用,类里面使用this调用本类的方法,此时这个this不是代理对象,所以事务失效
     */
    public void addMpUserToDB6() {
        addMpUserToDB3();
    }

这种情况,可以通过使用Spring代理对象调用方法来使事务生效:

    /**
     * 当前方法无事务,使用spring容器中对象引用调用当前类的事务方法(事务生效:事务方法中出现异常会回滚)
     * 原理:发生自调用,类里面使用容器中的代理类调用的事务方法,所以事务生效
     */
    public void addMpUserToDB7() {
        springTransactionDemoService.addMpUserToDB3();
    }

    /**
     * 当前方法无事务,调用其他类的事务方法(事务生效:事务方法中出现异常会回滚)
     * 原理:发生自调用,类里面使用容器中其他类的代理对象调用的事务方法,所以事务生效
     */
    public void addMpUserToDB8() {
        springTransactionDemoService2.addMpUserToDB8();
    }

    /**
     * 使用当前类的代理对象调用事务方法(事务生效)
     */
    public void addMpUserToDB9() {
        ((SpringTransactionDemoService) AopContext.currentProxy()).addMpUserToDB3();
    }

事务失效场景四、事务方法被非public修饰

    /**
     * 【事务失效场景4:事务方法被非public修饰】
     */
    @Transactional
    void addMpUserToDB11() {
        MpUser user = getMpUser(11);
        this.mpUserMapper.insert(user);
        int i = 1 / 0;
    }

事务失效场景五、事务方法被final修饰

    /**
     * 【事务失效场景5:事务方法被final修饰】
     * 被final修饰的方法,代理类中是获取不到成员属性的
     */
    @Transactional
    public final void addMpUserToDB12() {
        MpUser user = getMpUser(12);
        this.mpUserMapper.insert(user);
    }

事务失效场景六、设置了错误的传播行为

    /**
     * 【事务失效场景6:设置了错误的传播行为】
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void addMpUserToDB13() {
        MpUser user = getMpUser(13);
        this.mpUserMapper.insert(user);
        int i = 1 / 0;
    }
    public static MpUser getMpUser(Integer num) {
        MpUser user = new MpUser();
        user.setName("事务测试用户" + num);
        return user;
    }

事务失效场景七、多线程情况下@Transactional事务失效

主要原因:
1、事务管理范围不正确:@Transactional注解仅对当前方法有效,如果在方法内创建新的线程或使用线程池等异步操作,该方法之外的代码将无法受到事务的管理。因此,在使用多线程进行批量操作时,需要确保整个批量操作处于同一事务管理范围内。
2、Spring事务和Java线程池机制的互动问题:在使用ThreadPoolExecutor进行批量操作时,线程池中的线程和Spring管理的事务并不是同一个线程,这可能会导致事务管理器感知不到线程中的异常,从而导致事务未能回滚。

最后

Spring声明式事务底层还是对数据库事务的支持和增强,事务隔离级别也以Spring事务设置的为准,当使用的数据库不支持事务,如mysql的存储引擎由 Innodb切换为 myisam,@Transaction注解自然也就失效了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值