likes
comments
collection
share

问了ChatGPT一个事务问题,然后被忽悠了

作者站长头像
站长
· 阅读数 16

Hello 大家好, 这里是Anyin。

给大家说一个笑话:我被ChatGPT忽悠了。

引言

前两天在编码的时候遇到一个事务未生效的问题,当时感觉比较诡异。因为我一个方法注解了@Transactional注解,但是事务未生效,而且这个方法是kafka监听之后调用的。

我出于对kafka的不熟悉,第一反应就是问下ChatGPT,结果竟然被它忽悠了!!!

实例代码如下:

问了ChatGPT一个事务问题,然后被忽悠了

问了ChatGPT一个事务问题,然后被忽悠了

这里的active就是false,说明事务未开始。

ChatGPT忽悠之路

问题1 : @KafkaListener注解的方法中调用someService.foo()方法,foo()方法注解@Transactional,事务无法生效为什么

回答1 : 根据描述,您在使用Spring Framework中的@KafkaListener注解来监听Kafka消息,并且在处理消息的方法中调用了someService.foo()方法,该方法被@Transactional注解标记。然而,您发现事务并未生效。

通常,@Transactional注解只对在同一个线程中的方法调用才会生效。在您的情况下,@KafkaListener注解会在一个新的线程中启动消息监听器,因此该方法调用someService.foo()将在一个新的线程中执行。这意味着事务管理器无法自动地控制这个新线程中的事务。

为了解决这个问题,您可以尝试在@KafkaListener方法中使用Spring的TransactionTemplate或者PlatformTransactionManager手动开启一个新的事务。

问题2: 可是注解了@ Transactional 不会自动新开一个事务吗

回答2: 是的,使用@Transactional注解的方法应该会自动创建一个新的事务,但是在您的情况下,由于@KafkaListener方法被注解标记,它会在另一个线程中异步执行,因此会导致@Transactional注解失效。

这是因为Spring事务管理器默认使用ThreadLocal机制来管理事务,每个线程都有自己的事务状态。在@KafkaListener方法中调用的someService.foo()方法实际上是在另一个线程中执行,而不是在发起消息的线程中执行,因此事务无法跨越线程进行管理。

为了解决这个问题,您可以在@KafkaListener方法中手动开启一个新的事务。这样可以确保在处理Kafka消息时创建新的事务并管理事务的生命周期。您可以使用TransactionTemplate或者PlatformTransactionManager手动开启一个新的事务,具体实现可以参考上面的代码示例。


通过以上的2轮回答,我当时也没多想,认为是@KafkaListener这个注解的内部机制,导致的事务失效,并且当时也随手把这两个问答发到了微头条,没想到微头条竟然小爆了下,给我带来了6块钱的收益。

问了ChatGPT一个事务问题,然后被忽悠了

问题排查

被ChatGPT忽悠之后,我也是跟着它这个思路,认为是@KafkaListener内部机制导致的问题。那么kafka作为一个主流的MQ,不可能忽视这个问题。

所以,通过ChatGPT问答和Google,我了解到了一个KafkaTransactionManager这么一个类,认为这是解决问题的关键(其实已经被带偏了),通过一通的操作,最后还是没解决问题。

到这里,已经陷入深深的自我怀疑当中,这么多年的Java代码都白写了吗?

接着,不信邪的我,干脆写4个用例,自测下。

  • 正常的Controller,同步调用Service,Service添加@Transactional注解,Service内部同步调用Update方法
  • 正常的Controller,同步调用Service,Service添加@Transactional注解,Service内部异步执行Update方法
  • 正常的Controller,异步调用Service,Service添加@Transactional注解,Service内部同步调用Update方法
  • 正常的Controller,异步发送Kafka,Kafka消费者调用Service,Service添加@Transactional注解,Service内部同步调用Update方法

Controller 代码如下:

问了ChatGPT一个事务问题,然后被忽悠了

Service 代码如下:

问了ChatGPT一个事务问题,然后被忽悠了

作为一个多年的老Java程序猿,我当然知道@Transactional只会在当前线程生效,所以在我的预想当中,肯定只有innerAsyncUpdate方法不会进行事务回滚,另外两个肯定会回滚。

然后,事情出乎我的意料,4个场景,都没有回滚!!!

这个时候我才意识到,这个不是@KafkaListener注解机制的问题了,应该是整个项目配置问题,从而导致整个事务都没有启动!!!

问题解决

本次问题知道原因了,那就好解决了。

理论上,SpringBoot项目应该是默认启用事务管理器的,但是项目配置没有,所以我就手动启用事务管理器,在启动类上添加上@EnableTransactionManagement注解。

接着再使用测试用例1测试下,发现报异常了。

问了ChatGPT一个事务问题,然后被忽悠了

嗯? 没有事务管理器? 理论上,应该也是默认初始化的呀!!

好吧,那我继续手动初始化一个事务管理器就好。

问了ChatGPT一个事务问题,然后被忽悠了

继续测试,OK!! 问题解决了,4个测试用例,都能正常工作,符合预期。


好了,问题解决了,但是为什么不会自动启用事务管理,为什么没有初始化事务管理器实例?

带着这2个问题,我根据DataSourceTransactionManager找到了自动初始化它的配置类。

问了ChatGPT一个事务问题,然后被忽悠了

到这里,看了下各个Condition条件,都是符合的,为什么没有初始化?

这个时候反应过来,该不会像DataSourceAutoConfiguration一样,被exclude了吧?

问了ChatGPT一个事务问题,然后被忽悠了

果然,在启动类上被exclude了。

至此,诡异的问题解决了:就是项目初始化的时候把事务给去掉了。(谁干的!!!)

而我也被ChatGPT忽悠了一次,闹了一个笑话。

最后

现在ChatGPT很火,并且最近还开放了插件生态,以后可能就是所有流量的入口了,可能会带来一大批职业失业的危机,但是也带来机遇。

作为一个普通人,改变不了什么,只能拥抱变化,尽量去体验下。当然现在ChatGPT还不是那么成熟,还需要自己甄别下,因为它经常会一本正经的胡说八道。

另外到3月26日为止,我再问ChatGPT一样的问题,我发现它已经不忽悠我了,已经能正确解答问题(虽然解决不了我的问题)。

你看, AI变化多快。

转载自:https://juejin.cn/post/7214725299807649851
评论
请登录