【发布时间】:2013-04-10 18:36:10
【问题描述】:
我在 Spring 托管事务中遇到了一些奇怪的行为。这是一个 Spring MVC 应用程序。我将实体直接绑定到 Web 层。我使用以下服务层代码从数据库加载一个持久实体以进行编辑,该代码是从使用@ModelAttribute 注释的方法调用的。
@Transactional(readOnly = true)
@PreAuthorize("#id == authentication.principal.id or hasRole('ROLE_ADMIN')")
public User findById(Long id) {
return repository.findOne(id);
}
提交表单时,数据将绑定到此分离实体。作为验证的一部分,我查询数据库以验证为用户指定的电子邮件地址是唯一的。
@Transactional(readOnly = true)
public boolean isEmailAddressUnique(String emailAddress, Long userId){
return repository.checkEmailAddressUnique(emailAddress, userId) == 0;
}
此时,在执行此查询之前,Hibernate 会尝试刷新之前加载的我不理解的分离实体。如果电子邮件地址不是唯一的,显然这会导致异常。
我正在使用 OpenEntityManagerinViewFilter,但是我希望这应该将 FlushMode 设置为 NEVER,也许不会。
如果我从 isEmailAddressUnique() 中删除 @Transactional 属性,则不会发生刷新,并且一切都按预期工作,我想这可以解决问题,但是我想了解这里发生了什么。
有什么建议吗?
=============
好的,我已经进一步调查了。所以我有 OpenEntityManagerInViewFilter 配置了默认值:
场景一。
[1] MVC 控制器在只读事务 T1 中加载用户实体 [2] 实体因 MVC 字段绑定而被修改 [3] 第二个事务在只读事务 T2 中针对用户表运行。 [4] Hibernate 刷新 T1 中加载的实体。 [5] Hibernate 发出查询
场景 2。
子类化 OEMIVFilter 以将 FlushMode 设置为提交(默认为自动)。
如上所述,将步骤 [4] 和 [5] 颠倒过来,即。即使 T2 被标记为 readOnly Hibernate 仍然尝试在事务结束时刷新。我可以理解场景 1,因为 readOnly 可能只引用当前事务 T2(并且会在执行 T2 之前尝试同步数据库)。但是我希望在场景 2 中不应该发出 Flush。
【问题讨论】:
-
考虑到这一点我想这是有道理的,因为每个事务都绑定到同一个 Hibernate 会话,即。绑定到 OEMIV 过滤器的请求。然而,这似乎有点危险,因为更改可以刷新到数据库而无需显式重新附加和保存实体。 OpenSessionViewFilter 允许指定 singleSession false 在我看来,这似乎可以通过将每个事务绑定到自己的会话来解决问题,但是我没有看到 OpenEntityManagerInView 过滤器的类似设置。
标签: spring hibernate jpa transactions