Transaction rolled back because it has been marked as rollback-only

2023-11-09

 http://hsyd.iteye.com/blog/586772

错误信息: Transaction rolled back because it has been marked as rollback-only  原因:事务提交多次 检查代码 例:service嵌套service

=============================================================================

http://blog.csdn.net/fzfeng/article/details/5355257

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

 

我遇到这个异常出现的情况是这样的: 配置事务拦截处理如下

  <bean id="txProxy" abstract="true"
    class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="transactionManager" ref="transactionManager"/>
    <property name="transactionAttributes">
      <props>
        <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
        <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
        <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
        <prop key="query*">PROPAGATION_REQUIRED,readOnly</prop>
        <prop key="is*">PROPAGATION_REQUIRED,readOnly</prop>
        <prop key="*">PROPAGATION_REQUIRED,-java.lang.Exception</prop>
      </props>
    </property>
  </bean>

如在一个save方法中去调一个load方法,因为事务属性配置都为PROPAGATION_REQUIRED,

所以两方法使用的是同一事务,而load方法不执行提交,(关于Spring声明式事务管理源码解读之事务提交,可见

http://ahuaxuan.javaeye.com/blog/89072)如果load方法出现异常,则org.springframework.transaction.interceptor.

TransactionInterceptor的invoke处理后则会抛出异常.执行completeTransactionAfterThrowing(txInfo, ex);

public Object invoke(final MethodInvocation invocation) throws Throwable {
        // Work out the target class: may be <code>null</code>.
        // The TransactionAttributeSource should be passed the target class
        // as well as the method, which may be from an interface.
        Class targetClass = (invocation.getThis() != null ? invocation.getThis().getClass() : null);

        // If the transaction attribute is null, the method is non-transactional.
        final TransactionAttribute txAttr =
                getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);
        final String joinpointIdentification = methodIdentification(invocation.getMethod());

        if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) {
            // Standard transaction demarcation with getTransaction and commit/rollback calls.
            TransactionInfo txInfo = createTransactionIfNecessary(txAttr, joinpointIdentification);
            Object retVal = null;
            try {
                // This is an around advice: Invoke the next interceptor in the chain.
                // This will normally result in a target object being invoked.
                retVal = invocation.proceed();
            }
            catch (Throwable ex) {
                // target invocation exception
                completeTransactionAfterThrowing(txInfo, ex);
                throw ex;
            }
            finally {
                cleanupTransactionInfo(txInfo);
            }
            commitTransactionAfterReturning(txInfo);
            return retVal;
        }

        else {
            // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
            try {
                Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr,
                        new TransactionCallback() {
                            public Object doInTransaction(TransactionStatus status) {
                                TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status);
                                try {
                                    return invocation.proceed();
                                }
                                catch (Throwable ex) {
                                    if (txAttr.rollbackOn(ex)) {
                                        // A RuntimeException: will lead to a rollback.
                                        if (ex instanceof RuntimeException) {
                                            throw (RuntimeException) ex;
                                        }
                                        else {
                                            throw new ThrowableHolderException(ex);
                                        }
                                    }
                                    else {
                                        // A normal return value: will lead to a commit.
                                        return new ThrowableHolder(ex);
                                    }
                                }
                                finally {
                                    cleanupTransactionInfo(txInfo);
                                }
                            }
                        });

                // Check result: It might indicate a Throwable to rethrow.
                if (result instanceof ThrowableHolder) {
                    throw ((ThrowableHolder) result).getThrowable();
                }
                else {
                    return result;
                }
            }
            catch (ThrowableHolderException ex) {
                throw ex.getCause();
            }
        }
    }

completeTransactionAfterThrowing方法源码如下:   根据TransactionInfo的事务属性判断处理的异常是否需要做

