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

Des*_*ess 17 java spring hibernate transactions rollback

这是我的问题:

我正在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) {
     // ...
   }
   // ...
 }
}
Run Code Online (Sandbox Code Playgroud)

在执行继续时捕获异常,但在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)
Run Code Online (Sandbox Code Playgroud)

什么时候method2不抛出这个例外,它运作良好.

我尝试过的:

  • 设置@Transactional(noRollbackFor={UserException.class}))method1
  • 试试并接受 method2

但它没有改变任何东西.

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

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

更新

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

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

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

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

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

JB *_*zet 36

默认情况下,Spring事务通过使用处理事务和异常的代理包装Spring bean来工作.当你打电话method2()method1(),你完全绕过了这个代理,因此无法启动新的交易,并且你实际上method2()是通过与通过该呼叫打开的交易相同的交易进行呼叫method1().

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

这在文档中描述.

  • 请注意,如果需要这些自调用,则应在Spring中使用AspectJ模式.请参阅http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html:"如果您期望自我调用,请考虑使用AspectJ模式(请参阅下表中的mode属性)在这种情况下,首先不会有代理;相反,目标类将被编织(即,它的字节代码将被修改),以便将@Transactional转换为运行时任何一种方法的行为." (2认同)