• 用spring目标对象处理Transaction rolled back because it has been marked as rollback-only


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

    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)

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

    ITestAService:

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

    TestAService:

     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:

     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 }

    测试用例:

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

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

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

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

    Spring中七种Propagation类的事务属性详解: 

    REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 

     SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。 

     MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。 

     REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。 

     NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 

     NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。 

     NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。 

    注意:这个配置将影响数据存储,必须根据情况选择

  • 相关阅读:
    史上最详细 github 使用教程(英文烂的血泪史)
    如何解决跨域问题
    KSImageNamed 安装
    VVDocumenter插件安装
    通过appearance设置app主题
    UITableViewCell注册情况
    iOS9.2 xcode 7.1.1真机测试
    UIAlertController iOS9
    Values of type 'NSInteger' should not be used as format arguments; add an explicit cast to 'long' instead
    GIT
  • 原文地址:https://www.cnblogs.com/chLxq/p/11887149.html
Copyright © 2020-2023  润新知