• @Transactional注解事务失效的八种原因分析


    @Transactional是一种基于注解管理事务的方式,spring通过动态代理的方式为目标方法实现事务管理的增强。

    @Transactional使用起来方便,但也需要注意引起@Transactional失效的场景,本文总结了七种情况,下面进行逐一分析。

    1、异常被捕获后没有抛出

    当异常被捕获后,并且没有再抛出,那么deleteUserA是不会回滚的。

    @Transactional
    public void deleteUser() {
        userMapper.deleteUserA();
        try {
            int i = 1 / 0;
            userMapper.deleteUserB();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    2、抛出非运行时异常

    异步虽然抛出了,但是抛出的是非RuntimeException类型的异常,依旧不会生效。

    @Transactional
    public void deleteUser() throws MyException{
        userMapper.deleteUserA();
        try {
            int i = 1 / 0;
            userMapper.deleteUserB();
        } catch (Exception e) {
            throw new MyException();
        }
    }

    如果指定了回滚异常类型为Exception,那么就可以回滚非RuntimeException类型异常了。

    @Transactional(rollbackFor = Exception.class)

    3、方法内部直接调用

    如果先调用deleteUser(),那么deleteUserA()是不会回滚的,其原因就是@Transactional根本没生成代理,如果直接调用deleteUser2()那么没问题,deleteUserA()会回滚。

    public void deleteUser() throws MyException{
        deleteUser2();
    }
    
    @Transactional
    public void deleteUser2() throws MyException{
        userMapper.deleteUserA();
        int i = 1 / 0;
        userMapper.deleteUserB();
    }

    修改方式,把当前类自己注入一下调用即可。

    @Service
    public class UserService {
        @Autowired
        private UserMapper userMapper;
        //自己注入自己
        @Autowired
        UserService userService;
    
        public void deleteUser() throws MyException{
            userService.deleteUser2();
        }
    
        @Transactional
        public void deleteUser2() throws MyException{
            userMapper.deleteUserA();
            int i = 1 / 0;
            userMapper.deleteUserB();
        }
    }

    4、新开启一个线程

    如下的方式deleteUserA()也不会回滚,因为spring实现事务的原理是通过ThreadLocal把数据库连接绑定到当前线程中,新开启一个线程获取到的连接就不是同一个了。

    @Transactional
    public void deleteUser() throws MyException{
        userMapper.deleteUserA();
        try {
            //休眠1秒,保证deleteUserA先执行
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            int i = 1/0;
            userMapper.deleteUserB();
        }).start();    
    }

    5、注解到private方法上

    idea直接会给出提示Methods annotated with ‘@Transactional’ must be overridable ,原理很简单,private修饰的方式,spring无法生成动态代理。

    @Transactional
    private void deleteUser() throws MyException{
        userMapper.deleteUserA();
        int i = 1/0;
        userMapper.deleteUserB();
    }

    6、数据库本身不支持

    mysql数据库,必须设置数据库引擎为InnoDB。

    7、事务传播属性设置错误

    注意传播属性的设置,比如设置了:PROPAGATION_NOT_SUPPORIED(以非事务的方式执行,如果当前有事务则把当前事务挂起)。

    8、操作数据库使用了truncate关键字

    整个业务里面只要有 truncate 就会导致事务回滚失效

  • 相关阅读:
    c# datagridview 设置某行不可见解决办法
    MessageBox, MessageBoxBurttons, MessageBoxIcon 详细解析
    c# 项目带皮肤一起打包发布解决办法
    Winform DataGridView CheckBoxColumn c# 单选 解决方案
    机器学习 课程笔记
    机器学习-review-1 线性回归
    Office升级到2013版后无法登录微软账号问题
    Address already in use: make_sock: could not bind to address 0.0.0.0:80
    PHP的数组排序函数
    事件与委托例子
  • 原文地址:https://www.cnblogs.com/guanxiaohe/p/15814392.html
Copyright © 2020-2023  润新知