@Transactional 注解失效场景?看这一篇就够了!

2,031 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

人生天地间,忽如远行客。

@Transactional注解

你们项目中有使用到事务吗?

这是什么问题?(内心小声bb)

面试官你好,我们是使用过事务的,一般的业务场景是在我们保证一组操作需要满足原子性的时候使用,在Spring中呢,一般来说有两种方法来使用事务,第一种是编程式事务管理,另一种呢,就是我们常用的@Transactional注解了,对于@Transactional注解来说的话,一般来说有七种不同的事务传播级别...

@Transactional注解失效的场景

那你说说看......什么情况下注解会失效呢?

???(为什么不按常理出牌?不应该问我传播级别吗???)

@Transactional注解属性 propagation设置问题

咳...是这样的面试官,你看我刚刚说话被您打断了,我正要说呢,我刚刚所说的事务传播级别里面就有两种情况会导致@Transactional注解失效的情况,分别是:

PROPAGATION_SUPPORTS:如果上下文中存在事务则加入当前事务,如果没有事务则以非事务方式执行。

PROPAGATION_NOT_SUPPORTED:当上下文中有事务则挂起当前事务,以非事务方式执行完当前逻辑后再恢复上下文事务。

PROPAGATION_NEVER:该传播级别要求上下文中不能存在事务,否则抛出异常。

从这三种参数的定义解释就能发现,不能产生事务。第三种就不用多说了,在此隔离级别下根本不会有事务存在,如果有,那就直接抛出异常,我们很容易就会发现。那更应该注意的是,第一种和第二种传播级别下注解失效的情况,因为这种情况下我们根本不能马上发现我们的@Transactional注解失效了。

@Transactional注解使用在非public方法上

好的我知道了,还有吗?

别急啊,多的是,还有还有,比如我们错误的把注解使用在非public方法上的时候,此注解是不生效的。

为什么呢?

(哎哎哎,我能答出来,当然知道为什么啦,上套了吧)

是这样的,我们使用@Transactional注解,本质上是通过AOP来完成的,cglib动态代理会把你当前执行方法的前后加上切面,加切面的过程中呢,会调用到下面这个方法:

image.png

我们可以看到第一行代码,在我们的方法是非public的时候,会直接返回null。所以当我们把注解加在非public方法上面时,@Transactional注解会失效。

那你知道为什么要这么做吗?

(越界了啊!)

咳....容俺想想...

哦!我想到了,cglib动态代理跟jdk动态代理的区别是jdk动态代理通过反射的方式来实现动态代理,而cglib使用的是继承后再通过修改字节码的方式来实现的。对!就是因为继承,在我们使用cglib动态代理的时候,我们会要求被代理的类不能是final的,因为如果一个类被标记为final的时候,我们就无法继承它了。

那么同样的道理,我们如果一个方法不是非public的,我们就无法覆盖这个方法,那就也就无法在这个方法前后添加切面了!然后这种注解失效的类型编译器也不会报错,编译过程也不会报错,我们也无法马上注意到注解失效了,这种情况也是我们要注意的。

@Transactional 注解属性 rollbackFor 设置问题

那还有吗?

(刚刚说了嘛!有的有的~)

我们知道,我们的@Transactional注解的默认情况下是只有在RuntimeException的情况下才会回滚的,如果我们的业务中需要发生其他异常的时候来回滚,比如我们自定义的业务异常,那我们就需要去设置rollbackFor参数,其实我们只需要保证我们的业务异常类是我们配置在rollbackFor参数中类的子类就可以了。

@Transaction注解加在方法内部调用的方法中

还有还有,这种情况是我们经常在开发中遇到的,也就是说,如果我有一个A类,里面分别有两个function,分别是functionA,functionB,当然,我在functionA中调用的functionB,functionA中没有声明事务,而在functionB中声明了事务,然后我在外部调用functionA时,functionB的事务是不会生效的。

那为什么呢?

是这样的,这个问题也是由我们的SpringAOP造成的,因为只有我们的事务方法在被当前类意外的方法调用时,Spring才会生成代理对象。

数据库引擎不支持事务

这种方法说法我认为等于脱裤子放屁...(???)

小伙子不错,回去等通知吧。

???

一些问题

在梳理这个问题的时候,我也有了解到这个问题的一些争议,是关于try-catch的

问题是,如果我在try中包裹了我的业务逻辑,然后我把异常catch住了,那数据库操作还会回滚吗?

关于这个问题,大家可以看看我的上一篇文章:一文带你彻底搞懂Spring事务传播机制(面试系列)

结论是,try-catch不会使注解失效。

后记

这个问题我在面试中被问过N次了,一般来说,说到事务,八成的几率会被问。

希望文章能带给大家一点力量。

同时也带给我自己一点力量。

同时也希望大家可以关注我的公众号【类似程序员】,会定期与大家分享我的一些想法与学习感悟,谢谢铁子们!