发现Spring事务的一个实锤bug,官方还拒不承认?你来评评理...

事情是这样的,上周我正在全神贯注的摸鱼,然后有个小伙伴给我发来微信消息,提出了自己关于事务的一个疑问,并配上两段代码:

先说结论:我认为这是 Spring 事务的一个 bug。但是官方说这只能算是文档上的缺陷,不能算是代码的 bug。

(好吧,我这篇文章写了好几天,所以我写到上面这一句的时候,官方还不承认是 bug,但是写完之后他们也承认确实是代码缺陷。不影响,接着往下看。)

好家伙,我懂了,一切解释权归官方所有。

在开始刨根问底之前,我想先就关于如何提问这个问题掰扯几句。

我把上面这个读者的问题截出来,是因为我觉得这个提问简直就是模板方法般的提问。

给了一段示例代码、给了一段源码、说明自己的问题、并表明自己已经查询过但是没有找到合适的答案。

我读完他的文字,我很快就能 get 到他的问题是什么,所以我们之间的交流就非常的高效。

最终我们并没有讨论出一个合理的解释,于是他去提了一个 issues,希望能得到官方的比较权威的回答。

所以我们的故事就围绕着这个 issues 开始吧。

舞台搭建

在正戏开始之前,我先给你把舞台搭建出来,也就是把 Demo 搞出来。

因为是关于 Spring 事务的问题嘛,所以这个 Demo 主要就是体现出“事务”的应用就行了嘛。

所以 Demo 里面最核心的东西就是这个部分:

其中涉及到的两个异常就是简单的自定义异常:

假设这里有一个只允许 10-18 岁的用户使用奇怪的网站,这个部分就代表这个网站的用户注册功能。

接着我们往 user_info 表里面插入一条数据,然后判断年龄如果大于 18 岁,那么抛出 AgeExceptionOver18 异常,表示这个用户不是目标用户。

但是你注意,我 @Transactional 注解里面的 rollbackFor 是 AgeException.class,意思是我并不想回滚这个大于 18 岁的用户,我还是想把他的注册信息保存下来,只是抛出一个异常来表示他不是我的目标用户,

而当我们插入一个年龄小于 10 岁的用户的时候,会抛出 AgeException.class,应该把刚刚执行的插入语句给回滚掉,我并不想保存这部分用户的信息。

好的,那么现在就会有小伙伴问了:小于 10 岁的用户既然不想保存,那么为什么不在插入之前判断呢?

很好的问题,实际开发中肯定是要在插入之前判断的,但是我这里只是为了演示事务功能,所以我就这样写了,咋地了吧!

上面的代码,我来搞个接口触发一下,也就是弄个 Controller 出来:

上面的四个类,就是最关键的几个类,所以我单独拿出来说一下。

整个项目结构也非常的简单:

其他的类不关键,就不拿出来说了,都是你最拿手的 crud。花五分钟搭一个这个项目出来不过分吧?中间还能摸两分钟的鱼。

我把日志级别调整为 debug 级别,接着把项目跑起来验证一下功能。

然后调用这个链接:

http://127.0.0.1:8085/insertUser?age=8

对应的日志是这样的:

可以看到我框起来的部分,首先确认执行了 insert 语句,且 age 确实是为 8。但是最后 Rolling back 了,即回滚了。

为什么回滚,我们心里也是门清,因为这里呼应上了:

接下来试一下 age 为 18 岁的用户:

http://127.0.0.1:8085/insertUser?age=18

对应的日志是这样的:

这没啥说的,事务成功提交,数据插入成功。是我们预期的结果。

数据库数据也没毛病:

然后试一下 age 为 28 岁的用户。

这个用户我们的预期是抛出 AgeExceptionOver18 异常,但是数据得插入成功。

来走一个:

http://127.0.0.1:8085/insertUser?age=28

对应的日志是这样的:

首先数据居然回滚了???

异常倒是抛出来了,但是这也没呼应上啊!

先不管到底啥原理吧,从我的认知来说,首先我的 @Transactional 注解用法绝对没有错,事务配置没有绝对没有错,我的异常也没有乱抛,你凭什么给我回滚了?

你还说这不是 bug?

just 改改 documentation 就行了?

言外之意是要“抵赖”,强行从文档上找补吗?

这不是欺负老实人吗?

额...等等,我写这段的时候情况是这样的。

但是等我写完这段,第二天再次进 issues 里面去看,发现事情发生了变化,官方又承认这是一个 bug 了,会在 5.3.x 版本里面修改文档上的描述,会在 6.0 版本里面进行代码上的修复。

但是我前面已经铺垫了这么多,已经写好了,我就不改了,就当在这里留下一个创作痕迹吧,哈哈。

我们接着往下看。

戏剧冲突

一部戏,肯定有它的戏剧冲突,这是它的核心部分。那么我们 Demo 里面的核心冲突是什么呢?

这一小节就先告诉你“戏剧冲突”在哪。

我先问你一个问题:

Spring 管理的事务,默认回滚的异常是什么呢?

我们带着这个问题去看源码,找到了这个问题的答案,你就能丝滑入戏。

先搞个断点,把程序跑起来,然后看调用栈:

可以看到调用栈里面和事务相关的有这样一个方法:

org.springframewo

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值