rollback处理,从来根据事务状态来回滚事务.

    protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
        if (txInfo != null && txInfo.hasTransaction()) {
            if (logger.isTraceEnabled()) {
                logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
                        "] after exception: " + ex);
            }
            if (txInfo.transactionAttribute.rollbackOn(ex)) {
                try {
                    getTransactionManager().rollback(txInfo.getTransactionStatus());
                }
                catch (TransactionSystemException ex2) {
                    logger.error("Application exception overridden by rollback exception", ex);
                    ex2.initApplicationException(ex);
                    throw ex2;
                }
                catch (RuntimeException ex2) {
                    logger.error("Application exception overridden by rollback exception", ex);
                    throw ex2;
                }
                catch (Error err) {
                    logger.error("Application exception overridden by rollback error", ex);
                    throw err;
                }
            }
            else {
                // We don't roll back on this exception.
                // Will still roll back if TransactionStatus.isRollbackOnly() is true.
                try {
                    getTransactionManager().commit(txInfo.getTransactionStatus());
                }
                catch (TransactionSystemException ex2) {
                    logger.error("Application exception overridden by commit exception", ex);
                    ex2.initApplicationException(ex);
                    throw ex2;
                }
                catch (RuntimeException ex2) {
                    logger.error("Application exception overridden by commit exception", ex);
                    throw ex2;
                }
                catch (Error err) {
                    logger.error("Application exception overridden by commit error", ex);
                    throw err;
                }
            }
        }
    }

 

org.springframework.transaction.support.AbstractPlatformTransactionManager的rollback方法如下:

如果事务状态是未完成的则抛出IllegalTransactionStateException,否则则根据此事务状态处理事务回滚。

    public final void rollback(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;
        processRollback(defStatus);
    }

 

执行processRollback方法,如果status有事务,事务是部分失败全局回滚。则执行实现类HibernateTransaction

Manager的doSetRollbackOnly()方法。

private void processRollback(DefaultTransactionStatus status) {
        try {
            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 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");
                }
            }
            catch (RuntimeException ex) {
                triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
                throw ex;
            }
            catch (Error err) {
                triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
                throw err;
            }
            triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
        }
        finally {
            cleanupAfterCompletion(status);
        }
    }

 

HibernateTransactionManager的doSetRollbackOnly()方法:将HibernateTransactionObject设置为rollbackonly.

    protected void doSetRollbackOnly(DefaultTransactionStatus status) {
        HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
        if (status.isDebug()) {
            logger.debug("Setting Hibernate transaction on Session [" +
                    SessionFactoryUtils.toString(txObject.getSessionHolder().getSession()) + "] rollback-only");
        }
        txObject.setRollbackOnly();
    }

 

        public void setRollbackOnly() {
            getSessionHolder().setRollbackOnly();
            if (hasConnectionHolder()) {
                getConnectionHolder().setRollbackOnly();
            }
        }

正如前面所说的, 两个方法:save,load同属于一个事务,而load事务产生异常,修改了sessionHolder和connection

Holder的rollbackonly属性为true.

而当save执行commit的时候,判断事务状态是否为全局回滚(就是判断HibernateTransactionObject的sessionHolder

和connectionHolder是否为rollbackonly),此时的rollbackonly属性则为true。处理完回滚后,继续判断事务状态是否为

新事务(因为save方法启用时是新建立的一个事务,而load方法则是使用同一事务),所以则抛出了UnexpectedRollbackException

    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);
            return;
        }
        if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
            if (defStatus.isDebug()) {
                logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
            }
            processRollback(defStatus);
            // Throw UnexpectedRollbackException only at outermost transaction boundary
            // or if explicitly asked to.
            if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
                throw new UnexpectedRollbackException(
                        "Transaction rolled back because it has been marked as rollback-only");
            }
            return;
        }

        processCommit(defStatus);
    }

 

====================

http://my.oschina.net/jing31/blog/10414

原来是这样设置的:

        <tx:attributes>

            <tx:method name="*" read-only="true"/>

        </tx:attributes>

发现selectA调用selectB,如果selectB抛出Exception,selectA中捕获Exception但是并不继续向外抛出,最后会出现错误。

 

Transaction rolled back because it has been marked as rollback-only

纠其原理其实很简单,在selectB返回的时候,transaction被设置为rollback-only了,但是selectA正常消化掉,没有继续向外抛。

那么selectA结束的时候,transaction会执commit操作,但是transaction已经被设置为rollback-only了。

所以会出现这个错误。

