【发布时间】:2018-10-13 12:11:48
【问题描述】:
我试图找出如何结合 Spring 的@Transactional 最好地处理持久性(以及可能的其他)异常。
在这篇文章中,我将举一个简单的用户注册示例,由于用户名重复,可能会导致DataIntegrityViolationException。
我尝试过以下几件事,但对我来说并不是很满意:
1。幼稚的方法:抓住异常
val entity = UserEntity(...)
try {
repo.save(entity)
} catch (e: DataIntegrityViolationException) {
// not included: some checks for which constraint failed
throw DuplicateUsername(username) // to be handled by the controller
}
这在 @Transactional 方法中不起作用,因为在提交事务之前不会发生持久性异常,这发生在我在 spring 事务包装器中的服务方法之外。
2。退出前刷新EntityManager
在我的服务方法结束时在EntityManager 上显式调用flush。这将强制写入数据库并因此触发异常。但是它可能效率低下,因为我现在必须注意不要在请求期间无缘无故地刷新多次。我也最好永远不要忘记它,否则例外会消失得无影无踪。
3。创建两个服务类
将@Transactional 方法放在一个单独的spring bean 中,并在主服务中围绕它们进行try-catch。这很奇怪,因为我必须注意将代码的一部分放在 A 位置,另一部分放在 B 位置。
4。在控制器中处理DataIntegrityViolationException
只是……不。控制器在处理来自数据库的异常时没有任何业务(hue hue hue)。
5。不要抓DataIntegrityViolationException
我在网上看到了一些资源,尤其是结合 Hibernate 的资源,这表明捕获此异常是错误的,应该在保存之前检查条件(即通过手动查询检查用户名是否存在)。这在并发场景中不起作用,即使使用事务也是如此。是的,您将获得与事务的一致性,但是当“其他人先来”时,您仍然会获得DataIntegrityViolationException。因此,这不是一个可接受的解决方案。
7。不要使用声明式事务管理
使用 Spring 的 TransactionTemplate 而不是 @Transactional。这是唯一有点令人满意的解决方案。然而,使用它比“只是在方法上抛出 @Transactional”要“笨拙”得多,甚至 Spring 文档似乎也在推动你使用 @Transactional。
我想要一些关于如何最好地处理这种情况的建议。我上次提出的解决方案有更好的替代方案吗?
【问题讨论】:
-
为什么不作为 (3) 的变体,在同一个服务中有两种方法?一个是
@Transactional,另一个是DataIntegrityViolationException。 -
遇到同样的问题,我将使用 (3) 的这种变体,听起来是最好的方法。不要忘记将第二种方法也公开,因为它应该能够被“分割”(可能取决于您使用的 aop 框架)。
-
您好,您是否尝试过使用@ControlerAdvice?如果没有,也许看看here。
-
基本上就是第 4 点。根据 MVC,控制器层不应该处理来自数据库的异常。
标签: java spring hibernate transactions