@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并恢复外部事务。
这在文档中描述。