有的同学说了,那不是没得搞了,service不能抛出异常,或者不能拦截异常了?

其实不然,其实错误不在这里,而是select这种操作为什么要启动事务呢?

调整好问题,找解决方案,问题就出现在propagation="REQUIRED"这个属性上。

标准文档上这样写:

MANDATORY 
          Support a current transaction, throw an exception if none exists.
NESTED 
          Execute within a nested transaction if a current transaction exists, behave like PROPAGATION_REQUIRED else.
NEVER 
          Execute non-transactionally, throw an exception if a transaction exists.
NOT_SUPPORTED 
          Execute non-transactionally, suspend the current transaction if one exists.
REQUIRED 
          Support a current transaction, create a new one if none exists.
REQUIRES_NEW 
          Create a new transaction, suspend the current transaction if one exists.
SUPPORTS 
          Support a current transaction, execute non-transactionally if none exists.

 

看来我们需要如下修改:

        <tx:attributes>

            <tx:method name="*" read-only="true" propagation="NOT_SUPPORTED"/>

        </tx:attributes>

这样select这样的检索操作根本就不启动事务了,而且在有事务的方法中也是可以正常调用select方法的。

现在就没问题了。

但是现在出现了另外一个问题,就是,如果在一个事物内对db进行操作,然后在出事物之前对刚才db操作的数据进行select是获取不到修改结果的,为什么呢?因为not——supported是会在执行select之前挂起原有事物,不在原有事物内,当然无法获得修改后的数据。

怎么办?改成supports:

        <tx:attributes>

            <tx:method name="*" read-only="true" propagation="SUPPORTS"/>

        </tx:attributes>

这个状态用一句话概括就是“有则加入事物,无也不创建事物”。

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Transaction rolled back because it has been marked as rollback-only 的相关文章

