調用一個事務方法時如何顯式指定爲其開啓一個新事務

@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...

相關文章
相關標籤/搜索