前言

最近在项目中发现了一则报错:“org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only”。根据报错信息来看是spring框架中的事务管理报错:事务回滚了,因为它被标记为回滚状态。

报错原因

多层嵌套事务中,如果使用了默认的事务传播方式,当内层事务抛出异常,外层事务捕捉并正常执行完毕时,就会报出rollback-only异常。
spring框架是使用AOP的方式来管理事务,如果一个被事务管理的方法正常执行完毕,方法结束时spring会将方法中的sql进行提交。如果方法执行过程中出现异常,则回滚。spring框架的默认事务传播方式是PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。
在项目中,一般我们都会使用默认的传播方式,这样无论外层事务和内层事务任何一个出现异常,那么所有的sql都不会执行。在嵌套事务场景中,内层事务的sql和外层事务的sql会在外层事务结束时进行提交或回滚。如果内层事务抛出异常e,在内层事务结束时,spring会把事务标记为“rollback-only”。这时如果外层事务捕捉了异常e,那么外层事务方法还会继续执行代码,直到外层事务也结束时,spring发现事务已经被标记为“rollback-only”,但方法却正常执行完毕了,这时spring就会抛出“org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only”。
代码示例如下:

Class ServiceA {
@Resource(name = "serviceB")
private ServiceB b; @Transactional
public void a() {
try {
b.b()
} catch (Exception ignore) {
}
}
} Class ServiceB {
@Transactional
public void b() {
throw new RuntimeException();
}
}

当调用a()时,就会报出“rollback-only”异常。

解决方案

  • 如果希望内层事务抛出异常时中断程序执行,直接在外层事务的catch代码块中抛出e.
  • 如果希望程序正常执行完毕,并且希望外层事务结束时全部提交,需要在内层事务中做异常捕获处理。
  • 如果希望内层事务回滚,但不影响外层事务提交,需要将内层事务的传播方式指定为PROPAGATION_NESTED。注:PROPAGATION_NESTED基于数据库savepoint实现的嵌套事务,外层事务的提交和回滚能够控制嵌内层事务,而内层事务报错时,可以返回原始savepoint,外层事务可以继续提交。

在我的项目中之所以会报“rollback-only”异常的根本原因是代码风格不一致的原因。外层事务对错误的处理方式是返回true或false来告诉上游执行结果,而内层事务是通过抛出异常来告诉上游(这里指外层事务)执行结果,这种差异就导致了“rollback-only”异常。虽然最后事务依然是回滚了,不影响程序对sql的处理,但外层事务的上游本期望返回true和false,却收到了UnexpectedRollbackException异常,(╯ ̄Д ̄)╯︵ ┻━┻。

附:事务传播方式

@see org.springframework.transaction.annotation.Propagation

事务传播方式 说明
PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是默认的传播方式
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行
PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常
PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

转自https://segmentfault.com/a/1190000016418596

