问题描述

事务设置手动回滚:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()

代码需要返回比较友好的提示,但try…catch了,事务就不会回滚了,所以在catch中设置手动回滚,但每次执行完就抛出异常

2023-01-08 13:37:51.790 ERROR 5192 --- [nio-8880-exec-1] o.a.c.c.C.[.[.[.[dispatcherServlet]      : Servlet.service() for servlet [dispatcherServlet] in context with path [/ceshi] threw exception [Request processing failed; nested exception is org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only] with root cause

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:870) ~[spring-tx-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:707) ~[spring-tx-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:633) ~[spring-tx-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:386) ~[spring-tx-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) ~[spring-tx-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at cn.lw.service.impl.ShiwuServiceImpl$$EnhancerBySpringCGLIB$$33a86b07.insertRobllBack(<generated>) ~[classes/:na]

原因分析:

异常示例代码如下:

@Transactional
public String insertRobllBack(ForlanA forlanA, ForlanB forlanB) {
return forlanaService.insertForlanA(forlanA);
} @Transactional
public String insertForlanA(ForlanA forlanA) {
try {
forlanBService.insertForlanB(new ForlanB());
} catch (Exception e) {
e.printStackTrace();
// 为了支持特定异常,设置手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return "特定异常结果";
}
return "成功";
} @Transactional
public void insertForlanB(ForlanB forlanB) {
forlanBDao.insert(forlanB);
int res = 1 / 0; //java.lang.ArithmeticException: / by zero
}

分析核心源码AbstractPlatformTransactionManager

@Override
public final void commit(TransactionStatus status) throws TransactionException {
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
} DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
processRollback(defStatus, false);
return;
} if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
processRollback(defStatus, true);
return;
} processCommit(defStatus);
} private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected; try {
triggerBeforeCompletion(status); if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
status.rollbackToHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
doRollback(status);
}
else {
// Participating in larger transaction
if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
doSetRollbackOnly(status);
}
else {
if (status.isDebug()) {
logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
}
}
}
else {
logger.debug("Should roll back transaction but cannot - no transaction available");
}
// Unexpected rollback only matters here if we're asked to fail early
if (!isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = false;
}
}
}
catch (RuntimeException | Error ex) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
} triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK); // Raise UnexpectedRollbackException if we had a global rollback-only marker
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
}
finally {
cleanupAfterCompletion(status);
}
}

通过源码,我们发现如果有全局回滚标记,还进行commit的情况下,则引发UnexpectedRollbackException

if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
processRollback(defStatus, true);
return;
} // Raise UnexpectedRollbackException if we had a global rollback-only marker
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}

If a participating transaction (e.g. with PROPAGATION_REQUIRED or PROPAGATION_SUPPORTS encountering an existing transaction) fails, the transaction will be globally marked as rollback-only

原因:就是我们内部事务已经抛出异常了,此时,事务已经被标记为rollback-only,最外层事务commit的话,这就有问题了


解决方案:

1、修改内层事务传播特性为@Transactional(propagation = Propagation.REQUIRES_NEW) ,这样内层和外层分别使用了不同事务,就不影响了
2、try…catch写到最外层事务,但是记得在catch设置手动回滚,不然还是存在上面问题

3、不要try…catch,异常直接抛到最外层

