【Spring】21、用spring目标对象处理Transaction rolled back because it has been marked as rollback-only

来源:互联网 发布:qq文件夹删除数据恢复 编辑:程序博客网 时间:2024/06/09 23:12

 在使用spring做事务管理时,很多人都会遇到这样一段异常:

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)   

出现上面问题的场景类似下面代码这样:

ITestAService:

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

TestAService:

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:

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("抛异常");      }  }  

测试用例:

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

testAService调用testBService的testB()方法,testB()方法里抛了一个BusinessException异常,但是testAService用try{}catch{}捕获异常并不往上层抛了。

看起来好像没什么问题,异常被捕获了。其实不然,在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异常。

解决办法:把TestBService的testB()方法的事务注解改成@Transactional(propagation = Propagation.NESTED),确实可以达到避免异常的效果。

阅读全文
0 0