spring事务@Transactional失效原因及解决办法小结

 更新时间:2023年08月20日 10:47:23   作者:程序猿(攻城狮)  
今天就跟大家聊聊有关spring中@Transactional失效原因及解决办法小结,主要从三个方面考虑,具有一定的参考价值,感兴趣的可以了解一下

spring事务@Transactional失效情况分析主要从以下几个方面考虑:

1. mysql数据库

默认情况下mysql数据库使用的是Innodb存储引擎(5.5版本之后),它是支持事务的,但是如果你的表的存储引擎是MyISAM,MyISAM是不支持事务的。这样就会出现“事务失效”的问题了。

解决方案:修改存储引擎为Innodb

2. 业务代码

2.1 执行事务的Bean交由Spring管理

我们要使用Spring的申明式事务,那么需要执行事务的Bean是否已经交由了Spring管理,在代码中的体现就是类上是否有@Service、Component等一系列注解。

解决方案:将Bean交由Spring进行管理(添加@Service注解)

2.2 非public的方法进行事务管理

默认情况下你无法使用@Transactional对一个非public的方法进行事务管理

解决方案:修改需要事务管理的方法为public

2.3 出现了自调用

多个方法都在同一个类中,其中第一个方法中调用了本类中的第二个和第三个方法,这就是自调用。

那么自调用为什么会导致事务失效呢?我们知道Spring中事务的实现是依赖于AOP的,当容器在创建Service这个Bean时,发现这个类中存在了被@Transactional标注的方法(修饰符为public)那么就需要为这个类创建一个代理对象并放入到容器中。由于方法实际上是由Service也就是目标类自己调用的,所以在方法的前后并不会执行事务的相关操作。这也是自调用带来问题的根本原因:自调用时,调用的是目标类中的方法而不是代理类中的方法。

解决方案

  • 自己注入自己,然后显示的调用,
  • 这种方案看起来不是很优雅
  • 利用AopContext,如下:
@Service
public class DemoService {
	@Transactional
	public void save(A a, B b) {
		((DemoService) AopContext.currentProxy()).saveB(b);
	}
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void saveB(B b){
		dao.saveB(a);
	}
}

使用上面这种解决方案需要注意的是,需要在配置类上新增一个配置

// exposeProxy=true代表将代理类放入到线程上下文中,默认是false
@EnableAspectJAutoProxy(exposeProxy = true)

3. 事务回滚相关问题

3.1 该回滚的时候事务提交了

这种情况往往是程序员对Spring中事务的rollbackFor属性不够了解导致的。

Spring默认抛出了未检查unchecked异常(继承自 RuntimeException 的异常)或者 Error才回滚事务;其他异常不会触发回滚事务,已经执行的SQL会提交掉。如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定rollbackFor属性。

默认情况下,只有出现RuntimeException或者Error才会回滚

public boolean rollbackOn(Throwable ex) {
    return (ex instanceof RuntimeException || ex instanceof Error);
}

所以,如果你想在出现了非RuntimeException或者Error时也回滚,请指定回滚时的异常,例如:

@Transactional(rollbackFor = Exception.class)

3.2 该提交的事务被回滚

对应的异常信息如下:

Transaction rolled back because it has been marked as rollback-only

总结起来,主要的原因就是因为内部事务回滚时将整个大事务做了一个rollbackOnly的标记,所以即使我们在外部事务中catch了抛出的异常,整个事务仍然无法正常提交,并且如果你希望正常提交,Spring还会抛出一个异常。

3.3 解决方案

这个解决方案要依赖业务而定,你要明确你想要的结果是什么

内部事务发生异常,外部事务catch异常后,内部事务自行回滚,不影响外部事务
将内部事务的传播级别设置为nested/requires_new均可。在我们的例子中就是做如下修改:

// @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
public void a() throws ClassNotFoundException{
    // ......
    throw new ClassNotFoundException();
}