@Transactional事务回滚异常:Transaction rolled back because it has been marked as rollback-only的更多相关文章

  1. Spring中@Transactional事务回滚

    转载: Spring中@Transactional事务回滚 一.使用场景举例 在了解@Transactional怎么用之前我们必须要先知道@Transactional有什么用.下面举个栗子:比如一个部 ...

  2. Spring @Transactional ——事务回滚

    工作原理运行配置@Transactional注解的测试类的时候,具体会发生如下步骤1)事务开始时,通过AOP机制,生成一个代理connection对象,并将其放入DataSource实例的某个与Dat ...

  3. Spring中@Transactional事务回滚实例及源码

    一.使用场景举例 在了解@Transactional怎么用之前我们必须要先知道@Transactional有什么用.下面举个栗子:比如一个部门里面有很多成员,这两者分别保存在部门表和成员表里面,在删除 ...

  4. Spring中@Transactional事务回滚(含实例详细讲解,附源码)

    一.使用场景举例 在了解@Transactional怎么用之前我们必须要先知道@Transactional有什么用.下面举个栗子:比如一个部门里面有很多成员,这两者分别保存在部门表和成员表里面,在删除 ...

  5. Spring中@Transactional事务回滚(含实例具体解说,附源代码)

    一.使用场景举例 在了解@Transactional怎么用之前我们必须要先知道@Transactional有什么用. 以下举个栗子:比方一个部门里面有非常多成员,这两者分别保存在部门表和成员表里面,在 ...

  6. Spring事务回滚和异常类

    1.异常的一些基本知识 异常的架构 异常的继承结构:Throwable为基类,Error和Exception继承Throwable.Error和RuntimeException及其子类成为未检查异常( ...

  7. @Transactional 的回滚

    默认情况下,Exception是不会引起回滚操作的,RuntimeException才会引起回滚操作. 当然如果所有的Exception都要回滚的话,直接@Transactional(rollback ...

  8. Try-Catch包裹的代码异常后,竟然导致了产线事务回滚!

    导读:​一段被try-catch包裹后的代码在产线稳定运行了200天后忽然发生了异常,而这个异常竟然导致了产线事务回滚.这期间究竟发生了什么?日常在项目过程中该如何避免事务异常?就在这个时候,老板拿着 ...

  9. (转)spring异常抛出触发事务回滚策略

    背景:在面试时候问到事务方法在调用过程中出现异常,是否会传递的问题,平时接触的比较少,有些懵逼. spring异常抛出触发事务回滚策略 Spring.EJB的声明式事务默认情况下都是在抛出unchec ...

  10. spring @Transaction事务回滚失败

    今天客户提出一个新问题,出库一批商品,提示失败了,但是库存数量却减少了.看了一下代码一头雾水,我们的代码加了事物,且捕获异常. 经过调试代码发现就是两个原因导致的 第一.在当前方法的catch中处理了 ...

随机推荐

  1. 支持JDK19虚拟线程的web框架,之一:体验

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 关于虚拟线程 随着JDK19 GA版本的发布,虚拟线程 ...

  2. 驱动开发:内核监视LoadImage映像回调

    在笔者上一篇文章<驱动开发:内核注册并监控对象回调>介绍了如何运用ObRegisterCallbacks注册进程与线程回调,并通过该回调实现了拦截指定进行运行的效果,本章LyShark将带 ...

  3. 用昇腾AI护航“井下安全”

    摘要:基于CANN(异构计算架构)打造的"智能矿山安全生产管理平台",能够更便捷和更高效地服务于更多矿山安全生产建设. 本文分享自华为云社区<华为携手云话科技助力矿山智能化, ...

  4. Hugging Face发布diffuser模型AI绘画库初尝鲜!

    作者:韩信子@ShowMeAI 深度学习实战系列:https://www.showmeai.tech/tutorials/42 TensorFlow 实战系列:https://www.showmeai ...

  5. 十、RHEL Podman命令

    Podman介绍 Podman 是一个开源的容器运行时项目,可在大多数 Linux 平台上使用.Podman 提供与 Docker 非常相似的功能.正如前面提到的那样,它不需要在你的系统上运行任何守护 ...

  6. ML-决策树

    信息增益 香农熵: 指混乱程度,越混乱,值越大 信息增益(information gain): 在划分数据集前后信息发生的变化称为信息增益(香农熵的差) 基尼不纯度也可度量集合的无序程度 香农熵的计算 ...

  7. TensorFlow?PyTorch?Paddle?AI工具库生态之争:ONNX将一统天下

    作者:韩信子@ShowMeAI 深度学习实战系列:https://www.showmeai.tech/tutorials/42 本文地址:https://www.showmeai.tech/artic ...

  8. Spring Boot 应用的热部署配置

    前言 所谓热部署,简单来说,就是代码修改后不需重启项目就可自动加载出新的内容. 注意:热部署在 debug 调试模式下才生效! IDEA 配置 在 IDE(IDEA)中开启相关项目自动构建选项 开启编 ...

  9. threejs三维地图大屏项目分享

    这是最近公司的一个项目.客户的需求是基于总公司和子公司的数据,开发一个数据展示大屏. 大屏两边都是一些图表展示数据,中间部分是一个三维中国地图,点击中国地图的某个省份,可以下钻到省份地图的展示. 地图 ...

  10. 2022春每日一题:Day 34

    题目:lowbit求和 (没有找到哪个公开题库有这个题) 题意:求数组中任意一对数的异或和的lowbit的总和. 对于异或,二进制位中两个数相等则为0,反之为1,而且此题是要求lowbit,那我们利用 ...