SpringBoot事务不回滚的解决方案

 更新时间:2022年09月26日 08:38:48   作者:Java中文社群  
这篇文章主要介绍了SpringBoot事务不回滚的解决方案的相关资料,需要的朋友可以参考下

在 Spring Boot 中,造成事务不自动回滚的场景有很多,比如以下这些:

  • 非 public 修饰的方法中的事务不自动回滚;
  • 当 @Transactional 遇上 try/catch 事务不自动回滚;
  • 调用类内部的 @Transactional 方法事务不自动回滚;
  • 抛出检查异常时事务不自动回滚;
  • 数据库不支持事务,事务也不会自动回滚。

那么对于上面的这些场景,我们应该如何解决呢?接下来我们一一来看。

1.非 public 方法解决方案

非 public 方法中事务不回滚的直接原因是,在非 public 方法上添加的 @Transactional 关键字是无效的,也就是此方法本身是以非事务的方式运行的,所以它当然不会自动回滚事务了。

因为 @Transactional 使用的是 Spring AOP 实现的,而 Spring AOP 是通过动态代理实现的,而 @Transactional 在生成代理时会判断,如果方法为非 public 修饰的方法,则不生成代理对象,这样也就没办法自动回滚事务了,它的部分实现源码如下:

protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
   // Don't allow no-public methods as required.
   // 非 public 方法,设置为 null
   if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
      return null;
   }
   // 后面代码省略....
 }

此问题的解决方案是将方法的权限修饰符改为 public 即可。

2.try/catch 解决方案

当程序中出现了 try/catch 代码时,事务不会自动回滚,这是因为 @Transactional 注解在其实现时,需要感知到异常才会自动回滚,而用户自行在代码中加入了 try/catch 之后,@Transactional 就无法感知到异常了,那么也就不能自动回滚事务了。

此问题的解决方案有两种:一种是在 catch 中将异常重新抛出去,另一种是使用代码手动将事务回滚。

解决方案1:将异常重新抛出

解决方案2:使用代码手动回滚事务

除了解决方案 1 这种不是很友好的回滚事务的方式之外,我们还可以选择更加友好的,不报错,但可以回滚事务的方式,其核心实现代码如下:

3.调用内部 @Transactional 方法解决方案

调用类内部 @Transactional 的方法不自动回滚事务的原因是,@Transactional 是基于 Spring AOP 实现的,而 Spring AOP 又是基于动态代理实现的,而当调用类内部的方法时,不是通过代理对象完成的,而是通过 this 对象实现的,这样就绕过了代理对象,从而事务就失效了。

此时我们的解决方案是给调用的方法上也加上 @Transactional,具体实现代码如下:

4.检查异常的事务解决方案

所谓的检查异常(Checked Excetion)指的是编译器要求开发者必须处理的异常,如下图所示:

检查异常不回滚事务的原因是因为,@Transactional 默认只回滚运行时异常 RuntimeException 和 Error,而对于检查异常默认是不回滚的。

此问题的解决方案是给 @Transactional 注解上,添加 rollbackFor 参数并设置 Exception.class 值即可,具体实现代码如下:

5.数据库不支持事务的解决方案

当我们在程序中添加了 @Transactional,相当于给调用的数据库发送了:开始事务、提交事务、回滚事务的指令,但是如果数据库本身不支持事务,比如 MySQL 中设置了使用 MyISAM 引擎,因为它本身是不支持事务的,这种情况下,即使在程序中添加了 @Transactional 注解,那么依然不会有事务的行为,也就不会执行事务的自动回滚了。

在这种情况下,我们只需要设置 MySQL 的引擎为 InnoDB 就可以解决问题了,因为 InnoDB 是支持事务的,当然 MySQL 5.1 之后的默认引擎就是 InnoDB,引擎的设置分为以下两种情况:

在新建表时设置数据库引擎:

在修改表时设置数据库引擎

PS:也就是数据库的引擎是和表直接相关的,我们只需要正确的设置引擎之后,事务就可以正常的执行了。

总结

本文我们介绍了 5 种事务不自动回滚的场景和相应的解决方案,开发者应该根据自己的实际情况,选择合适自己解决方案进行处理。

到此这篇关于SpringBoot事务不回滚的解决方案的文章就介绍到这了,更多相关SpringBoot事务不回滚内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java中compareTo方法使用小结

    Java中compareTo方法使用小结

    compareTo是Java中Object类中的一个方法,它的作用是比较两个对象的大小关系,本文主要介绍了Java中compareTo方法使用小结,感兴趣的可以了解一下
    2024-01-01
  • Java实现简易的洗牌和发牌功能

    Java实现简易的洗牌和发牌功能

    本文主要介绍了Java实现简易的洗牌和发牌功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • Java探索之Thread+IO文件的加密解密代码实例

    Java探索之Thread+IO文件的加密解密代码实例

    这篇文章主要介绍了Java探索之Thread+IO文件的加密解密代码实例,具有一定参考价值,需要的朋友可以了解下。
    2017-10-10
  • import java和javax区别小结

    import java和javax区别小结

    Java包和javax包在Java编程语言中都起着至关重要的作用,本文就来介绍一下import java和javax区别小结,具有一定的参考价值,感兴趣的可以了解一下
    2024-10-10
  • java调用7zip解压压缩包的实例

    java调用7zip解压压缩包的实例

    下面小编就为大家带来一篇java调用7zip解压压缩包的实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • java的jps命令使用详解

    java的jps命令使用详解

    这篇文章介绍了java的jps命令使用详解,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-12-12
  • 查看本地启动SpringBoot的本地端口号的几种方式

    查看本地启动SpringBoot的本地端口号的几种方式

    这篇文章主要介绍了查看本地启动SpringBoot的本地端口号的几种方式,文中通过代码示例讲解的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-10-10
  • Java TCP网络通信协议详细讲解

    Java TCP网络通信协议详细讲解

    TCP/IP是一种面向连接的、可靠的、基于字节流的传输层通信协议,它会保证数据不丢包、不乱序。TCP全名是Transmission Control Protocol,它是位于网络OSI模型中的第四层
    2022-09-09
  • java ArrayList.remove()的三种错误用法以及六种正确用法详解

    java ArrayList.remove()的三种错误用法以及六种正确用法详解

    这篇文章主要介绍了java ArrayList.remove()的三种错误用法以及六种正确用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-01-01
  • SpringBoot整合RabbitMQ处理死信队列和延迟队列

    SpringBoot整合RabbitMQ处理死信队列和延迟队列

    这篇文章将通过示例为大家详细介绍SpringBoot整合RabbitMQ时如何处理死信队列和延迟队列,文中的示例代码讲解详细,需要的可以参考一下
    2022-05-05

最新评论