小工具知识网

当前位置: 首页 > 行业知识 > 【spring】@Transactional 注解失效的原因及解决办法

行业知识

【spring】@Transactional 注解失效的原因及解决办法

2026-11-04 04:48:26 来源:互联网转载

文章目录

  • 前言
  • 一、@Transactional 属性介绍
    • 1.事务的传播行为:propagation
    • 2.事务的隔离级别:isolation
    • 3.事务的超时时间:timeout
    • 4.事务的回滚类型:rollbackFor
    • 二、@Transactional 失效场景
      • 1.同一个类中方法调用,注解失效
      • 2.异常被 catch “吃了”,注解失效
      • 3.@Transactional 应用在非 public 修饰的方法上
      • 4.@Transactional 注解属性 rollbackFor 设置错误
      • 5.@Transactional 注解属性 propagation 设置错误
      • 6.数据库引擎不支持事务
      • 总结

        前言

        面试中经常会被问到事务失效的场景有哪些,其实在开发中,若是不了解事务失效的场景,当你觉得加了事务,就会回滚,就大错特错了,今天就来了解一下吧。


        提示:以下是本篇文章正文内容,下面案例可供参考

        一、@Transactional 属性介绍

        1.事务的传播行为:propagation

        这就是我们常说的事务的七种传播行为,默认值:Propagation.REQUIRED

        属性解释
        Propagation.REQUIRED如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。
        Propagation.SUPPORTS如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
        Propagation.MANDATORY如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
        Propagation.REQUIRES_NEW重新创建一个新的事务,如果当前存在事务,暂停当前的事务。
        Propagation.NOT_SUPPORTED以非事务的方式运行,如果当前存在事务,暂停当前的事务。
        Propagation.NEVER以非事务的方式运行,如果当前存在事务,则抛出异常。
        Propagation.NESTED嵌套事务。

        2.事务的隔离级别:isolation

        这就是我们常说的事务的隔离级别,默认值:Isolation.DEFAULT

        属性解释
        Isolation.DEFAULT使用底层数据库默认的隔离级别
        Isolation.READ_UNCOMMITTED读未提交
        Isolation.READ_COMMITTED读已提交
        Isolation.REPEATABLE_READ可重复读
        Isolation.SERIALIZABLE串行化

        3.事务的超时时间:timeout

        • timeout :事务的超时时间,默认值为 -1。如果超过该时间限制但事务还没有完成,则自动回滚事务。

          4.事务的回滚类型:rollbackFor

          • rollbackFor :用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。

            二、@Transactional 失效场景

            1.同一个类中方法调用,注解失效

            场景重现:当调用 saveCabinet 方法时,save 方法中出异常不会回滚,如下代码

            @Servicepublic class CabinetServiceImpl implements CabinetService {    @Autowired    private CabinetMapper cabinetMapper;    @Override    public void saveCabinet(Cabinet cabinet) {        save(cabinet);    }    @Transactional(rollbackFor = Exception.class)    public void save(Cabinet cabinet) {        cabinetMapper.insert(cabinet);        int i = 1/0;// 模拟出错    }}

            原因分析:spring 中采用动态代理来实现事务,当同一个类调用 save 时,不是代理类在调用,所以扫描不到事务注解

            解决方案1:将 save 方法提到另一个类中,saveCabinet 调用另一个类的 save 方法(推荐使用)

            // 当前类@Servicepublic class CabinetServiceImpl implements CabinetService {    @Autowired    private MethodTest methodTest;    @Override    public void saveCabinet(Cabinet cabinet) {        methodTest.save(cabinet);    }}// 另一个类@Servicepublic class MethodTest {    @Autowired    private CabinetMapper cabinetMapper;        @Transactional(rollbackFor = Exception.class)    public void save(Cabinet cabinet) {        cabinetMapper.insert(cabinet);        int i = 1/0; // 模拟出错    }}

            解决方案2:在 CabinetServiceImpl 中自己注入自己,用自己的代理类调用 save 方法,即 cabinetServiceImpl.save()

            @Servicepublic class CabinetServiceImpl implements CabinetService {    @Autowired    private CabinetMapper cabinetMapper;        @Autowired    private CabinetServiceImpl cabinetServiceImpl;        @Override    public void saveCabinet(Cabinet cabinet) {        cabinetServiceImpl.save(cabinet);    }        @Transactional(rollbackFor = Exception.class)    public void save(Cabinet cabinet) {        cabinetMapper.insert(cabinet);        int i = 1/0;// 模拟出错    }}

            2.异常被 catch “吃了”,注解失效

            场景重现:当调用 saveCabinet 方法时,方法体被 try-catch 了,不会回滚,如下代码

            @Servicepublic class CabinetServiceImpl implements CabinetService {    @Autowired    private CabinetMapper cabinetMapper;        @Override    @Transactional(rollbackFor = Exception.class)    public void saveCabinet(Cabinet cabinet) {        try {            cabinetMapper.insert(cabinet);            int i = 1/0;// 模拟出错        } catch (Exception e) {            e.printStackTrace();        }    }}

            原因分析:异常直接捕获没有向上抛出,而是自己处理了,注解捕获不到异常,所以失效

            解决方案:将 catch 到的异常向上抛出 throw new Exception(e);,异常交给注解处理

            @Servicepublic class CabinetServiceImpl implements CabinetService {    @Autowired    private CabinetMapper cabinetMapper;        @Override    @Transactional(rollbackFor = Exception.class)    public void saveCabinet(Cabinet cabinet) throws Exception {        try {            cabinetMapper.insert(cabinet);            int i = 1/0;// 模拟出错        } catch (Exception e) {            e.printStackTrace();            throw new Exception(e);        }    }}

            3.@Transactional 应用在非 public 修饰的方法上

            • 场景重现:当调用的方法不是 public 修饰时,不会回滚,事务失效
            • 原因分析:在 spring aop 代理时,事务拦截器会在目标方法的执行前后进行拦截,其中的方法会去获取 Transactional 注解的一些信息,而其中的方法会去检查目标方法是否被 public 修饰,不是的话获取不到注解信息
            • 解决方案:将调用的方法访问修饰符改为 public

              4.@Transactional 注解属性 rollbackFor 设置错误

              场景重现:当注解没有指定 rollbackFor 属性时,如下若抛出 FileNotFoundException 异常则不会回滚

              @Servicepublic class CabinetServiceImpl implements CabinetService {    @Autowired    private CabinetMapper cabinetMapper;        @Override    @Transactional    public void saveCabinet(Cabinet cabinet) throws IOException {        cabinetMapper.insert(cabinet);        // 模拟出错        throw new FileNotFoundException();    }}

              原因分析:spring 默认抛出未检查异常,继承 RuntimeException 或者 Error 才会回滚,其他异常如 FileNotFoundException 则不会回滚

              解决方案:指定 rollbackFor 属性,一般指定 @Transactional(rollbackFor = Exception.class) 即可,也可以指定自定义异常

              @Servicepublic class CabinetServiceImpl implements CabinetService {    @Autowired    private CabinetMapper cabinetMapper;        @Override    @Transactional(rollbackFor = Exception.class)    public void saveCabinet(Cabinet cabinet) throws IOException {        cabinetMapper.insert(cabinet);        // 模拟出错        throw new FileNotFoundException();    }}

              5.@Transactional 注解属性 propagation 设置错误

              • 使用了不支持事务的传播属性

                6.数据库引擎不支持事务

                • 数据库不支持事务

                  总结

                  一生太短了,我想为每一天发生的开心事情而庆祝。即使没有,我也要为“今天没有发生糟糕的事情”而庆祝。即使发生了,我也要为“又熬过人生中一件糟糕的事情”而庆祝。

上一篇:揭秘PHP下载站源码,一站式解决方案助你轻松搭建专业下载站!

下一篇:foxmail怎么要服务器?