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的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: hibernate 联合主键 merge
- 下一篇: hibernate异常:not-null