我在Spring管理事务中遇到一些奇怪的行为。这是一个Spring MVC应用程序。我将实体直接绑定到Web层。我使用从@ModelAttribute注释的方法调用的以下服务层代码从数据库加载持久实体进行编辑。Spring JPA事务
@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设置为从不,也许不会。
如果我从isEmailAddressUnique()中删除@Transactional属性,则不会发生刷新,并且按预期方式工作,但我想解决问题,但是我想了解这里发生了什么。
有什么建议吗?
=============
好的,我已经进一步调查了。所以我有OpenEntityManagerInViewFilter默认配置:
情景一。
[1] MVC控制器加载在只读事务T1 用户实体[2]实体被修改为MVC场结合 [3]第二个事务的结果在只读事务T2执行针对用户表。 [4] Hibernate刷新T1中加载的实体。 [5]休眠发出查询
场景2.
创建子类的OEMIVFilter设置FlushMode提交(默认为自动)。
如上面步骤[4]和[5]相反,即,即使T2被标记为readOnly Hibernate仍会在事务结束时尝试刷新。我可以将场景1理解为readOnly只能引用当前事务T2(并且将在执行T2之前尝试同步数据库)。但是我希望在场景2中不应该发布Flush。
考虑了这一点,我想这是有道理的,每个事务都绑定到相同的Hibernate会话,也就是说,那些由OEMIV过滤器绑定的请求。然而,这看起来有点危险,因为可以在不明确重新附加和保存实体的情况下将更改刷新到数据库。OpenSessionViewFilter允许指定singleSession为false,这似乎是我通过将每个事务绑定到自己的会话来解决问题,但是我没有看到OpenEntityManagerInView过滤器的类似设置。 –