面试突击:SpringBoot 事务不回滚?怎么解决?

网站建设4年前发布
17 0 0

20230306012207b4451d614a4a4041819791d1655ffcbe8da8da116,在 Spring Boot 中,造成事务不自动回滚的场景有很多,比如以下这些:,那么对于上面的这些场景,我们应该如何解决呢?接下来我们一一来看。,​非 public 方法中事务不回滚的直接原因是,在非 public 方法上添加的 @Transactional 关键字是无效的,也就是此方法本身是以非事务的方式运行的,所以它当然不会自动回滚事务了。,因为 @Transactional 使用的是 Spring AOP 实现的,而 Spring AOP 是通过动态代理实现的,而 @Transactional 在生成代理时会判断,如果方法为非 public 修饰的方法,则不生成代理对象,这样也就没办法自动回滚事务了,它的部分实现源码如下:,此问题的解决方案是将方法的权限修饰符改为 public 即可。,当程序中出现了 try/catch 代码时,事务不会自动回滚,这是因为@Transactional 注解在其实现时,需要感知到异常才会自动回滚,而用户自行在代码中加入了 try/catch 之后,@Transactional 就无法感知到异常了,那么也就不能自动回滚事务了。,此问题的解决方案有两种:一种是在 catch 中将异常重新抛出去,另一种是使用代码手动将事务回滚。​,202303060122073267f7270e6bc574a5a544a1f4b8727b53f624213,除了解决方案 1 这种不是很友好的回滚事务的方式之外,我们还可以选择更加友好的,不报错,但可以回滚事务的方式,其核心实现代码如下:,20230306012243e451878214a9a500bba699654ecaabb2eb635f587,调用类内部 @Transactional 的方法不自动回滚事务的原因是,@Transactional 是基于 Spring AOP 实现的,而 Spring AOP 又是基于动态代理实现的,而当调用类内部的方法时,不是通过代理对象完成的,而是通过 this 对象实现的,这样就绕过了代理对象,从而事务就失效了。,此时我们的解决方案是给调用的方法上也加上 @Transactional,具体实现代码如下:,2023030601220856226a345526101821c36683d9812014dec0ec994,所谓的检查异常(Checked Excetion)指的是编译器要求开发者必须处理的异常,如下图所示:,2023030601220944135a6689a096501159983edf896cef9fd796260,​检查异常不回滚事务的原因是因为,@Transactional 默认只回滚运行时异常 RuntimeException 和 Error,而对于检查异常默认是不回滚的。,此问题的解决方案是给 @Transactional 注解上,添加 rollbackFor 参数并设置 Exception.class 值即可,具体实现代码如下:,20230306012210c911e6680e597fbe1e52035eb37c3d837f614a978,​当我们在程序中添加了 @Transactional,相当于给调用的数据库发送了:开始事务、提交事务、回滚事务的指令,但是如果数据库本身不支持事务,比如 MySQL 中设置了使用 MyISAM 引擎,因为它本身是不支持事务的,这种情况下,即使在程序中添加了 @Transactional 注解,那么依然不会有事务的行为,也就不会执行事务的自动回滚了。,在这种情况下,我们只需要设置 MySQL 的引擎为 InnoDB 就可以解决问题了,因为 InnoDB 是支持事务的,当然 MySQL 5.1 之后的默认引擎就是 InnoDB,引擎的设置分为以下两种情况:,在新建表时设置数据库引擎:,20230306012210d7c0d2b21750d58f725398308e5edaa4053524957,在修改表时设置数据库引擎:,20230306012243a175f3e45dcb49188e7190c8123f01d03ec38a589,PS:也就是数据库的引擎是和表直接相关的,我们只需要正确的设置引擎之后,事务就可以正常的执行了。,本文我们介绍了 5 种事务不自动回滚的场景和相应的解决方案,开发者应该根据自己的实际情况,选择合适自己解决方案进行处理。

© 版权声明

相关文章