查看原文
其他

@Transactional注解加不加 rollbackFor = Exception.class 的区别?

一行Java 2023-02-06

Java开发中,往往都是通过@Transactional或者@Transactional(rollbackFor = Exception.class)来表示一个事务操作;

所谓事务,就是将单个操作单元中的多个SQL操作作为一个整体,一起进行提交,要么全部成功,要么全部失败;一旦任意一个步骤出现异常,需要将本次操作单元的数据回滚到操作之前的状态;

但两种写法的注解,虽然说只有一个参数的区别,却对最终的事务回滚效果有着特别大的影响,下面就一起来看看:

一、准备

首先我在Mysql中准备了一条数据

二、开始测试

  1. 简单的准备一下sql

    目的是需要把delflag修改为0

    <update id="test">
        UPDATE tbl_users set delflag='0' where account='admin'
    </update>
  2. 我们先来测试一下@Transactional 代码如下 大家都知道2/0必会抛出异常

    @Override
    @Transactional
    public Ret test(){
            int i = articleMapper.test();
            int a = 2/0;
            if(i > 0){
                ResultUtil.success();
            }
        return ResultUtil.error();
    }
  3. 执行测试 i=1说明更新成功 别着急咱们继续断点往下面走

  4. 果然不出所料 执行到第54行的时候报错了 出现了java.lang.ArithmeticException: /by zero

  5. 细心的同学会发现ArithmeticException这个异常类是继承了RuntimeException

    @Transactional默认回滚的的异常就是RuntimeException

  6. 我们在点进去RuntimeException这个类里面一探究竟 我们发现RuntimeException又是继承Exception

    而所有的异常类基本都是继承RuntimeException包括刚才上面的java.lang.ArithmeticException异常

    所以只要是RuntimeExceptionRuntimeException下面的子类抛出的异常 @Transactional都可以回滚的

  7. 这个时候我们去看一下数据库的值到底有没有修改成功 很显然数据是被回滚了 并没有修改成0

三、不回滚的事务

在一些特殊的场景下,会有事务不回滚的情况;下面我们在试试@Transactional不能回滚的异常 代码如下

  1. 我们直接先用try catch来捕获异常 然后在catch里面自定义抛出Exception异常

    @Override
    @Transactional
    public Ret test() throws Exception {
        int i = articleMapper.test();

        try {
            int a = 2 / 0;
        } catch (Exception e) {
            throw new Exception();
        }
        if (i > 0) {
            ResultUtil.success();
        }
        return ResultUtil.error();
    }

    执行之后,可以看到正常抛出了java.lang.Exception异常 ,由于默认情况下,@Transactional只会回滚RuntimeException异常,所以这里的回滚将会失效;

    去看看数据库,发现数据已经成功更新;确实并没有发生回滚;

  2. 默认访问符修饰的方法

    @Transactional
    void update() {
        int i = articleMapper.update();

        if(i<=0){
            throw new RuntimeException("err");
        }
    }

    这里的方法是使用的默认访问修饰符,Spring在扫描@Transactional注解信息的过程,如果方法不是public修饰,将不会对Bean进行代理对象的创建以及代理方法的调用;

  3. 内部方法的调用

    @Transactional
    public void update() {
        int i = articleMapper.update();

        if(i<=0){
            throw new RuntimeException("err");
        }
    }

    public void test(){
        update();
    }

    以上代码,当直接调用test(),事务将不会生效,原因也比较容易理解,Spring的事务,是通过代理对象来调用并提交/回滚,直接通过内部的方法调用并没有走代理方法调用,最终导致事务失效;

四、总结一下

  • @Transactional只能回滚RuntimeExceptionRuntimeException下面的子类抛出的异常 不能回滚Exception异常

  • 如果需要支持回滚Exception异常请用@Transactional(rollbackFor = Exception.class)

    这里如果是增删改的时候我建议大家都使用@Transactional(rollbackFor = Exception.class)

  • 特殊场景下,事务回失效,在开发过程中,需要特别注意。

参考:blog.csdn.net/weixin_42169734/

article/details/117122084

👉最新2T+免费Java视频学习资料点击领取>>

END

精品资料,超赞福利,免费领


微信扫码/长按识别 添加【技术交流群
群内每天分享精品学习资料


最近开发整理了一个用于速刷面试题的小程序;其中收录了上千道常见面试题及答案(包含基础、并发、JVM、MySQL、Redis、Spring、SpringMVC、SpringBoot、SpringCloud、消息队列等多个类型),欢迎您的使用。


超详细 45 个 Git 经典命令操作场景,还有谁不会?
7个提升 Spring Boot 吞吐量的神技,让你的代码飞起来!
Redis 只会用缓存?20种妙用让同事直呼牛X
阿里禁用 boolean 类型变量用 isXxx 命名,why?
【原创】怒肝3W字Java学习路线!从入门到封神全包了(建议收藏)
程序员专属导航站(baoboxs.com),一站式工作、学习、娱乐!

👇👇

👇点击"阅读原文",获取更多资料(持续更新中)

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存