我正在尝试弄清楚如何与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
不行...控制器没有处理来自数据库的异常的业务。
5. 不要捕获DataIntegrityViolationException
我看到过网上的几个资源,特别是与Hibernate结合使用时,建议捕获这个异常是错误的,并且人们应该在保存之前检查条件(即使用手动查询检查用户名是否存在)。在并发情况下,即使使用事务,这种方法也行不通。是的,您将获得事务的一致性,但仍然会在“其他人先到”的情况下收到DataIntegrityViolationException
。因此,这不是一个可接受的解决方案。
7. 不要使用声明式事务管理
使用Spring的TransactionTemplate
而不是@Transactional
。这是唯一比较令人满意的解决方案。但是,它比“只是在方法上添加@Transactional
”要复杂得多,即使Spring文档似乎也倾向于使用@Transactional
。
我希望能得到有关如何最好处理这种情况的建议。还有更好的选择吗?
@Transactional
,另一种是捕获DataIntegrityViolationException
。 - Markus Pscheidt