无法提交JPA事务:标记为rollbackOnly的事务 [英] Could not commit JPA transaction: Transaction marked as rollbackOnly

查看:143
本文介绍了无法提交JPA事务:标记为rollbackOnly的事务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我在使用Spring和Hibernate的一个应用程序中工作,并且处理事务时遇到了问题。我得到了一个服务类,它从数据库中加载一些实体,修改它们的一些值,然后(当一切都有效时)将这些更改提交给数据库。如果新值无效(我只能在设置它们后才能检查),我不想坚持更改。为了防止Spring / Hibernate保存更改,我在该方法中抛出异常。但是,这会导致以下错误:

 无法提交JPA事务:标记为rollbackOnly的事务



这是服务:

pre $ @Service
class MyService {

@Transactional(rollbackFor = MyCustomException.class)$ b $ public void doSth()throws MyCustomException {
//从数据库加载实体
//修改它们的一些值
//检查它们是否有效
if(invalid){//如果它们不是有效的,抛出异常
throw new MyCustomException();
}

}
}

是我如何调用它:

  class ServiceUser {
@Autowired
private MyService myService;

public void method(){
try {
myService.doSth();
} catch(MyCustomException e){
// ...
}
}
}

我希望发生的事情:没有对数据库进行更改,也没有任何用户可见的异常。



发生了什么:数据库没有更改,但应用程序崩溃:

  org.springframework.transaction.TransactionSystemException:无法提交JPA交易; 
嵌套异常是javax.persistence.RollbackException:标记为rollbackOnly的事务

正确设置事务到rollbackOnly,但为什么回滚与异常崩溃?

解决方案

我的猜测是 ServiceUser。 method()本身是事务性的。它不应该。这是为什么的原因。



以下是调用 ServiceUser.method()方法时会发生的情况:
$ b


  1. 事务拦截器拦截方法调用,并启动事务,因为没有事务处于活动状态。
  2. 该方法称为

  3. 该方法调用MyService.doSth()
  4. 事务拦截器拦截方法调用,发现事务已经存在活动,并且不执行任何操作

  5. 执行doSth()并引发异常事件拦截器拦截异常,将事务标记为rollbackOnly,并传播异常

  6. ServiceUser.method()捕获异常并返回事务拦截器
  7. ,因为它已启动事务,尝试承诺它。但是Hibernate拒绝这样做,因为事务被标记为rollbackOnly,所以Hibernate会抛出一个异常。事务拦截器通过抛出一个包装hibernate异常的异常来向调用者发出信号。

现在如果 ServiceUser。 method()不是事务性的,这是发生了什么:


  1. 该方法称为

  2. 方法调用MyService.doSth()
  3. 事务拦截器拦截方法调用,发现没有事务处于活动状态,从而启动事务
  4. $执行b $ b
  5. doSth()并引发异常

  6. 事务拦截器拦截异常。由于它已经启动了事务,并且由于抛出异常,它将回滚事务并传播异常。

  7. ServiceUser.method()捕获异常并返回


I'm using Spring and Hibernate in one of the applications that I'm working on and I've got a problem with handling of transactions.

I've got a service class that loads some entities from the database, modifies some of their values and then (when everything is valid) commits these changes to the database. If the new values are invalid (which I can only check after setting them) I do not want to persist the changes. To prevent Spring/Hibernate from saving the changes I throw an exception in the method. This however results in the following error:

Could not commit JPA transaction: Transaction marked as rollbackOnly

And this is the service:

@Service
class MyService {

  @Transactional(rollbackFor = MyCustomException.class)
  public void doSth() throws MyCustomException {
    //load entities from database
    //modify some of their values
    //check if they are valid
    if(invalid) { //if they arent valid, throw an exception
      throw new MyCustomException();
    }

  }
}

And this is how I invoke it:

class ServiceUser {
  @Autowired
  private MyService myService;

  public void method() {
    try {
      myService.doSth();
    } catch (MyCustomException e) {
      // ...
    }        
  }
}

What I'd expect to happen: No changes to the database and no exception visible to the user.

What happens: No changes to the database but the app crashes with:

org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction;
nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly

It's correctly setting the transaction to rollbackOnly but why is the rollback crashing with an exception?

解决方案

My guess is that ServiceUser.method() is itself transactional. It shouldn't be. Here's the reason why.

Here's what happens when a call is made to your ServiceUser.method() method:

  1. the transactional interceptor intercepts the method call, and starts a transaction, because no transaction is already active
  2. the method is called
  3. the method calls MyService.doSth()
  4. the transactional interceptor intercepts the method call, sees that a transaction is already active, and doesn't do anything
  5. doSth() is executed and throws an exception
  6. the transactional interceptor intercepts the exception, marks the transaction as rollbackOnly, and propagates the exception
  7. ServiceUser.method() catches the exception and returns
  8. the transactional interceptor, since it has started the transaction, tries to commit it. But Hibernate refuses to do it because the transaction is marked as rollbackOnly, so Hibernate throws an exception. The transaction interceptor signals it to the caller by throwing an exception wrapping the hibernate exception.

Now if ServiceUser.method() is not transactional, here's what happens:

  1. the method is called
  2. the method calls MyService.doSth()
  3. the transactional interceptor intercepts the method call, sees that no transaction is already active, and thus starts a transaction
  4. doSth() is executed and throws an exception
  5. the transactional interceptor intercepts the exception. Since it has started the transaction, and since an exception has been thrown, it rollbacks the transaction, and propagates the exception
  6. ServiceUser.method() catches the exception and returns

这篇关于无法提交JPA事务:标记为rollbackOnly的事务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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