@Transactional(propagation=Propagation.REQUIRES_NEW) 的奇怪行为 [英] Strange behaviour with @Transactional(propagation=Propagation.REQUIRES_NEW)

查看:67
本文介绍了@Transactional(propagation=Propagation.REQUIRES_NEW) 的奇怪行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的问题:

我正在 Java EE/Spring/Hibernate 应用程序上运行批处理.此批处理调用 method1.这个方法调用一个 method2,它可以抛出 UserException(一个继承 RuntimeException 的类).这是它的样子:

I'm running a batch on a Java EE/Spring/Hibernate application. This batch calls a method1. This method calls a method2 which can throw UserException (a class extending RuntimeException). Here is how it looks like :

@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.

The exception is catched as the execution continues, but at the end of method1 when the transaction is closed a RollbackException is thrown.

这是堆栈跟踪:

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 没有抛出这个异常时,它运行良好.

When method2 is not throwing this exception, it works well.

我尝试过的:

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

但它没有改变任何东西.

But it didn't change anything.

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

As the exception is thrown in a different transaction where a rollback happened I don't understand why it doesn't work. I had a look at this : Jpa transaction javax.persistence.RollbackException: Transaction marked as rollbackOnly but it didn't really help me.

如果有人能给我一个线索,我将非常感激.

I will be very greatful if someone could give me a clue.

更新

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

I've made it work by setting propagation=Propagation.REQUIRES_NEW on the method called by method2 (which is actually the one which is sending the exception). This method is defined in a class very similar to my BatchService. So I don't see why it works on this level and not on method2.

  • 我已将 method2 设置为公开,因为如果文档中所述方法是私有的,则不考虑注释 @Transactional :
  • I've set method2 as public as the annotation @Transactional is not taken into account if the method is private as said in the documentation :

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

The @Transactional annotation may be placed before an interface definition, a method on an interface, a class definition, or a public method on a class.

  • 我也尝试使用 Exception 而不是 RuntimeException(因为它更合适)但它也没有改变任何东西.
    • I also tried to use Exception instead of RuntimeException (as it is more appropriate) but it also didn't change anything.
    • 即使它在工作,问题仍然悬而未决,因为它有一种奇怪的行为,我想了解为什么它没有表现得像它应该的那样.

      Even if it is working the question remains open as it has a strange behaviour and I would like to understand why it's not acting like it should be.

      推荐答案

      默认情况下,Spring 事务通过使用处理事务和异常的代理包装 Spring bean 来工作.当您从 method1() 调用 method2() 时,您完全绕过了此代理,因此它无法启动新事务,并且您实际上是在调用 method2() 来自与调用 method1() 所打开的交易相同的交易.

      Spring transactions, by default, work by wrapping the Spring bean with a proxy which handles the transaction and the exceptions. When you call method2() from method1(), you're completely bypassing this proxy, so it can't start a new transaction, and you're effectively calling method2() from the same transaction as the one opened by the call to method1().

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

      On the contrary, when you call a method of another injected bean from method1(), you're in fact calling a method on a transactional proxy. So if this alien method is marked with REQUIRES_NEW, a new transaction is started by the proxy, and you're able to catch the exception in method1() and resume the outer transaction.

      这在文档中有所描述.

      这篇关于@Transactional(propagation=Propagation.REQUIRES_NEW) 的奇怪行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