场景1:抛出受检查异常
失效原因:Spring默认只会回滚非检查异常和error错误。
解决办法:在@Transactional注解中添加rollbackFor = 受检查异常.class
。
非检查异常:RuntimeException及其子类。
❗注意事项❗
- 如果抛出的异常是运行时异常或错误,且rollbackFor未明确指定:事务会回滚,因为运行时异常和错误是默认会导致事务回滚的。
- 如果抛出的异常是检查型异常,且rollbackFor未包含该异常类型:事务不会回滚,因为检查型异常默认不会导致事务回滚。
- 如果rollbackFor指定了某些异常类型:
- 如果抛出的异常是
rollbackFor
指定的类型之一,那么事务会回滚。 - 如果抛出的异常不是
rollbackFor
指定的类型,且是运行时异常或错误,事务仍然会回滚(因为它们是默认回滚的)。 - 如果抛出的异常不是
rollbackFor
指定的类型,且是检查型异常,事务不会回滚。
- 如果抛出的异常是
Java异常体系结构图:
场景2:类没有被Spring管理
失效原因:没有使用注解将类放进Spring容器中,即使方法上加了@Transactional注解,事务也是不会生效的。
解决方法:在当前类上添加注解,让其被Spring容器管理。
场景3:事务方法不是public修饰
失效原因:@Transactional注解只能用于public方法上。
解决方法:将访问修饰符改成public。
场景4:事务方法被final修饰
失效原因:因为Spring事务是用动态代理实现的,如果方法被final修饰,则代理类无法对目标方法进行重写,无法植入事务功能。
解决方法:方法不要用final修饰。
场景5:事务方法被static修饰
失效原因:原因与场景4一样。静态方法属于类级别的,编译过程中已确定,不支持运行期动态植入。
解决方法:方法不要用static修饰。
场景6:方法内部调用事务方法
失效原因:this对象不是代理类,而是当前xxxService类本身,因为未被事务管理,所有会事务失效。
解决方法:
方法一:将当前service通过@Autowired注入到本类中。
方法二:通过AopContext.currentProxy()
获取代理对象,进行方法调用。
场景7:数据库引擎不支持事务
失效原因:使用了不支持事务的数据库存储引擎,例如MySQL中的MyISAM。
解决方法:使用支持数据库事务的存储引擎,例如InnoDB。
场景8:异常未抛出
失效原因:Spring事务只有捕捉到了业务抛出去的异常才能进行进行后续的处理。如果业务自己捕获了异常不抛出,则事务无法感知。
解决方法:不要捕获异常,或者catch中将异常抛出。
场景9:未配置事务管理器
失效原因:项目中没有配置Spring的事务管理器,即使使用了Spring的事务管理功能,Spring的事务也不会生效。
解决方法:配置事务管理器。在SpringBoot中,事务管理器默认开启。
场景10:多线程调用
失效原因:因为Spring的事务是通过数据库连接来实现的,而数据库连接Spring是放在ThreadLocal中。同一个事务只能用同一个数据库连接。而多线程场景下,拿到的数据库连接是不一样的,即属于不同的事务。
解决方法:事务方法里面不用多线程。