当前位置: 首页 > 后端技术 > Java

面试惊喜86:SpringBoot事务不回滚?如何处理?

时间:2023-04-01 13:51:23 Java

在SpringBoot中,有很多场景会导致事务不自动回滚,比如:非public修饰方法中的事务不自动回滚;当@Transactional遇到try/catch事务时不会自动回滚;在类内部调用@Transactional方法不会自动回滚事务;抛出检查异常时,事务不会自动回滚;数据库不支持事务,事务不会自动回滚。那么针对以上场景,我们应该如何解决呢?接下来,让我们一一了解。1、非public方法解决方案非public方法中事务不回滚的直接原因是非public方法中添加的@Transactional关键字无效,即方法本身运行在非public方法中事务方式,所以肯定不会自动回滚事务。因为@Transactional是SpringAOP实现的,而SpringAOP是通过动态代理实现的,而@Transactional在生成代理的时候会判断,如果方法是非public修饰的方法,则不会生成代理对象,所以不会有该方法自动回滚事务,部分实现源码如下://非公共方法,设置为nullif(allowPublicMethodsOnly()&&!Modifier.isPublic(method.getModifiers())){returnnull;}//下面代码省略....}解决这个问题的办法是把方法的权限修饰符改成public。2.try/catch解决方案当程序中出现try/catch代码时,事务不会自动回滚。这是因为@Transactional注解在实现的时候,需要感知到异常才会自动回滚,而用户在代码中自动回滚,@Transactional加上try/catch后,就无法感知异常,所以不会自动回滚事务。这个问题有两种解决方法:一种是在catch中重新抛出异常,另一种是使用代码手动回滚事务。方案一:重新抛出异常方案二:使用代码手动回滚事务除了方案一不是很友好的回滚事务方式外,我们还可以选择更友好的不报错的方式,但是可以回滚事务的方式,其核心实现代码如下:3.调用内部@Transactional方法的解决方法调用类内部@Transactional方法不会自动回滚事务的原因是@Transactional是基于SpringAOP实现的,而SpringAOP是基于动态代理实现的,调用类内部的方法时,不是通过代理对象,而是通过this对象,这样代理对象就是绕过,交易无效。这时候我们的解决方法是在调用方法中加上@Transactional。具体实现代码如下:4.检查异常的事务解决方案所谓检查异常(CheckedException)是指编译器要求开发人员处理的事情。异常,如下图:检查异常不回滚事务是因为@Transactional默认只回滚运行时异常RuntimeException和Error,而检查异常默认不回滚。这个问题的解决方法是注解@Transactional,加上rollbackFor参数,设置Exception.class的值。具体实现代码如下:5、数据库不支持事务的解决方法当我们在程序中添加@Transactional时,相当于发送了启动事务、提交事务、回滚事务的命令到调用的数据库。但是,如果数据库本身不支持事务,比如MySQL中设置了MyISAM引擎,因为它本身不支持事务。在这种情况下,即使在程序中加上了@Transactional注解,仍然不会有事务行为,也不会进行事务的自动回滚。这种情况下,我们只需要将MySQL引擎设置为InnoDB即可解决问题,因为InnoDB是支持事务的。当然,MySQL5.1之后默认的引擎是InnoDB。引擎设置分为以下两种情况:修改表时设置数据库引擎:修改表时设置数据库引擎:PS:也就是数据库的引擎是直接和表相关的,我们只需要正确设置引擎,交易可以正常执行。小结在本文中,我们介绍了5种事务不自动回滚的场景以及对应的解决方案。开发者应根据自己的实际情况选择合适的方案。判断是非在自己,名誉在别人,得失在人数。公众号:Java面试真题分析面试合集:https://gitee.com/mydb/interview