线上问题排查@Transactional事务失效
问题排查
事情是这样的, 在用户测试系统时,有一个功能出现了点问题,跟我沟通了一下,让我排查一下,我第一时间去查看系统error日志, 但是我打开日志一看,哇塞,好多报错,而且都是一样的,而且一直在报错,我一开始想不管这个报错的,但是根本不行啊,error信息一直刷,我根本查不到我想看的我那部分的报错信息。 后来我只好先排查原来的这个报错,很明显它是一个定时任务再疯狂报错。(报错如下:)
然后下面看一下代码。
194行:
305行:
257行:
这就很明显是一个每8秒调用一次的定时任务。
报错信息是: org.springframework.orm.hibernate5.HibernateSystemException: Could not obtain transaction-synchronized Session for current thread; nested exception is org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
因为我们的项目使用的是hibernate框架, 所以这个报错就是说:hibernate 在crud时必须要在事务内运行,不然就会报错。
其实最开始,这几个方法中只有设置定时任务的那个方法写了@Transactional注解,后续两个方法时我后加的,但是显然还是不好用,还是报错,而且还是同样的错。
我当时非常奇怪, 因为我对比了项目中其他使用了hibernate的部分,我复制粘贴的跟其他部分一模一样,但是死活就是在这里不好用。
我中途尝试了更换各种事务的传播方式,但最后都不好用。
因为最近在看一下八股,在这些都尝试了一遍之后还是不好用,没想到看八股也是有点收获的。我突然想起来,我把所有的事务的写法都试了,还不好使,那就不是事务本身的事了,而是这个事务没生效。
对然后我回想了事务失效的集中情况。
@Transactional事务是基于spring框架中的AOP代理实现的,所以使用这个事务,那就一定要走代理。我排查代码发现,这三个方法这都在一个service里啊, 靠, 这自己在调用自己,肯定不走AOP,就没有代理了。那事务失效了呀, 怪不得怎么改都不行。那么这下问题定位到了,解决起来就不难了。
解决方案
既然是没使用代理,那就让他走代理。
因为这是以前的老代码,我要做到最少的改动,尽力不影响以前的任何东西,所以我直接就在文件顶部注入了一个当前的service,然后通过aop自己调用自己。
ok,最后这样就解决了。
小结: 其实我们平常正常的业务逻辑都不会发生事务失效这个问题,因为我们都是按照MVC三层架构来运行的,所以就都是使用了注入的service来调用方法的,所以就会走spring的aop代理,事务也就会生效。
我这个事务失效是因为直接在service层使用了定时任务表达试才导致,根本没有controller层,这也是以前写这个代码的人不规范导致的。我们项目其实是有一个专门的前台能够配置定时任务的,所有的定时任务其实都要统一写在一个controller中,前端配置执行就可以了,而不需要代码层直接使用 @Scheduled(cron = "0/8 * * * * ? ") 这种表达式来启动,这样很不方便管理。
下面粘出正确的定时任务使用方式:
这样即好管理也好查看日志,比较方便正规。
我怀疑可能是原来开发的小伙伴对公司已经有的一些平台功能不太熟悉,才自己写了定时任务,所以大家写代码之前可以先跟公司大佬们请教一下公司已经有的框架,都能够大大缩短自己开发时间。
我的博客:http://he-bi.cn/#/