Spring事务回滚中的那些坑

      在使用Spring注解进行事务控制的时候,我们都习惯性用@Transactional 注解进行处理,但是指值得注意的一点是真正出现异常的时候,你的事务真正回滚了吗?以下就列举几种添加@Transactional但是事务回滚没有生效的情景。

一、除非特殊配置只有定义在 public 方法上的 @Transactional 才能生效

        原因是,Spring 默认通过动态代理的方式实现 AOP,对目标方法进行增强,private 方法无法代理到,Spring 自然也无法动态增强事务处理逻辑。

二、必须通过代理过的类从外部调用目标方法才能生效

Spring 通过 AOP 技术对方法进行增强,要调用增强过的方法必然是调用代理后的对象。

(1)同一个类中通过this调用的场景

图1

      图1中的代码逻辑通过this调用,抛出RuntimeException 是不会回滚事务的,具体原因是this 指针代表对象自己,而不是Spring注入的代理。而只有通过代理访问的方法才有可能进行回滚。而代理和this 的区别如图2所示:this指的是TestController这个类,而真正经历Spring注入的代理是有“CGLIB”字样的testService。只有经过testService代理直接调用的方法在出现异常的时候才会进行回滚。


图2this和代理的区别

三、不是所有的异常都会触发回滚

      默认情况下,出现 RuntimeException(未检查异常)或 Error 的时候,Spring 才会回滚事务。此处需要解释一下Java中的异常:

      Java中异常分为两大类:checkedexception(检查异常)和unchecked exception(未检查异常)。

      未检查异常也可以叫做RuntimeException(运行时异常),他们的主要区别:对于运行时异常,java编译器不要求捕获或者一定要继续抛出,但是必须捕获或者抛出检查异常。

    常见的检查异常:Exception,FileNotFoundException,IOException,SQLException。

      常见的未检查异常:      NullPointerException,ClassCastException,ArrayIndexsOutOfBoundsException,ArithmeticException(算术异常,除0溢出)。

    (1)如果在代码中对异常进行了try catch 操作使得RuntimeException(未检查异常)或 Error 没有抛出来,此时事务业务不会回滚,也可以正常执行。如果你希望自己捕获异常进行处理的话,也没关系,可以手动设置让当前事务处于回滚状态。具体代码如图3所示。


图3

  (2)在注解中声明,期望遇到所有的 Exception 都回滚事务(来突破默认不回滚受检异常的限制)可以使用如下代码对所有的异常进行回滚。

@Transactional(rollbackFor = Exception.class)

四、多次数据库操作回滚互不影响

在一个事务中有多个更新数据库的操作,但是又不想因为某一个数据库操作的回滚影响到其他操作的完成。

为了描述方便我们把不需要回滚的称为主流程,能够回滚的是子流程。如果不做额外处理的话子流程如果回滚,即使子流程的代码被try catch 住由于子流程已经将该事务标注为回滚事务,最后主流程也会跟着回滚。

解决这个问题的办法让子流程在独立事务中运行,也就是为子流程注解加上 propagation = Propagation.REQUIRES_NEW 来设置 REQUIRES_NEW 方式的事务传播策略,也就是执行到这个方法时需要开启新的事务,并挂起当前事务即可。

@Transactional(propagation = Propagation.REQUIRES_NEW)

总结:

(1)我们务必确认调用 @Transactional 注解标记的方法是 public 的,并且是通过 Spring 注入的 Bean 进行调用的。

(2)因为异常处理不正确,导致事务虽然生效但出现异常时没回滚。Spring 默认只会对标记 @Transactional 注解的方法出现了 RuntimeException 和 Error 的时候回滚,如果我们的方法捕获了异常,那么需要通过手动编码处理事务回滚。如果希望 Spring 针对其他异常也可以回滚,那么可以相应配置 @Transactional 注解的 rollbackFor 和 noRollbackFor 属性来覆盖其默认设置。

(3)如果方法涉及多次数据库操作,并希望将它们作为独立的事务进行提交或回滚,那么我们需要考虑进一步细化配置事务传播方式,也就是 @Transactional 注解的 Propagation 属性。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272