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