sping事务失效的几种场景

一.数据库引擎不支持事务

spring的事务需要底层数据库引擎的支持

这里以 MySQL 为例,其 MyISAM 引擎是不支持事务操作的,InnoDB 才是支持事务的引擎,一般要支持事务都会使用 InnoDB。

根据 MySQL 的官方文档:

http://dev.mysql.com/doc/refman/5.6/en/storage-engines.html

从 MySQL 5.5.5 开始的默认存储引擎是:InnoDB,之前默认的都是:MyISAM,所以这点要值得注意,底层引擎不支持事务再怎么搞都是白搭。

MySQL的几种引擎可以了解 https://www.cnblogs.com/zluckiy/p/13793799.html

二.在非public修饰的方法使用

以下来自 Spring 官方文档:

When using proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.

大概意思就是 @Transactional 只能用于 public 的方法上,否则事务不会失效,如果要用在非 public 方法上,可以开启 AspectJ 代理模式。

代码中解释:

@Transactional注解使用的是AOP,在使用动态代理的时候只能针对public方法进行代理,源码依据在AbstractFallbackTransactionAttributeSource类中的computeTransactionAttribute方法中,如下:

1 protected TransactionAttribute computeTransactionAttribute(Method method,
2     Class<?> targetClass) {
3         // Don't allow no-public methods as required.
4         if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
5         return null;
6 }

此处如果不是标注在public修饰的方法上并不会抛出异常,但是会导致事务失效。

三.在try.catch代码段中,没有将异常抛出,导致事务无法回滚

1 @Transactional
2 public void method(){
3   try{//错误不会回滚
4     //插入一条数据
5     //更改一条数据
6   }catch(Exception ex){
7     return;
8   }
9 }

四.抛出的异常类型不对,默认为运行时异常RuntimeException

1 @Transactional
2 public void method(){
3   try{
4     //插入一条数据
5     //更改一条数据
6   }catch(Exception ex){
7     return;
8   }
9 }

这样事务也是不生效的,因为默认回滚的是:RuntimeException,如果你想触发其他异常的回滚,需要在注解上配置一下,如:

 1 @Transactional(rollbackFor = Exception.class)                                                                                                                             

五.方法中调用同类的方法

 1 public class Test{
 2   public void A(){
 3     //插入一条数据
 4     //调用B方法
 5     B();
 6   }
 7   
 8   @Transactional
 9   public void B(){
10     //插入数据
11   }
12 }

简单的说就是一个类中的A方法(未标注声明式事务)在内部调用了B方法(标注了声明式事务),这样会导致B方法中的事务失效。

为什么会失效呢?:其实原因很简单,Spring在扫描Bean的时候会自动为标注了@Transactional注解的类生成一个代理类(proxy),当有注解的方法被调用的时候,实际上是代理类调用的,代理类在调用之前会开启事务,执行事务的操作,但是同类中的方法互相调用,相当于this.B(),此时的B方法并非是代理类调用,而是直接通过原有的Bean直接调用,所以注解会失效。

六.配置错误导致事务失效

1.Propagation传播行为配置错误

spring默认的事务传播属性是Propagation.REQUIRED,但是一旦配置了错误的传播属性,也是会导致事务失效,如下三种配置将会导致事务失效:

Propagation.SUPPORTS

Propagation.NOT_SUPPORTED

Propagation.NEVER

 

 

2代码中我们一般通过spring管理,去实现事务,需要注意代码块有没有被spring进行管理

3数据源没有配置事务管理器等配置相关的问题导致

 

参考https://javastack.blog.csdn.net/article/details/103871083

  https://blog.csdn.net/qq_34162294/article/details/105803966

posted @ 2020-10-26 23:55  Jeessu  阅读(599)  评论(0编辑  收藏  举报