您现在的位置是:网站首页 > 代码编程 > JAVA开发JAVA开发
【原】Transaction rolled back because it has been marked as rollback-only
不忘初心 2021-12-05 围观() 评论() 点赞() 【JAVA开发】
简介:最近在整理以前的工作笔记,又发现一个有意思的bug,在一个service调用另外一个类的方法时,出现了一个spring的事务问题,Transaction rolled back because it has been marked as rollback-only,时间过去有点儿久了,不记得是压测时出的问题还是线上运行时出的问题了。
最近在整理以前的工作笔记,又发现一个有意思的bug
,在一个service
调用另外一个类的方法时,出现了一个spring
的事务问题,Transaction rolled back because it has been marked as rollback-only,时间过去有点儿久了,不记得是压测时出的问题还是线上运行时出的问题了。
2021-01-25 19:42:00.025 [pool-3-thread-1] ERROR o.s.s.support.TaskUtils$LoggingErrorHandler - Unexpected error occurred in scheduled task.
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:724)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:504)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
at com.sowin.xszz.service.impl.BsBjServiceImpl$$EnhancerBySpringCGLIB$$35a078c9.addBj(<generated>)
at com.sowin.xszz.task.XsBjTask.addBj(XsBjTask.java:23)
at sun.reflect.GeneratedMethodAccessor248.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
上面是完整的详细错误信息,Transaction rolled back because it has been marked as rollback-only这句话翻译过来大体上的意思就是:事务已经回滚,因为它被标记为仅回滚,咋一听还是挺拗口,但是如果大家对spring
事务有一定了解的话,就很容易看出来,这个问题就是一个典型的嵌套事务问题。
我为什么强调是这是一个典型案例呢?
因为在很多时候,有些人害怕方法出错导致整个调用链崩溃,所以在自己写的方法中都会下意识的加上try catch
语句,而spring
的事务是利用aop
机制来实现的,也就是说提交事务和回滚事务这些操作都是它帮我们做的,我们并没有显示处理,那它之所以能够正确知道是提交事务还是回滚事务,这个就得靠我们给它信号了,没错,你们已经猜到了,抛异常就是我们给它的信号。
这么解释大家应该清楚了,应该也能大概猜到这问题多少都跟try catch
有关,不着急,我们接着往下看,后面会用到这块儿知识点,我们现在回到spring
事务本身来。
在spring的事务体系中,事务是可以继承的,它利用一个传播属性propagation
来标记各种继承关系(详细的继承关系这里就不细说,如果大家想了解清楚,请移步Srping事务的七种传播特性),默认的是REQUIRED
,也就是说里层的方法共用外层方法的事务。
为了给大家看的更清楚,我给大家简单模拟两句出问题的代码,这样有助于理解,模拟场景为方法A调用方法B,双方都加事务,然后B方法出错抛异常。
// 外层
@Transactional(readOnly = true, rollbackFor = Exception.class)
public String A() {
try {
// 调用B方法
String s = B();
// ...
return s;
} catch(Exception e) {
e.printStacktrace();
}
return null;
}
// 里层
@Transactional(readOnly = true, rollbackFor = Exception.class)
public String B() {
// throw Exception
return "test";
}
如果上面的分析看明白了,那这里就好理解了,A()
调用B()
时,B()
方法出错抛异常了,但是A()
方法呢,自己把异常给捕获了,而且在吃掉异常之后,它并没有做进一步处理,也没有继续向上抛,那此时就会出现本文中所提到的错误Transaction rolled back because it has been marked as rollback-only。
那可能有人又要问了,那A()
吃掉异常跟B()
有什么关系呢?
诶,问到点子上来了,我们上面提到了事务的继承关系,那么代入到这个案例中,也就是说B()
其实最终使用的还是A()
的事务,那么在B()
本身出异常时,它自己是已经抛过了的,此时它的transaction
已经被设置为了rollback-only
了,而当A()
将异常吃掉之后,统一交由spring
来提交事务,这时候B()
就会报这个错误了,大家可以简单理解一下,在这种嵌套事务中外层一旦吃掉异常之后,就会导致里层方法的事务状态不对,从而就会导致该错误的产生。
解决方法:
1、在try catch中手动处理transaction事务状态;
// 外层
@Transactional(readOnly = true, rollbackFor = Exception.class)
public String A() {
try {
// 调用B方法
String s = B();
// ...
return s;
} catch(Exception e) {
// 划重点,看这里
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
e.printStacktrace();
}
return null;
}
2、给每一层开启新事务,各自为政(如果涉及到db操作则不建议)
// 里层
// 重点看注解中的propagation参数
@Transactional(readOnly = true, rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public String B() {
// throw Exception
return "test";
}
问题总结:
这个问题其实真的很典型,尤其是新手,一不小心就可能遇到,如果项目是MVC
架构,那我们尽量不要在service
层中搞太多的try catch
,除非是必须的一些场景(如:多线程、IO),不要害怕抛异常,现在很多框架都会有全局异常处理器这个东西,很多时候,我们完全可以利用异常和它的搭配减轻很多重复代码。
看完文章,有任何疑问,请加入群聊一起交流!!!
很赞哦! ()
相关文章
- SpringBoot使用@Async注解导致循环依赖的原因及解决方案
- springboot配置双数据源报错“jdbcUrl is required with driverClassName”
- 低版本idea中SpringBoot项目启动失败,提示找不到 javax/servlet/ServletContext类
- springboot整合jpa启动报错'hibernate.dialect' not set
- springboot使用jackson处理时间碰到的两个坑
- springboot项目提示“Failed to determine a suitable driver class”
- springboot打war包,部署到外部tomcat
- springboot项目在mac下启动特别慢
- 使用idea搭建springboot项目图文教程
- Spring Boot配置Druid数据源和使用教程
标签云
猜你喜欢
- IntelliJ IDEA 2019.2已经可以利用补丁永久破解激活了
- IntelliJ IDEA 2019.3利用补丁永久破解激活教程
- IntelliJ IDEA高版本最灵活的永久破解激活方法(含插件激活,时长你说了算)
- Jetbrains全家桶基于ja-netfilter的最新破解激活详细图文教程
- IntelliJ IDEA 2022.1永久破解激活教程(亲测可用,持续更新)
- 分享几个正版 IntelliJ IDEA 激活码(破解码、注册码),亲测可用,持续更新
- ja-netfilter到底需不需要mymap,2021.3.2版本激活失效?
- 如何激活idea2022.1及以上版本中的插件(亲测可用)
- 【史上最全】IntelliJ IDEA最新2022.1版本安装和激活视频教学(含插件)
- IntelliJ IDEA 2022.2 版本最新2099年永久激活方法,亲测可用,也可以开启新UI了。
站点信息
- 网站程序:spring + freemarker
- 主题模板:《今夕何夕》
- 文章统计:篇文章
- 标签管理:标签云
- 微信公众号:扫描二维码,关注我们