Su的技术博客

  • 首页
  • 原创
  • 视频
  • Java
  • MySQL
  • DDD
  • 事故复盘
  • 架构方案
  • AI
  • Other
  • 工具
    • AI工具集
    • 工具清单
    • JSON在线格式化
    • JSON在线比较
    • SQL在线格式化
  • 打赏
  • 关于
路很长,又很短
  1. 首页
  2. Java
  3. 正文
                           

【转载】事务异常:Transaction rolled back because it has been marked as rollback-only

2023-12-16 11124点热度 0人点赞 1条评论

最近在整理以前的工作笔记,又发现一个有意思的bug,在一个service调用另外一个类的方法时,出现了一个Spring的事务问题,Transaction rolled back because it has been marked as rollback-only,时间过去有点儿久了,不记得是压测时出的问题还是线上运行时出的问题了。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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)
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)
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方法出错抛异常。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 外层
@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";
}
// 外层 @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"; }
// 外层
@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事务状态;

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 外层
@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;
}
// 外层 @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; }
// 外层
@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操作则不建议)

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 里层
// 重点看注解中的propagation参数
@Transactional(readOnly = true, rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public String B() {
// throw Exception
return "test";
}
// 里层 // 重点看注解中的propagation参数 @Transactional(readOnly = true, rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) public String B() { // throw Exception return "test"; }
// 里层
// 重点看注解中的propagation参数
@Transactional(readOnly = true, rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public String B() {
    // throw Exception
    return "test";
}

问题总结:

这个问题其实真的很典型,尤其是新手,一不小心就可能遇到,如果项目是MVC架构,那我们尽量不要在service层中搞太多的try catch,除非是必须的一些场景(如:多线程、IO),不要害怕抛异常,现在很多框架都会有全局异常处理器这个东西,很多时候,我们完全可以利用异常和它的搭配减轻很多重复代码。

转自:不忘初心 https://www.jiweichengzhu.com/article/9e7c096df3484dc1bd78c2b95a702620

更多文章:

  1. Spring事务无法生效的11个场景
  2. 聊聊spring事务失效的12种场景,太坑了
  3. Spring中@Autowired和@Inject注解的区别?
  4. 如何让Java编译器帮你写代码
  5. 殷浩详解DDD系列 第一讲 - Domain Primitive
  6. JVM 内存分析工具 MAT 的深度讲解与实践——进阶篇(长文)
  7. 记一次堆内外内存问题的排查和优化
  8. MySQL事务死锁问题排查
  9. 生产环境JVM崩溃问题排查解决
  10. 【进阶玩法】策略+责任链+组合实现合同签章
标签: 转载 线上问题 Java 后端 Spring 事务 异常
最后更新:2023-12-16

coder

分享干货文章,学习先进经验。

打赏 点赞
< 上一篇
下一篇 >

文章评论

  • 秋天0261

    这是后端开发常见的事务异常,务必掌握!

    2023-12-16
    回复
  • razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
    取消回复

    广告
    最新 热点 推荐
    最新 热点 推荐
    微服务架构:必懂的6大性能维度 Anthropic Code with Claude 开发者大会:开启 AI Agent 新时代 视频笔记-微服务架构P4:必懂5种设计模式 视频笔记:微服务架构P4 设计模式:每服务数据库、API 网关和事件驱动架构 干货 | 论Elasticsearch数据建模的重要性 马蜂窝消息总线——面向业务的消息服务设计 基于 MySQL Binlog 实现可配置的异构数据同步 视频笔记:Google发布Agent2Agent协议
    视频笔记-微服务架构P4:必懂5种设计模式Anthropic Code with Claude 开发者大会:开启 AI Agent 新时代微服务架构:必懂的6大性能维度
    事件驱动架构(EDA) VS 请求响应架构(RR) 高并发场景下JVM调优实践之路 解放双手!ChatGPT助力编写JAVA框架 Google Gemini技术报告要点提炼 系统设计 | 对象转换方案 视频笔记:Google发布Agent2Agent协议 《2023 年度 AI 大事记》 手把手教你落地DDD

    CRUD (1) Event Sourcing (1) graphql (1) id (1) NoSQL (1) quarkus (1) rest (1) RocketMQ (2) Spring Boot (1) zk (1) zookeeper (1) 上下文 (1) 事务消息 (1) 二级缓存 (1) 值对象 (1) 关系数据库 (1) 分布式缓存 (1) 原子性 (1) 唯一ID (1) 商品 (1) 多对多 (1) 子域 (1) 字符集 (1) 客户端心跳 (1) 幂等 (2) 干货 (1) 并发 (1) 应用场景 (1) 应用架构图 (1) 康威定律 (2) 异步复制 (1) 微服务架构 (3) 总体方案 (1) 技术方案 (2) 技术架构 (2) 技术架构图 (1) 技能 (1) 持续集成 (1) 支撑域 (1) 故障恢复 (1) 数据架构图 (1) 方案选型 (1) 日记 (1) 服务发现 (1) 服务治理 (1) 服务注册 (2) 机房 (1) 核心域 (1) 泄漏 (1) 洋葱架构 (1) 消息队列 (5) 源码剖析 (1) 灰度发布 (1) 熔断 (1) 生态 (1) 画图工具 (1) 研发团队 (1) 线程 (2) 组织架构 (1) 缓存架构 (1) 编码 (1) 视频 (20) 读写分离 (1) 贵州 (1) 软件设计 (1) 迁移 (1) 通用域 (1) 集群化 (1) 雪花算法 (1) 顺序消息 (1)

    推荐链接🔗
    • AI工具集
    • 工具箱🛠️

    站点已运行 1500 天

    COPYRIGHT © 2014-2025 verysu.com . ALL RIGHTS RESERVED.

    Theme Kratos Made By Seaton Jiang

    粤ICP备15033072号-2

    通知