UnexpectedRollbackException解决方案的更多相关文章

  1. 构建一个基本的前端自动化开发环境 —— 基于 Gulp 的前端集成解决方案(四)

    通过前面几节的准备工作,对于 npm / node / gulp 应该已经有了基本的认识,本节主要介绍如何构建一个基本的前端自动化开发环境. 下面将逐步构建一个可以自动编译 sass 文件.压缩 ja ...

  2. 常用 Gulp 插件汇总 —— 基于 Gulp 的前端集成解决方案(三)

    前两篇文章讨论了 Gulp 的安装部署及基本概念,借助于 Gulp 强大的 插件生态 可以完成很多常见的和不常见的任务.本文主要汇总常用的 Gulp 插件及其基本使用,需要读者对 Gulp 有一个基本 ...

  3. 干货来袭-整套完整安全的API接口解决方案

    在各种手机APP泛滥的现在,背后都有同样泛滥的API接口在支撑,其中鱼龙混杂,直接裸奔的WEB API大量存在,安全性令人堪优 在以前WEB API概念没有很普及的时候,都采用自已定义的接口和结构,对 ...

  4. 移动端IOS点击事件失效解决方案

    解决方案 解决办法有 4 种可供选择: 1 将 click 事件直接绑定到目标元素(即 .target)上 2 将目标元素换成 <a> 或者 button 等可点击的元素 3 将 clic ...

  5. 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一)

    相关连接导航 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一) 执行 $Gulp 时发生了什么 —— 基于 Gulp 的前端集成解决方案(二) 常用 Gulp 插件汇总 ...

  6. 执行 $Gulp 时发生了什么 —— 基于 Gulp 的前端集成解决方案(二)

    前言 文章 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一) 中,已经完成对 gulp 的安装,由于是window环境,文中特意提到了可以通过安装 gitbash 来代替 ...

  7. Paypal开发中遇到请求被中止: 未能创建 SSL/TLS 安全通道及解决方案

    最近在基于ASP.NET上开发了Paypal支付平台,在ASP.NET开发的过程中没有遇到这个问题,但是引用到MVC开发模式中的时候就出现了"未能创建 SSL/TLS 安全通道及解决方案&q ...

  8. inline-block元素间距问题的几种解决方案

    不知道大家有没有碰到过设置了display:inline-block;的几个相邻元素之间有几px间距的问题,这里提供几种简单实用的解决方法,希望能够帮到大家!   方法1. 将<li>标签 ...

  9. CRL快速开发框架系列教程十一(大数据分库分表解决方案)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

随机推荐

  1. 3道入门字典树例题,以及模板【HDU1251/HDU1305/HDU1671】

    HDU1251:http://acm.hdu.edu.cn/showproblem.php?pid=1251 题目大意:求得以该字符串为前缀的数目,注意输入格式就行了. #include<std ...

  2. 创建Sqoop作业,报错Exception in thread "main" java.lang.NoClassDefFoundError: org/json/JSONObject

    WARN tool.BaseSqoopTool: Setting your password on the command-line is insecure. Consider using -P in ...

  3. 小记--------sqoop的简单从mysql导入到hbase操作

    sqoop import -D sqoop.hbase.add.row.key=true                        //是否将rowkey相关字段列入列族中,默认为false :该 ...

  4. SQLite进阶-10.约束

    约束 约束是作用于数据表中列上的规则,用于限制表中数据的类型.约束的存在保证了数据库中数据的精确性和可靠性. 约束可以是列级或表级,列级约束作用于单一的列,而表级约束作用于整张数据表. SQLite中 ...

  5. MGR复制

    CentOS7 配置如下 5.7.22 Group ReplicationMySQL5.7.22安装略   在三台db服务器上面设置/etc/hosts映射,如下:192.168.1.101 mydb ...

  6. Springboot使用javaMail进行邮件发送

    导入相关依赖 <!--邮件发送--> <dependency> <groupId>javax.mail</groupId> <artifactId ...

  7. Java EE javax.servlet ServletContainerInitializer接口

    ServletContainerInitializer接口 public interface ServletContainerInitializer 一.介绍 该接口,允许在 web 应用程序的启动阶 ...

  8. 怎样禁止 html 中 <textarea> 标签可以拉伸改变大小 ?

    1. 一般来说, 使用 <textarea> 这个标签都会禁用掉它的这个默认属性, 不然可能会被用户玩儿坏, 而且也不利于其他元素的展示, 使用到的属性是 resize , 改为 none ...

  9. mybatis数组和集合的长度判断及插入

    1.在使用foreach的是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下4种情况: 如果传入的是单参数且参数类型是一个List的时候,collect ...

  10. MFC 下调试 出现 warning : fail to load indicator string 0x0069

    MFC 下调试 出现 warning : fail to load indicator string 0x0069 就是程序状态栏每一个标识列中至少有一个值没有初始值 或初始值为空 导致程序没有获取到 ...