@Transactional(propagation=Propagation.REQUIRES_NEW)的行为异常

20

这是我的问题:

我在一个Java EE/Spring/Hibernate应用程序上运行批处理。此批处理调用method1。此方法调用method2,该方法可能会抛出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) {
     // ...
   }
   // ...
 }
}

当事务结束并关闭时,异常会被捕获,但在方法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不抛出异常时,它能正常工作。
我尝试过:
- 在method1上设置@Transactional(noRollbackFor={UserException.class})) - 在method2中使用try和catch
但这些都没有改变什么。
由于异常是在另一个事务中抛出且已回滚,所以我不明白为什么它不能正常工作。我查看了这个问题:Jpa transaction javax.persistence.RollbackException: Transaction marked as rollbackOnly,但它并没有真正帮助我解决问题。
我非常感激如果有人能给我一些提示。
更新:
通过在由method2调用的方法上设置propagation=Propagation.REQUIRES_NEW,我使其工作起来了(实际上是发送异常的那个方法)。这个方法定义在一个与我的BatchService非常相似的类中。所以我不明白为什么它在这个级别上可以工作,而在method2中不能。
我将method2设置为public,因为根据文档,在方法是private时,注释@Transactional不起作用:
“@Transactional注释可以放置在接口定义之前,在接口上的方法之前,在类定义之前或在类上的public方法之前。”
我还尝试使用Exception而不是RuntimeException(因为它更适合),但它也没有改变任何东西。
即使它正在工作,问题仍然存在,因为它有一种奇怪的行为,我想了解为什么它不能像应该那样工作。

请参考 https://dev59.com/XG435IYBdhLWcg3w4Uas 以获取可能的解决方法。 - Vadzim
1个回答

42

Spring事务默认通过将Spring bean包装在代理中来处理事务和异常。当从method1()调用method2()时,完全绕过了此代理,因此无法启动新事务,并且实际上是从由对method1()调用打开的事务中调用method2()

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

这在文档中有描述。


非常完美的答案,真正理解了发生的事情,谢谢! - DessDess
2
请注意,如果您需要这些自我调用起作用,则应在Spring中使用AspectJ模式。请参见http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html:“如果您希望将自我调用也包装在事务中,请考虑使用AspectJ模式(请参见下表中的模式属性)。在这种情况下,首先不会有代理;相反,目标类将被编织(即,其字节码将被修改),以便在任何类型的方法上将@Transactional转换为运行时行为。” - Wim Deblauwe
是的,真正理解Spring事务...谢谢 - Vito

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接