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 原因:事务提交多次 检查代码 例: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>
这个状态用一句话概括就是“有则加入事物,无也不创建事物”。
Transaction rolled back because it has been marked as rollback-only相关推荐
- Transaction rolled back because it has been marked as rollback-only分析解决方法
Transaction rolled back because it has been marked as rollback-only分析解决方法 参考文章: (1)Transaction rolle ...
- Spring事务 Transaction rolled back because it has been marked as rollback-only
前言 使用Spring事务时,出现异常:org.springframework.transaction.UnexpectedRollbackException: Transaction rolled ...
- “Transaction rolled back because it has been marked as rollback-only”
spring的声明事务提供了强大功能,让我们把业务关注和非业务关注的东西又分离开了.好东西的使用,总是需要有代价的.使用声明事务的时候,一 个不小心经常会碰到"Transaction rol ...
- 【Spring】21、用spring目标对象处理Transaction rolled back because it has been marked as rollback-only...
在使用spring做事务管理时,很多人都会遇到这样一段异常: org.springframework.transaction.UnexpectedRollbackException: Transact ...
- Transaction rolled back because it has been marked as rollback-only 原因 和解决方案
Transaction rolled back because it has been marked as rollback-only 原因 和解决方案 参考文章: (1)Transaction ro ...
- Spring嵌套事务异常Transaction rolled back because it has been marked as rollback-only
项目场景: 在循环里面使用try-catch去捕获异常的时候,并且try里面调的方法它也使用了事务注解 @transactional或者用了事务切面AOP去实现方法事务注入 问题描述 我这里的业务需求 ...
- 关于spring事务传播行为引发的Transaction rolled back because it has been marked as rollback-only
偶尔博客闲逛发现有人讨论这个问题(我自己没有遇到过),翻了几个帖子没有几个讲清楚的,自己测试下吧 测试类: package com.web.service;import com.StudyApplic ...
- Transaction has been rolled back because it has been marked as rollback
框架采用的是spring管理声明式事务,这几天业务开发时遇到了点麻烦,记录下备忘. 场景:在Service类中使用子事务(saveponit)时,当子事务抛出异常后,此异常处理掉不继续往外抛,spri ...
- UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
转载自:https://dongguabai.blog.csdn.net/article/details/114686998 PROPAGATION_REQUIRES_NEW:原有事务A新起事务B,事 ...
最新文章
- shell下的作业管理(转)
- NIPS2018 | 腾讯AI Lab入选20篇论文,含2篇Spotlight
- 程序员如何乘风破浪?从数据库历史看技术人发展 | CSDN 高校俱乐部
- Asp.net 用户控件和自定义控件注册
- 【BZOJ 1927】 [Sdoi2010]星际竞速
- 组策略(八)使用使用自定义RealVNC adm模板对域内工作站设置统一标准设置
- 【Spark篇】---SparkStream初始与应用
- 深入FFM原理与实践
- eof怎么结束输入_SimRobot算法社第二次活动圆满结束啦!
- Centos7.5-文件的归档和压缩
- python多行注释符号_python知识学习,python标识符和关键字
- 直接调用支持ACE的product search API
- 九九乘法表编程上三角python_java语言打印上三角和下三角,进一步得到九九乘法表...
- 吵架后女生和男生的夜晚!所有男生都这样吗?
- 教你玩转CSS 伪元素
- Green Plum测试报告
- 开心问答—首个基于迅雷链智能合约上执行的问答游戏
- 回溯法采用的搜索策略_下面哪种函数是回溯法中为避免无效搜索采取的策略( )...
- hazelcast 搭建_使用HazelCast实现Spring Config Server配置
- 嵌入式 linux 屏 翻转,linux下如何把屏幕设置成竖屏
热门文章
- SpaceX完成“星舰”空中悬停,距载人探火星还远吗?
- 198. House Robber
- 2015年第六届蓝桥杯 - 省赛 - C/C++大学A组 - G. 手链样式
- 1027:输出浮点数
- Linux(一) 概述 、 系统安装与分区
- 【C++】Visual Studio教程(七) -修改 Visual Studio
- 【ARM】ARM汇编程序设计(五) str和ldr
- 【Linux系统编程】线程基本操作
- android权限询问,Android:检查是否必须询问运行时权限
- python如何计算分子描述符_Python——描述符(descriptor)解密