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

问题描述

这是我的问题:



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

  @Transactional 
公共类BatchService实现IBatchService {
@Transactional(传播= Propagation.REQUIRES_NEW)
public用户方法2(用户用户){
//处理,可以抛出RuntimeException
}

public void method1(){
// ...
尝试{
this.method2(user);
} catch(UserException e){
// ...
}
// ...
}
}
code>

在执行过程中捕获异常,但在 method1 当事务关闭时抛出RollbackException。



以下是堆栈跟踪:

  org.springframework.transaction.TransactionSystemException:无法提交JPA事务;嵌套异常是javax.persistence.RollbackException:在org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:476)处标记为rollbackOnly 
的事务在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)
在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)

当<$ c

我尝试过: @Transactional(noRollbackFor = {UserException.class}))) on <$ c <$ c


  • code> method1

  • 尝试并捕获 method2
  • ul>

    但它没有改变任何东西。



    由于异常是在发生回滚的另一个事务中引发的,不明白为什么它不起作用。我看了一下:,但它并没有真正帮助我。



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



    更新



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




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


      blockquote

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




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



      即使工作正常,问题仍然存在,因为它有一个奇怪的行为,我想知道为什么它的行为并不像它应该的那样。 解决方案

默认情况下,Spring事务通过用一个prox y处理交易和异常。当你从 method1()调用 method2()时,你完全绕过了这个代理,所以它不能开始一个新的事务,并且从与调用 method1()打开的事务相同的事务中有效地调用 method2() code $。

相反,当你从 method1(),你实际上正在调用一个事务代理的方法。因此,如果这个外来方法用REQUIRES_NEW标记,代理会启动一个新的事务,并且您可以捕获 method1()中的异常并恢复外部事务。



这在。


Here is my problem :

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) {
     // ...
   }
   // ...
 }
}

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

Here is the stack trace :

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)

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

What I have tried:

  • Setting @Transactional(noRollbackFor={UserException.class})) on method1
  • Try and catch in method2

But it didn't change anything.

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.

Update

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.

  • 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 :

  • 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 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().

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.

This is described in the documentation.

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

08-04 07:03
查看更多