2013-04-17 37 views
0

我在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。

+0

考虑了这一点,我想这是有道理的,每个事务都绑定到相同的Hibernate会话,也就是说,那些由OEMIV过滤器绑定的请求。然而,这看起来有点危险,因为可以在不明确重新附加和保存实体的情况下将更改刷新到数据库。OpenSessionViewFilter允许指定singleSession为false,这似乎是我通过将每个事务绑定到自己的会话来解决问题,但是我没有看到OpenEntityManagerInView过滤器的类似设置。 –

回答

2

我在开始时遇到同样的问题。我们也使用OSIV过滤器,在我们的例子中,问题是事务注释上的只读标记没有传播到休眠状态。我们必须确保使用propere transactionmanager,更重要的是使用hibernatevendoradapter。 检查你的配置,如果你不能解决问题发布你的持久性和弹簧集成配置。

+0

嗨马丁。我不熟悉HibernateVendorAdapter。你能否提供关于它如何配置以及它做什么的更多信息?谢谢。 –

+0

是马丁。你是对的!问题是我没有为EMF指定JPAVendorAdaptor。没有这个,其他一切似乎都能正常工作。有趣的是,我从使用Spring Roo生成的示例项目中复制了最初的JPA配置。 Roo生成的文件没有指定适配器,但会自动配置OEMIV过滤器。所以我想任何使用Roo引导项目的人都会遇到同样的行为。任何,再次感谢。一直把我逼疯。 –

相关问题