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.数据库引擎不支持事务
- 数据库不支持事务
总结
到此这篇关于spring中@Transactional 注解失效的原因及解决办法的文章就介绍到这了,更多相关spring @Transactional 失效内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!