调用一个事务方法时如何显式指定为其开启一个新事务

@Transactional
public void foo(){
    doSomthing();
    try{
        bar();
    }catch(ApiException e){
        logger.warn("call bar failed",e);
        // do something recovery work here
    }
    doSomethingElse();
    
}
@Transactional
public void bar(){
    // ...
    if(meetSomeCondition){
        throw new ApiException(...);
    }
    // ...
}

发现bar方法抛出了异常 即便foo中捕捉了该异常 仍致使foo方法最后回滚 而且还抛出以下的异常html

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:720)

事故缘由java

However, in the case where an inner transaction scope sets the rollback-only marker, the outer transaction has not decided on the rollback itself, and so the rollback (silently triggered by the inner transaction scope) is unexpected. A corresponding UnexpectedRollbackException is thrown at that point. This is expected behavior so that the caller of a transaction can never be misled to assume that a commit was performed when it really was not. So if an inner transaction (of which the outer caller is not aware) silently marks a transaction as rollback-only, the outer caller still calls commit. The outer caller needs to receive an UnexpectedRollbackException to indicate clearly that a rollback was performed instead.spring

摘自: http://docs.spring.io/spring/...编程

发觉还真很差弄 ide

code

  • 没有办法经过下面的方法来禁止回滚orm

@Transactional(noRollbackFor=ApiException.class)

由于若是是在foo方法中抛出的ApiException的话 仍是须要回滚的htm

  • 在bar中显式指定事务传播方式 如事务

@Transactional(propagation=Propagation.REQUIRES_NEW)
public void bar()

即每次调用bar方法都会建立一个新事务 如foo方法建立了一个事务A 当调用bar方法时 会为其建立一个新的独立事务B 若是bar抛出异常 只会回滚B事务 不影响foo方法已有的事务Aci

但发觉也不可行 或许其余业务场景中 bar失败了就须要所有回滚呢?

因此最终仍是决定新建一个方法 不含事务注解

public void anotherBarWithoutTransaction(){
    // ...
    if(meetSomeCondition){
        throw new ApiException(...);
    }
    // ...
}

@Transactional
public void bar(){ // 只是加了事务注解 
    anotherBarWithoutTransaction();
}

这样的话 若是有场景须要调用bar方法 但bar失败了的话不用会滚 能够直接调用anotherBarWithoutTransaction

但也不能为每一个事务方法都准备一个不含事务的版本吧? 有没更好的解决方法呢?

还有一种解决方法

在foo中调用bar时使用编程式事务方式显式指定开启一个新事务 以下所示

@Autowired
private TransactionTemplate transactionTemplate;

transactionTemplate.execute(new TransactionCallbackWithoutResult() {
   protected void doInTransactionWithoutResult(TransactionStatus status) {
       try {
           barService.bar();
       } catch (ApiException e) {
           logger.warn("call bar failed",e);
           status.setRollbackOnly(); // 注意必定要有此代码 不然仍会回滚外部事务
       }
   }
});

同时须要在spring配置文件中显式配置transactionTemplate

<bean id="transactionTemplate"
          class="org.springframework.transaction.support.TransactionTemplate">
        <property name="propagationBehaviorName" value="PROPAGATION_REQUIRES_NEW"/>
        <property name="transactionManager" ref="txManager"/>
    </bean>

以为这种方式优于为每一个事务方法添加两个版本 如barWithTransactional和barWithoutTransactional

参考文档

http://docs.spring.io/autorep...

http://stackoverflow.com/ques...

相关文章
相关标签/搜索