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? 原因:事務(wù)提交多次 檢查代碼 例: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
?
我遇到這個異常出現(xiàn)的情況是這樣的: 配置事務(wù)攔截處理如下
? <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方法中去調(diào)一個load方法,因為事務(wù)屬性配置都為PROPAGATION_REQUIRED,
所以兩方法使用的是同一事務(wù),而load方法不執(zhí)行提交,(關(guān)于Spring聲明式事務(wù)管理源碼解讀之事務(wù)提交,可見
http://ahuaxuan.javaeye.com/blog/89072)如果load方法出現(xiàn)異常,則org.springframework.transaction.interceptor.
TransactionInterceptor的invoke處理后則會拋出異常.執(zhí)行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方法源碼如下:?? 根據(jù)TransactionInfo的事務(wù)屬性判斷處理的異常是否需要做
rollback處理,從來根據(jù)事務(wù)狀態(tài)來回滾事務(wù).
??? 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方法如下:
如果事務(wù)狀態(tài)是未完成的則拋出IllegalTransactionStateException,否則則根據(jù)此事務(wù)狀態(tài)處理事務(wù)回滾。
??? 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);
??? }
?
執(zhí)行processRollback方法,如果status有事務(wù),事務(wù)是部分失敗全局回滾。則執(zhí)行實現(xiàn)類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設(shè)置為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同屬于一個事務(wù),而load事務(wù)產(chǎn)生異常,修改了sessionHolder和connection
Holder的rollbackonly屬性為true.
而當(dāng)save執(zhí)行commit的時候,判斷事務(wù)狀態(tài)是否為全局回滾(就是判斷HibernateTransactionObject的sessionHolder
和connectionHolder是否為rollbackonly),此時的rollbackonly屬性則為true。處理完回滾后,繼續(xù)判斷事務(wù)狀態(tài)是否為
新事務(wù)(因為save方法啟用時是新建立的一個事務(wù),而load方法則是使用同一事務(wù)),所以則拋出了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
原來是這樣設(shè)置的:
?? ? ? ?<tx:attributes>
?? ? ? ? ? ?<tx:method name="*" read-only="true"/>
?? ? ? ?</tx:attributes>
發(fā)現(xiàn)selectA調(diào)用selectB,如果selectB拋出Exception,selectA中捕獲Exception但是并不繼續(xù)向外拋出,最后會出現(xiàn)錯誤。
?
Transaction rolled back because it has been marked as rollback-only
糾其原理其實很簡單,在selectB返回的時候,transaction被設(shè)置為rollback-only了,但是selectA正常消化掉,沒有繼續(xù)向外拋。
那么selectA結(jié)束的時候,transaction會執(zhí)commit操作,但是transaction已經(jīng)被設(shè)置為rollback-only了。
所以會出現(xiàn)這個錯誤。
有的同學(xué)說了,那不是沒得搞了,service不能拋出異常,或者不能攔截異常了?
其實不然,其實錯誤不在這里,而是select這種操作為什么要啟動事務(wù)呢?
調(diào)整好問題,找解決方案,問題就出現(xiàn)在propagation="REQUIRED"這個屬性上。
標(biāo)準(zhǔn)文檔上這樣寫:
| 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這樣的檢索操作根本就不啟動事務(wù)了,而且在有事務(wù)的方法中也是可以正常調(diào)用select方法的。
現(xiàn)在就沒問題了。
但是現(xiàn)在出現(xiàn)了另外一個問題,就是,如果在一個事物內(nèi)對db進行操作,然后在出事物之前對剛才db操作的數(shù)據(jù)進行select是獲取不到修改結(jié)果的,為什么呢?因為not——supported是會在執(zhí)行select之前掛起原有事物,不在原有事物內(nèi),當(dāng)然無法獲得修改后的數(shù)據(jù)。
怎么辦?改成supports:
?? ? ? ?<tx:attributes>
?? ? ? ? ? ?<tx:method name="*" read-only="true" propagation="SUPPORTS"/>
?? ? ? ?</tx:attributes>
這個狀態(tài)用一句話概括就是“有則加入事物,無也不創(chuàng)建事物”。
?
總結(jié)
以上是生活随笔為你收集整理的Transaction rolled back because it has been marked as rollback-only的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: hibernate 联合主键 merge
- 下一篇: hibernate异常:not-null