随机推荐

  • 当在浏览器中输入一个域名后,会发生什么

    今天 去哪儿网Geely老师分享了他在校招时的一个面试题 当输www qunar com时发生了什么 真不是做广告哈 因为学的并没有很扎实 不能更深更广的分析这个问题 遂 百度一下 发现这篇文章并转载保存下来 同时也分享一下 希望大家可以更
  • vue-cli3.0打包时如何忽略某个第三方依赖包

    前几天接到了用vue重构某个业务模块的需求 常规开发不做讨论 但是在打包时需要排除业务中使用的某个第三方依赖包 代码如下 main js import Vue from vue import Cesium from cesium Cesiu
  • 解决Win7启动时出现“windows未能启动。原因可能是最近更改了硬件或软件”的问题

    搜索资料的时候发现个小问题 问题描述 在给ThinkPad T490做win7系统时 出现了 windows未能启动 原因可能是最近更改了硬件或软件 的问题 一直启动不来 在系统盘微PE中甚至还会出现 0x490 找不到元素 的问题 如何来
  • cuda安装笔记

    更新2023 3 4 我笔记本用vs编译后老是提示no kernel image is available for execution on the device 一直以为是驱动版本太高了或者cudatoolkit版本太高了 后来才知道是版
  • 【数据挖掘】知识点总结

    一 绪论 什么是数据挖掘 就是通过算法从大量的数据中搜索隐藏在其中的信息 数据挖掘的基本任务 聚类分析 异常检测 关联分析和预测建模 高维性和维灾难 随着维度的增加计算复杂度也随之增加 二 数据 不同的属性类型 标称 例如 邮政编码 定性数
  • 大数据应用——Linux常用的命令

    帮助命令 1 基本语法 help 命令 功能描述 获得shell内置命令的帮助信息 2 案例实操 1 查看cd命令的帮助信息 root hadoop01 help cd 常用快捷键 常用快捷键 功能 ctrl c 停止进程 ctrl l 清
  • Python(十五)读取Excel

    今天来实战演示如何封装读取Excel方法 第一步 准备一个Excel文件 sheet页命名为 login 学习技术交流群 704807680 第二步 编写代码找到Excel文件所在的文件夹路径 path os path dirname os
  • 机器学习算法——混淆矩阵(Confusion Matrix)之鸢尾花实例

    一 理论 什么是混淆矩阵 其实就是把所有类别的预测结果与真实结果按类别放置到了同一个表里 在这个表里我们可以清楚地看到每个类别正确识别的数量和错误识别的数量 混淆矩阵在什么情况下最好呢 答案是类别不平衡时 混淆矩阵是除了ROC曲线和AUC之
  • chatGPT高考作文

    百花齐放 春满人间 吹灭别人的灯 并不会让自己更加光明 阻挡别人的路 也不会让自己行得更远 这是一句古老而智慧的话语 告诉我们要尊重和包容他人 要与人和睦相处 要共同进步 同样的道理 也适用于文化的交流和发展 文化是一个民族的灵魂 是一个国
  • 华为OD机试真题-不爱施肥的小布

    题目描述 某农场主管理了一大片果园 fields i 表示不同果林的面积 单位 m 2 现在要为所有的果林施肥且必须在n天之内完成 否则影响收成 小布是果林的工作人员 他每次选择一片果林进行施肥 且一片果林施肥完后当天不再进行施肥作业 假设
  • C++复合类型

    1 数组 数组声明应该指出元素类型 数组名和元素个数 数组的初始化需要遵循以下规则 a 只有定义数组时才能使用初始化 此后就不能使用了 也不能将一个数组赋给另一个数组 int narray1 4 1 2 3 4 int narray2 4
  • 操作系统 实验二 银行家算法

    题目描述 已知进程 P0 P1 P2 P3 P4 有三类系统资源A B C的数量分别为10 5 7 在T0时刻的资源分配情况如下图所示 1 若进程P1请求资源 发出请求向量Request1 1 0 2 编写程序用银行家算法判断系统能否将资源
  • 【javascript-基础小练习】角度转弧度,已知角度90,转成弧度?

    角转弧度的公式为 jiao 180 pai hu 假设pai的值为 3 14
  • 数据库的事务及变量声明方法

    事务 简单来说事务就是为了保持数据一致性的一种手段 在事务中的sql语句作为一个整体一起向系统提交 要么都执行 要么都不执行 可以回滚到原来的状态 事务的语法 开始事务 begin tran 或者transaction 提交事务 commi
  • Spring为什么不推荐你使用@Autowired ?

    Spring为什么不推荐你使用 Autowired 我们总能发现当使用IDEA写代码的时候 Autowired注解会报黄 我们把鼠标悬停在上面 可以看到这个如下图所示的警告信息 当我们按住alt 回车键 idea就会帮我们修改成这样 我向来
  • 微服务架构

    3 注册中心与服务发现 前言 1 服务发现基础知识 1 1 注册中心与服务发现的联系 1 2 使用 DNS 与负载均衡器发现服务的弊端 1 3 云中的服务发现应该具备的特点 1 4 服务发现架构 1 5 服务治理的概念 1 6 服务注册的概
  • Linux服务器(centos7)中Word转换PDF,文档出现中文乱码或方格【亲测可用,已解决】

    提示 在centos服务器使用aspose word转换word文件为pdf的时候只有中文乱码或则方格 但是在win服务器上使用可以正常转换 本次文章主要解决字体缺失问题 目录 前言 一 在linux服务器上生成的pdf都是这种格式的 二
  • 信号和槽函数的扩展

    信号和槽函数的扩展 一个信号连接多个槽函数 一个槽函数连接多个信号 信号连接信号 一个信号可以连接多个槽函数 发送一个信号有多个处理动作 需要写多个connect 连接 槽函数的执行顺序和信号的发射顺序相同 QT5中 信号的接收者可以是一个
  • C++primer总结

    目录 第一章 数据处理 2 整形 2 第二章 复合类型 3 数组 3 字符串 4 枚举 5 指针 5 第三章 函数的使用 6 内联函数 6 默认参数 6 函数的重载 7 函数的模板 7 第四章 内存模型和名称空间 7 文件的单独存放 7 变
  • Transaction rolled back because it has been marked as rollback-only

    http hsyd iteye com blog 586772 错误信息 Transaction rolled back because it has been marked as rollback only 原因 事务提交多次 检查代码