1

我们使用带有 OpenSessionInView 过滤器的 Hibernate Spring MVC。这是我们遇到的一个问题(伪代码)

transaction 1 
load object foo
transaction 1 end

update foo's properties (not calling session.save or session.update but only foo's setters)

validate foo (using hibernate validator)
if validation fails ?
 go back to edit screen
 transaction 2 (read only)
 load form backing objects from db
 transaction 2 end
 go to view
else 
transaction 3 
session.update(foo)
transaction 3 end

我们遇到的问题是,如果验证失败 foo 在休眠会话中被标记为“脏”(因为我们使用 OpenSessionInView,我们在整个 http 请求中只有一个会话),当我们加载表单支持对象时(比如一些实体的列表使用HQL 查询),在执行查询之前休眠检查会话中是否有脏对象,它看到 foo 是并刷新它,当事务 2 提交时,更新被写入数据库。问题是即使它是一个只读事务并且即使 foo 在事务 2 中没有更新,hibernate 也不知道哪个对象在哪个事务中更新,并且不只刷新该事务中的对象。有什么建议么?以前有人遇到过类似的问题吗

更新:这篇文章进一步阐明了这个问题:http ://brian.pontarelli.com/2007/04/03/hibernate-pitfalls-part-2/

4

6 回答 6

1

您可以在 foo 上运行 get 以将其放入休眠会话,然后将其替换为您在其他地方创建的对象。但是要让它工作,你必须知道你的对象的所有 id,这样这些 id 才能在 Hibernate 中看起来是正确的。

于 2008-10-28T20:21:31.187 回答
1

这里有几个选项。首先是您实际上不需要事务 2,因为会话已打开,您可以只从数据库加载支持对象,从而避免对会话进行脏检查。另一种选择是在检索 foo 后将其从会话中逐出,然后在您要存储更改时使用 session.merge() 重新附加它。

对于hibernate,了解幕后究竟发生了什么很重要。在每个提交边界,它将尝试刷新对当前会话中对象的所有更改,无论是否在当前事务或任何事务中进行了更改。这样您实际上不需要为会话中已经存在的任何对象调用 session.update() 。

希望这可以帮助

于 2008-10-31T17:44:40.403 回答
1

这里有一个设计问题。你认为 ORM 是对数据存储的透明抽象,还是认为它是一组数据操作库?我会说Hibernate是前者。它存在的全部原因是消除内存中对象状态和数据库状态之间的区别。它确实提供了低级机制,允许您将两者分开并分别处理它们,但这样做会消除 Hibernate 的很多价值。

非常简单 - Hibernate = 你的数据库。如果您不希望某些东西持久化,请不要更改持久性对象。

在更新域对象之前验证您的数据。无论如何也要验证域对象,但这是最后一道防线。如果您确实在持久对象上遇到验证错误,请不要吞下该异常。除非您阻止它,否则 Hibernate 会做正确的事情,即在此处关闭会话。

于 2009-07-15T02:48:30.877 回答
0

使用 Session.clear() 和/或 Session.evict() 怎么样?

于 2008-11-15T22:00:10.937 回答
0

在过滤器上设置 singleSession=false 怎么样?这可能会将您的操作放入单独的会话中,因此您不必处理第一级缓存问题。否则,您可能希望按照上述用户的建议手动分离/附加对象。如果您不希望自动刷新内容(FlushMode.MANUAL),您也可以更改 Session 上的 FlushMode。

于 2008-11-29T07:46:35.853 回答
0

实现一个服务层,查看 spring 的 @Transactional 注释,并在适用的情况下将您的方法标记为 @Transactional(readOnly=true)。

您的刷新模式可能设置为自动,这意味着您无法真正控制何时发生数据库提交。

您还可以将刷新模式设置为手动,并且您的服务/存储库只会在您告诉它们时尝试将数据库与您的应用程序同步。

于 2009-11-24T18:57:41.113 回答