用spring目標對象處理Transaction rolled back because it has been marked as rollback-only

在使用spring作事務管理時,不少人都會遇到這樣一段異常:java

1 org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only   
2 at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:718)   
3 at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:475)   
4 at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:270) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)   
5 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96)   
6 at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260)   
7 at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)

出現上面問題的場景相似下面代碼這樣:spring

ITestAService:測試

package com.gigamore.platform.ac.service;  
import com.onlyou.framework.exception.BusinessException;  
public interface ITestAService {      
    void testA() throws BusinessException;  
} 

TestAService:this

 1 package com.gigamore.platform.ac.service;  
 2   
 3 import org.springframework.beans.factory.annotation.Autowired;  
 4 import org.springframework.stereotype.Service;  
 5 import org.springframework.transaction.annotation.Transactional;  
 6   
 7 import com.gigamore.platform.base.service.impl.BaseServiceImpl;  
 8 import com.onlyou.framework.exception.BusinessException;  
 9 @Service  
10 public class TestAService extends BaseServiceImpl implements ITestAService{  
11     @Autowired  
12     private TestBService testBService;  
13     @Transactional  
14     public void testA(){  
15         try{  
16             testBService.testB();  
17         }catch(BusinessException e){  
18             logger.info(e.getMessage());  
19         }catch(Exception e){  
20             logger.info(e.getMessage());  
21         }  
22     }  
23 }

TestBService:spa

 1 package com.gigamore.platform.ac.service;  
 2   
 3 import java.util.Date;  
 4   
 5 import org.springframework.stereotype.Service;  
 6 import org.springframework.transaction.annotation.Propagation;  
 7 import org.springframework.transaction.annotation.Transactional;  
 8   
 9 import com.gigamore.platform.ac.entity.LoanProjectEntity;  
10 import com.gigamore.platform.base.service.impl.BaseServiceImpl;  
11 import com.onlyou.framework.exception.BusinessException;  
12 @Service  
13 public class TestBService extends BaseServiceImpl{  
14     @Transactional  
15     public void testB(){  
16         LoanProjectEntity project = this.selectByPrimaryKey(LoanProjectEntity.class, "2c9483e748321d4601485e1714d31412");  
17         project.setUpdDataTm(new Date());  
18         this.update(project);  
19         throw new BusinessException("拋異常");  
20     }  
21 }

測試用例:code

1 @Autowired  
2     private ITestAService testAService;  
3     @Test  
4     public void testA() {  
5         testAService.testA();  
6     }

testAService調用testBService的testB()方法,testB()方法裏拋了一個BusinessException異常,可是testAService用try{}catch{}捕獲異常並不往上層拋了。orm

看起來好像沒什麼問題,異常被捕獲了。其實否則,在testAService調用testBService的testB()方法時,會通過一次spring事務控制切面,事務切面裏自己會對testBService的testB()方法進行異常捕獲: TransactionAspectSupport.invokeWithinTransaction對象

 1 if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {  
 2             // Standard transaction demarcation with getTransaction and commit/rollback calls.  
 3             TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);  
 4             Object retVal = null;  
 5             try {  
 6                 // This is an around advice: Invoke the next interceptor in the chain.  
 7                 // This will normally result in a target object being invoked.  
 8                 retVal = invocation.proceedWithInvocation();  
 9             }  
10             catch (Throwable ex) {  
11                 // target invocation exception  
12                 completeTransactionAfterThrowing(txInfo, ex);  
13                 throw ex;  
14             }  
15             finally {  
16                 cleanupTransactionInfo(txInfo);  
17             }  
18             commitTransactionAfterReturning(txInfo);  
19             return retVal;  
20         }

completeTransactionAfterThrowing(txInfo, ex)裏面作了txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()),事務管理器作rollback, 把事務設置成rollback-only。 以上是testBService外層包裝的事務切面作的事情。當testAService的testA()方法執行完,此時執行到testAService外層包裝的事務切面,因爲testA()方法執行過程沒有拋出異常,因此事務正常提交,即執行的是commitTransactionAfterReturning(txInfo),事務對象txInfo對應的事務管理器進行提交事務,但事務已被設置爲rollback-only,故spring對外拋出了Transaction rolled back because it has been marked as rollback-only異常。blog

解決辦法:把TestBService的testB()方法的事務註解改爲@Transactional(propagation = Propagation.NESTED),確實能夠達到避免異常的效果。事務

 

 

Spring中七種Propagation類的事務屬性詳解: 

REQUIRED:支持當前事務,若是當前沒有事務,就新建一個事務。這是最多見的選擇。 

 SUPPORTS:支持當前事務,若是當前沒有事務,就以非事務方式執行。 

 MANDATORY:支持當前事務,若是當前沒有事務,就拋出異常。 

 REQUIRES_NEW:新建事務,若是當前存在事務,把當前事務掛起。 

 NOT_SUPPORTED:以非事務方式執行操做,若是當前存在事務,就把當前事務掛起。 

 NEVER:以非事務方式執行,若是當前存在事務,則拋出異常。 

 NESTED:支持當前事務,若是當前事務存在,則執行一個嵌套事務,若是當前沒有事務,就新建一個事務。 

注意:這個配置將影響數據存儲,必須根據狀況選擇

相關文章
相關標籤/搜索