【Spring】Spring事务什么情况下会失效 分手后的思念是犯贱 2023-10-15 11:31 1阅读 0赞 ### 前言: ### 上篇文章中讲到了Spring事务的传播机制,顺便谈了下事务在什么情况下会失效,spring事务失效有多方面的原因,上篇文章讲的失效情况比较浅,所以决定单出一篇文章讲一下。对[事务传播机制][Link 1]感兴趣的朋友可以转过去观看下。 ### 失效原因 ### #### 一:数据库引擎不支持事务。 #### 以mysql为例,不同的数据引擎对事务的支持情况是不同的。 **MyISAM:** 不支持事务 **InnoDB:** 支持事务,如果想使用事务需要更换到innodb引擎(mysql5.5之后版本默认是innodb) #### 二:没通过spring 动态代理来获取支持事务的实现类 #### 导致这个问题的情况有很多种。下面写点示例 ##### 1. 事务方法被final、static、private修饰 ##### **当方法被 final、static、private 修饰时,无论是使用 JDK 动态代理还是 CGLIB 代理,这些方法都不能被 Spring 代理。** 以下来自 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 注解应用于具有公共可见性的方法。如果您确实为受保护、私有或包可见的方法添加了 @Transactional 注解,虽然不会引发错误,但被注解的方法并不会展示配置的事务设置。如果您需要注解非公共方法,请考虑使用 AspectJ(见下文)。 ##### 2. 调用方法方式导致未被spring代理 ##### 调用事务方法时需要注意使用spring代理方式,同类中调用不能直接使用this.method()方式。 **错误示例:** 这种情况下不会执行事务 ![在这里插入图片描述][e2bddde576c64d60aa3d9a78c4c042e2.png] **正确示例:** 同类中调用需要先通过applicationContext.getBean()获取当前类的bean对象,通过bean对象调用事务方法。 ![在这里插入图片描述][7b80fbe9065b42cba88046dea32415fd.png] #### 三:事务传播机制配置不正确 #### ![在这里插入图片描述][a450b088f6fa49d7b225454e5c6afdc4.png] 当事务的传播机制选择的是requires\_new、not\_supported、never时当前事务不生效。 详细spring传播机制可以查看[spring事务的传播机制][spring],这里就不赘述了 #### 四:异常类型未正确处理 #### Spring 事务默认仅在运行时遇到非检查型异常 (unchecked exceptions,如 RuntimeException 及其子类) 时执行回滚。如果配置不当,例如方法抛出检查型异常(如 IOException)而没有在 @Transactional 注解中声明回滚,那么事务不会回滚,导致事务失效。 这种情况可以使用自定义回滚异常方式。 **1. 自定义回滚异常:** ![在这里插入图片描述][948be117640540f894db2ef9c463f64f.png] **2.捕获异常之后再抛出RuntimeException异常** 不推荐,一是有点脱裤子放屁了,二是抛出具体的异常信息容易定位问题。 #### 五:异步方法调用: #### 当在 Spring 中进行异步调用(如使用 @Async 注解)时,可能导致事务失效。这是因为异步方法将在另一个线程中运行, Spring 事务是基于线程绑定的,因此原始线程的事务上下文无法传递到新线程中。 所以可以给异步方法新建一个事务,但是需要注意**此事务是与上层事务分离的**,异步方法内如果进行了回滚,上层事务时不会回滚的。 如果需要解决这个问题,可以使用下述方法: @Service public class UserServiceImpl implements IUserService { @Resource ApplicationContext applicationContext; @Transactional //@Transactional(rollbackFor = Exception.class) 或者直接抛出Exception捕获通用异常 public void saveUser(User order) throws IOException { //业务逻辑 UserServiceImpl bean = applicationContext.getBean(UserServiceImpl.class); try { bean.asyncMethod(); } catch (Exception e) { // 当异步方法抛出异常时,捕获此异常,并将其转抛为 RuntimeException,以触发 parentMethod 的事务回滚 throw new RuntimeException("Async method failed", e); } // saveUser 的其他业务逻辑 } @Async public void asyncMethod() { UserServiceImpl myService = applicationContext.getBean(UserServiceImpl.class); myService.childMethod(); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void childMethod() { // 异步方法执行的逻辑 // ... // 在需要回滚事务的情况下抛出异常 throw new RuntimeException("Async method exception"); } } 为方便理解,这里贴上gpt对这段代码的解析 ![在这里插入图片描述][5f586e52ad2542ec95cee8eda2b017fa.png] 原理:就是通过异步方法回滚时,抛出RuntimeException异常。致使上层事务也触发回滚事件,解决异步方法事务不生效问题。 好了,本片文章至此结束,多谢观看! [Link 1]: https://blog.csdn.net/weixin_45716968/article/details/131513936?spm=1001.2014.3001.5501 [e2bddde576c64d60aa3d9a78c4c042e2.png]: https://img-blog.csdnimg.cn/e2bddde576c64d60aa3d9a78c4c042e2.png [7b80fbe9065b42cba88046dea32415fd.png]: https://img-blog.csdnimg.cn/7b80fbe9065b42cba88046dea32415fd.png [a450b088f6fa49d7b225454e5c6afdc4.png]: https://img-blog.csdnimg.cn/a450b088f6fa49d7b225454e5c6afdc4.png [spring]: https://blog.csdn.net/weixin_45716968/article/details/131513936?spm=1001.2014.3001.5502 [948be117640540f894db2ef9c463f64f.png]: https://img-blog.csdnimg.cn/948be117640540f894db2ef9c463f64f.png [5f586e52ad2542ec95cee8eda2b017fa.png]: https://img-blog.csdnimg.cn/5f586e52ad2542ec95cee8eda2b017fa.png
还没有评论,来说两句吧...