虽然这两者都能得到上面的结果,但是它们之间还是有不同的。当传播级别为requires_new时,两个事务完全没有联系,各自都有自己的事务管理机制(开启事务、关闭事务、回滚事务)。但是传播级别为nested时,实际上只存在一个事务,只是在调用a方法时设置了一个保存点,当a方法回滚时,实际上是回滚到保存点上,并且当外部事务提交时,内部事务才会提交,外部事务如果回滚,内部事务会跟着回滚。

 内部事务发生异常时,外部事务catch异常后,内外两个事务都回滚,但是方法不抛出异常

4. 读写分离跟事务结合使用时的问题

读写分离一般有两种实现方式

  • 配置多数据源
  • 依赖中间件

如果是配置了多数据源的方式实现了读写分离,那么需要注意的是:如果开启了一个读写事务,那么必须使用写节点,如果是一个只读事务,那么可以使用读节点。

如果是依赖于中间件那么需要注意:需要根据中间件的事务规范使用事务。

到此这篇关于spring事务@Transactional失效原因及解决办法小结的文章就介绍到这了,更多相关spring @Transactional失效内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Eclipse+Java+Swing实现图书管理系统(详细代码)

    Eclipse+Java+Swing实现图书管理系统(详细代码)

    这篇文章主要介绍了Eclipse+Java+Swing实现图书管理系统并附上详细代码,需要的小伙伴可以参考一下,希望对你有所帮助
    2022-01-01
  • Java 通过 二三法 巧解前端数据显示

    Java 通过 二三法 巧解前端数据显示

    实践来源于理论,做开发前肯定要先了解相关的规则和原理,看到标题或许你会好奇什么是二三法。本篇文章带你深入了解,需要的朋友可以参考下
    2021-10-10
  • JAVA中使用双括号来初始化静态常量的小技巧

    JAVA中使用双括号来初始化静态常量的小技巧

    这篇文章主要介绍了JAVA中使用双括号来初始化静态常量的小技巧,需要的朋友可以参考下
    2014-06-06
  • Java Spring开发环境搭建及简单入门示例教程

    Java Spring开发环境搭建及简单入门示例教程

    这篇文章主要介绍了Java Spring开发环境搭建及简单入门示例,结合实例形式分析了spring环境搭建、配置、使用方法及相关注意事项,需要的朋友可以参考下
    2017-11-11
  • 阿里dubbo出错提示Thread pool is EXHAUSTED问题及解决方法

    阿里dubbo出错提示Thread pool is EXHAUSTED问题及解决方法

    这篇文章主要介绍了阿里dubbo出错提示Thread pool is EXHAUSTED的问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08
  • Java实现入参数据批量数据校验详解

    Java实现入参数据批量数据校验详解

    在业务处理中一般入参是单条数据,这样数据校验比较容易,但是这种方法对于集合数据的校验不适用,下面我们就来看看如何对入参数据进行批量数据校验吧
    2024-02-02
  • 一文搞懂并学会使用SpringBoot的Actuator运行状态监控组件的详细教程

    一文搞懂并学会使用SpringBoot的Actuator运行状态监控组件的详细教程

    这篇文章主要介绍了一文搞懂并学会使用SpringBoot的Actuator运行状态监控组件,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • Java并发之嵌套管程锁死详解

    Java并发之嵌套管程锁死详解

    这篇文章主要介绍了Java并发之嵌套管程锁死详解,涉及嵌套管程锁死的发生,实例等相关内容,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • Java使用poi导出ppt文件的实现代码

    Java使用poi导出ppt文件的实现代码

    Apache POI 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java对Microsoft Office格式档案读和写的功能。本文给大家介绍Java使用poi导出ppt文件的实现代码,需要的朋友参考下吧
    2021-06-06
  • Quartz中的Job与JobDetail解析

    Quartz中的Job与JobDetail解析

    这篇文章主要介绍了Quartz中的Job与JobDetail解析,你定义了一个实现Job接口的类,这个类仅仅表明该job需要完成什么类型的任务,除此之外,Quartz还需要知道该Job实例所包含的属性;这将由JobDetail类来完成,需要的朋友可以参考下
    2023-11-11

最新评论