服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - spring @Transactional 无效的解决方案

spring @Transactional 无效的解决方案

2021-07-26 10:40dezun Java教程

这篇文章主要介绍了spring @Transactional 无效的解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

关于@transactional注解 一般都认为要注意以下三点

1 .在需要事务管理的地方加@transactional 注解。@transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上 。

2 . @transactional 注解只能应用到 public 可见度的方法上 。 如果你在 protected、private 或者 package-visible 的方法上使用 @transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。

3 . 注意仅仅 @transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据。必须在配置文件中使用配置元素,才真正开启了事务行为。

最近在项目中发现注解无效,经过跟踪源代码发现了问题,于是在网上找到相同出现此问题的人,以下为原文,讲解的很详细:

只要避开spring目前的aop实现上的限制,要么都声明要事务,要么分开成两个类,要么直接在方法里使用编程式事务

[问题]

spring的声明式事务,我想就不用多介绍了吧,一句话“自从用了spring aop啊,事务管理真轻松啊,真轻松;事务管理代码没有了,脑不酸了,手不痛了,一口气全配上了事务;轻量级,测试起来也简单,嘿!”。不管从哪个角度看,轻量级声明式事务都是一件解放生产力的大好事。所以,我们“一直用它”。

不过,最近的一个项目里,却碰到了一个事务管理上的问题:有一个服务类,其一个声明了事务的方法,里面做了三次插入sql操作,但是在后面出错回滚时,却发现前面插入成功了,也是说,这个声明了事务的方法,实际上并没有真正启动事务!怎么回事呢?难道spring的声明式事务失效了?

[探幽]

其实以前也会碰到有人说,spring的事务配置不起作用,但是根据第一反应和以往经验,我总会告诉他,肯定是你的配置有问题啦;所以这一次,我想也不会例外,大概是把事务注解配在了接口上而不是实现方法上,或者,如果是用xml声明方式的话,很可能是切入点的表达式没有配对。

 不过,在检查了他们的配置后,却发现没有配置问题,该起事务的实现方法上,用了@transactional事务注解声明,xml里也配了注解驱动<tx:annotation-driven .../>,配置很正确啊,怎么会不起作用?

我很纳闷,于是往下问:

问1:其他方法有这种情况么?

答1:没有。

问2:这个方法有什么特别的么(以下简称方法b)?

答2:就是调后台插了三条记录啊,没啥特别的。

问3:这个方法是从web层直接调用的吧?

答3:不是,是这个service类(以下简称servicea)的另外一个方法调过来的(以下简称方法a)。

问4:哦,那个调用它的方法配了事务么(问题可能在这了)?

 答4:没有。

问5:那web层的action(用的是struts2),调用的是没有声明事务的方法a,方法a再调用声明了事务的方法b?

答5:对的。

问6:你直接在方法a上加上事务声明看看

答6:好。。。

看来可能找到问题所在了,于是把@transactional也加在方法a上,启动项目测试,结果是:事务正常生效,方法a和方法b都在一个事务里了。

好了,现在总结一下现象:

1、servicea类为web层的action服务

2、action调用了servicea的方法a,而方法a没有声明事务(原因是方法a本身比较耗时而又不需要事务)

3、servicea的方法a调用了自己所在class的方法b,而方法b声明了事务,但是方法b的事务声明在这种情况失效了。

4、如果在方法a上也声明事务,则在action调用方法a时,事务生效,而方法b则自动参与了这个事务。

我让他先把a也加上事务声明,决定回来自己再测一下。

这个问题,表面上是事务声明失效的问题,实质上很可能是spring的aop机制实现角度的问题。

我想到很久以前研究spring的aop实现时发现的一个现象:对于以cglib方式增强的aop目标类,会创建两个对象,一个事bean实例本身,一个是cglib增强代理对象,而不仅仅是只有后者。我曾经疑惑过这一点,但当时没有再仔细探究下去。

 我们知道,spring的aop实现方式有两种:1、java代理方式;2、cglib动态增强方式,这两种方式在spring中是可以无缝自由切换的。

java代理方式的优点是不依赖第三方jar包,缺点是不能代理类,只能代理接口。

spring通过aopproxy接口,抽象了这两种实现,实现了一致的aop方式:

spring @Transactional 无效的解决方案

现在看来,这种抽象同样带了一个缺陷,那就是抹杀了cglib能够直接创建普通类的增强子类的能力,spring相当于把cglib动态生成的子类,当普通的代理类了,这也是为什么会创建两个对象的原因。下图显示了spring的aop代理类的实际调用过程:

spring @Transactional 无效的解决方案

因此,从上面的分析可以看出,methodb没有被aopproxy通知到, 导致最终结果是:

被spring的aop增强的类,在同一个类的内部方法调用时,其被调用方法上的增强通知将不起作用。

而这种结果,会造成什么影响呢: 1:内部调用时,被调用方法的事务声明将不起作用 2:换句话说,你在某个方法上声明它需要事务的时候,如果这个类还有其他开发者,你将不能保证这个方法真的会在事务环境中 3:再换句话说,spring的事务传播策略在内部方法调用时将不起作用。

不管你希望某个方法需要单独事务,是requiresnew,还是要嵌套事务,要nested,等等,统统不起作用。
4:不仅仅是事务通知,所有你自己利用spring实现的aop通知,都会受到同样限制。。。。

[解难]

问题的原因已经找到,其实,我理想中的aop实现,应该是下面这样:

spring @Transactional 无效的解决方案

只要一个cglib增强对象就好,对于java代理方式,我的选择是毫不犹豫的抛弃。

至于前面的事务问题,只要避开spring目前的aop实现上的限制,要么都声明要事务,要么分开成两个类,要么直接在方法里使用编程式事务,那么一切ok。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/gudejundd/article/details/54380141

延伸 · 阅读

精彩推荐