这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战
写在前面
我们都知道添加@Transactional注解便能实现事物(不考虑分布式事务),但是有些时候,却失效了,明明加着注解却没作用。
今天我们分为两部分: 温故、知新
温故,重新整理下那几种失效场景。知新,我们也能发现一点什么。接着往下看
我们先说 Spring事务的失效原因大概分为六种。
- 数据库引擎不支持事务(我们大部分用Mysql天然支持事务,所以该情况极少见)
- 没有被 Spring 管理
- 方法不是 public 的
- 自身调用问题 (常见)
- 异常被吃了 (常见)
- 异常类型错误
下面我们来一一举例说明:
必须被 Spring 管理
1 | java复制代码 // @Service |
如果此时把 @Service 注解注释掉,这个类就不会被加载成一个 Bean,那这个类就不会被 Spring 管理了,事务自然就失效了。
方法必须是 public 的
以下来自 Spring 官方文档:
When using proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.
大概意思就是 @Transactional 只能用于 public 的方法上,否则事务不会失效,如果要用在非 public 方法上,可以开启 AspectJ 代理模式。
自己不能再调自己
1 | java复制代码@Service |
update方法上面没有加 @Transactional 注解,调用有 @Transactional 注解的 updateOrder 方法,updateOrder 方法上的事务管用吗?
答案是不管用的,因为它们发生了自身调用,就调该类自己的方法,而没有经过 Spring 的代理类,默认只有在外部调用事务才会生效,这也是老生常谈的经典问题了。
解决方案:
1 | java复制代码@Resource |
这样就就交给spring管理了。
不能try catch
1 | java复制代码@Service |
我们经常这样写代码。但是事务会失效,原因是异常被吃掉了。人家事务就是根据你的异常去回滚事物的 结果你给吃掉了。
但是我们可以抛出来。在catch里面 抛出来 就可以解决 但是也不是任何抛异常都可以的。接下来看异常类型错误
异常类型需谨慎
1 | java复制代码@Service |
这样事务也是不生效的,因为默认回滚的是:RuntimeException,如果你想触发其他异常的回滚,需要在注解上配置一下,如:
1 | java复制代码@Transactional(rollbackFor = Exception.class) |
好了 失效场景就讲到这里,我们接下来考虑一件事情,就是我们能不能在try catch情况下也能实现事务,答案是可以的。我们接下来看新的发现
新的解决方案
在上面事务回滚的前提是添加@Transactional注解的方法中不含有try{…}catch{…}捕获异常,使得程序运行过程中出现异常能顺利抛出,从而触发事务回滚。
但是在实际开发中,我们往往需要在方法中进行异常的捕获,从而对异常进行判断,为客户端返回提示信息。但是此时由于异常的被捕获,导致事务的回滚没有被触发,导致事务的失败。
那么这该怎么做呢,下面给出三种办法
1、使用@Transactional注解,抛出@Transactional注解默认识别的RuntimeException
1 | java复制代码@Service |
2、使用@Transactional(rollbackFor = { Exception.class }),也能抛出捕获的非RuntimeException异常
方法上使用@Transactional(rollbackFor = { Exception.class })注解声明事务回滚级别,在捕获到异常时在catch语句中直接抛出所捕获的异常。
1 | java复制代码@Service |
不知道有没有发现上面两个在catch{…}中抛出异常的方法都有个不足之处,就是不能在catch{…}中存在return子句,比如我们需要一些return一些信息,那么就必须设置手动回滚,当捕获到异常时,手动回滚,同时返回前台提示信息。
手动回滚
1 | java复制代码@Service |
OK。今天的讲解就到这里,我们下期再见
弦外之音
感谢你的阅读,如果你感觉学到了东西,您可以点赞,关注。也欢迎有问题我们下面评论交流
加油! 我们下期再见!
给大家分享几个我前面写的几篇骚操作
本文转载自: 掘金