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


前言

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


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

一、@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 方法中出异常不会回滚,如下代码

@Service
public 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 方法(推荐使用)

// 当前类
@Service
public class CabinetServiceImpl implements CabinetService {
    @Autowired
    private MethodTest methodTest;
    @Override
    public void saveCabinet(Cabinet cabinet) {
        methodTest.save(cabinet);
    }
}
// 另一个类
@Service
public 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()

@Service
public 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 了,不会回滚,如下代码

@Service
public 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);,异常交给注解处理

@Service
public 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 异常则不会回滚

@Service
public 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) 即可,也可以指定自定义异常

@Service
public 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.数据库引擎不支持事务

  • 数据库不支持事务

总结

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

在这里插入图片描述

  • 32
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
@Transactional注解失效的情况有以下几种: 1. 方法没有被Spring代理 - 由于Spring的事务管理是基于AOP实现的,所以只有被Spring代理的方法才会被@Transactional注解所识别。如果方法没有被代理,那么@Transactional注解将无效。 2. 异常被catch住 - 如果在带有@Transactional注解的方法中,捕获了异常并且没有再次抛出,那么事务将不会回滚。事务只有在未被捕获的异常发生时才会回滚。 3. 事务方法内部调用 - 如果在同一个类中的方法内部调用带有@Transactional注解的方法,那么事务注解将无效。这是因为Spring使用基于代理的事务管理,而同一个类中的方法调用并不会通过代理。 4. 异常不属于rollbackFor指定的类型 - @Transactional注解的rollbackFor参数可以指定回滚的异常类型。如果抛出的异常不属于指定的类型,事务将不会回滚。 5. 事务方法是私有的、final的或静态的 - @Transactional注解只能应用于public方法。私有的、final的或静态的方法无法被事务管理。 6. 异常被try-catch重新抛出并修改了异常类型 - 如果在带有@Transactional注解的方法中,捕获了异常并在重新抛出时修改了异常类型,那么事务将不会回滚。事务回滚是基于异常类型的匹配,修改异常类型会导致匹配失败[3]。 7. 方法内部调用self method - 如果在同一个类中的方法内部调用了自身的方法,事务注解将无效。这是因为同一个类中的方法调用不会通过代理,所以事务注解不会生效。 以上是一些常见的@Transactional注解失效的情况,需要注意这些情况以确保事务能够正确地生效。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [@Transactional注解失效的情况](https://blog.csdn.net/qq_40834643/article/details/128535431)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值