Spring @Transactional 方法的错误处理

3

我正在使用一个带有 @Transactional 注解的事务方法。我试图使用 JpaRepository 的 save() 方法将一个对象保存到数据库中,但由于数据库关系上的约束,它会抛出错误。

现在,当我调试程序时,我发现异常是在事务方法结束时抛出的,而不是在调用 save 方法时抛出。这与非事务方法的行为相当不同。

有人能解释一下为什么会这样吗?为什么异常是在事务方法结束时抛出,而不是在实际应该发生时抛出?

我的第二个问题是,当方法是事务性的时,抛出的异常是 DataViolationException,而当它是非事务性的时,抛出的异常是 PSQLException(使用 Postgres 数据库)。为什么会这样呢?

下面是代码:

@Transactional
public ResponseType methodA(UserObject userObject) {
    //save call
    jpaRepoObject.save(userObject);
    //next call will fail due to relational constraints on database
    jpaRepoObject.save(userObject); //should throw PSQLException/DataViolationException

     return new ResponseType("success"); //Error thrown after this line.
}

因为保存只在事务结束时执行。 - NielsNet
请您能否发布您的代码,这样我们就可以修复它? - Jack Bashford
更重要的是,告诉我们哪个异常以及消息(在这种情况下不一定是堆栈跟踪)。 - chrylis -cautiouslyoptimistic-
@NielsNet 如果一个事务包含多个保存调用,它们都将在事务结束时完成吗?如果下一个保存依赖于前一个保存和其他代码,会发生什么? - Rishabh Gupta
@JBDouble05 已更新帖子并附上代码! - Rishabh Gupta
@chrylis DataViolationException或PSQLException。基本上是在写操作期间违反关系数据库(Postgres)表上的约束时抛出的异常。 - Rishabh Gupta
2个回答

1

这是JPA的一个特性,叫做事务性写后。你的代码执行的所有插入和更新操作都会被Jpa实现存储,直到事务被刷新。这使得jpa实现可以重新排序这些操作,以任何最合理的顺序进行。

刷新意味着执行entityManager实例迄今为止存储的所有操作。您可以告诉entityManager刷新,或者在达到事务边界时它将自动刷新。

需要显式刷新的示例是,如果您正在插入实体并且需要为其生成ID,以便稍后在同一方法中使用它。

您始终可以在事务中运行SQL,并稍后提交或回滚。刷新运行SQL但不提交。


谢谢提供信息,Nathan。我只是想澄清一下!所以,在事务方法中,无论我使用save()还是saveAndFlush(),提交或回滚都将在事务方法结束时执行,对吗? - Rishabh Gupta
另外,您能否也回答我的第二个问题。在事务性和非事务性方法的情况下,我在同一操作上得到了2个不同的异常。这将是非常有帮助的!谢谢! - Rishabh Gupta
Rishabh:无论如何,事务都会提交或回滚。当您移除@Transactional注释时,每个语句都在自己的事务中执行。 - Nathan Hughes

0

save 方法不会立即将数据刷新到底层数据库。首先,实体被保存到一级缓存中,当时间到达时(在事务结束时 - 就像在您的情况下,或者在发出查询时),才将实体保存到数据库中。

要查看您期望的行为,请使用 saveAndFlush 方法。


但是,当我从我的方法中删除@Transactional注释时,错误就会从save调用本身抛出!你能解释一下这种行为吗? - Rishabh Gupta
1
另外,如果我使用saveAndFlush(),在出现错误的情况下整个事务方法是否仍然能够回滚? - Rishabh Gupta

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