@Transactional的奇怪行为(propagation = Propagation.REQUIRES_NEW)

这是我的问题:

我正在Java EE / Spring / Hibernate应用程序上运行批处理。 该批次调用method1 。 此方法调用一个可以抛出UserException (一个扩展RuntimeException的类)。 它是这样的:

 @Transactional public class BatchService implements IBatchService { @Transactional(propagation=Propagation.REQUIRES_NEW) public User method2(User user) { // Processing, which can throw a RuntimeException } public void method1() { // ... try { this.method2(user); } catch (UserException e) { // ... } // ... } } 

在执行继续时捕获exception,但在事务关闭时在method1结束时抛出RollbackException。

这是堆栈跟踪:

 org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:476) at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754) at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723) at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) at $Proxy128.method1(Unknown Source) at batch.BatchController.method1(BatchController.java:202) 

method2没有抛出此exception时,它运行良好。

我试过的:

  • method1上设置@Transactional(noRollbackFor={UserException.class}))
  • 尝试并捕获method2

但它没有改变任何东西。

由于在发生回滚的不同事务中抛出exception,我不明白为什么它不起作用。 我看看这个: Jpa事务javax.persistence.RollbackException:事务标记为rollbackOnly但它并没有真正帮助我。

如果有人能给我一些线索,我会非常感激的。

更新

我通过在method2调用的方法(实际上是发送exception的方法)上设置propagation=Propagation.REQUIRES_NEW来使其工作。 此方法在与我的BatchService非常相似的类中定义。 所以我不明白为什么它在这个级别上工作而不是在method2

  • 我已将method2设置为public,因为如果方法是私有的,则不考虑注释@Transactional ,如文档中所述:

@Transactional注释可以放在接口定义,接口上的方法,类定义或类的公共方法之前。

  • 我也尝试使用Exception而不是RuntimeException (因为它更合适),但它也没有改变任何东西。

即使它正在工作,问题仍然是开放的,因为它有一个奇怪的行为,我想理解为什么它不应该像它应该的那样。

默认情况下,Spring事务通过使用处理事务和exception的代理包装Spring bean来工作。 当你从method1()调用method2() ,你完全绕过了这个代理,所以它无法启动一个新事务,并且你有效地从与调用打开的事务相同的事务中调用method2()method1()

相反,当你从method1()调用另一个注入bean的方法时,你实际上是在事务代理上调用一个方法。 因此,如果此外来方法标记为REQUIRES_NEW,则代理将启动新事务,并且您可以捕获method1()的exception并恢复外部事务。

这在文档中描述。