【Spring】2一、用spring目標對象處理Transaction rolled back because it has been marked as rollback-only

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

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

package com.gigamore.platform.ac.service;  
  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Service;  
import org.springframework.transaction.annotation.Transactional;  
  
import com.gigamore.platform.base.service.impl.BaseServiceImpl;  
import com.onlyou.framework.exception.BusinessException;  
@Service  
public class TestAService extends BaseServiceImpl implements ITestAService{  
    @Autowired  
    private TestBService testBService;  
    @Transactional  
    public void testA(){  
        try{  
            testBService.testB();  
        }catch(BusinessException e){  
            logger.info(e.getMessage());  
        }catch(Exception e){  
            logger.info(e.getMessage());  
        }  
    }  
}  

TestBService:spa

package com.gigamore.platform.ac.service;  
  
import java.util.Date;  
  
import org.springframework.stereotype.Service;  
import org.springframework.transaction.annotation.Propagation;  
import org.springframework.transaction.annotation.Transactional;  
  
import com.gigamore.platform.ac.entity.LoanProjectEntity;  
import com.gigamore.platform.base.service.impl.BaseServiceImpl;  
import com.onlyou.framework.exception.BusinessException;  
@Service  
public class TestBService extends BaseServiceImpl{  
    @Transactional  
    public void testB(){  
        LoanProjectEntity project = this.selectByPrimaryKey(LoanProjectEntity.class, "2c9483e748321d4601485e1714d31412");  
        project.setUpdDataTm(new Date());  
        this.update(project);  
        throw new BusinessException("拋異常");  
    }  
}  

測試用例:code

@Autowired  
    private ITestAService testAService;  
    @Test  
    public void testA() {  
        testAService.testA();  
    }  

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

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

if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {  
            // Standard transaction demarcation with getTransaction and commit/rollback calls.  
            TransactionInfo txInfo = createTransactionIfNecessary(tm, 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.proceedWithInvocation();  
            }  
            catch (Throwable ex) {  
                // target invocation exception  
                completeTransactionAfterThrowing(txInfo, ex);  
                throw ex;  
            }  
            finally {  
                cleanupTransactionInfo(txInfo);  
            }  
            commitTransactionAfterReturning(txInfo);  
            return retVal;  
        }  

     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),確實能夠達到避免異常的效果。事務

相關文章
相關標籤/搜索