@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
参考文档