我们正在使用Hibernate Spring MVC和OpenSessionInView过滤器。 这是我们遇到的一个问题(伪代码)
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在hibernate会话中被标记为“脏”(因为我们使用OpenSessionInView,我们在整个http请求中只有一个会话),当我们加载表单后备对象(比如使用HQL查询的一些实体的列表)时,hibernate在执行之前查询检查会话中是否有脏对象,它看到foo是并刷新它,当提交事务2时,更新被写入数据库。 问题是即使它是一个只读事务,即使foo没有在事务2中更新,hibernate也不知道哪个对象在哪个事务中被更新,并且不会只刷新该事务中的对象。 有什么建议?
之前有人遇到过类似的问题更新:这篇文章对这个问题有了更多了解:http://brian.pontarelli.com/2007/04/03/hibernate-pitfalls-part-2/
答案 0 :(得分:1)
您可以运行get on foo将其放入hibernate会话中,然后将其替换为您在其他位置创建的对象。但为了实现这一点,你必须知道对象的所有id,以便id对Hibernate看起来正确。
答案 1 :(得分:1)
这里有几个选项。首先,您实际上并不需要事务2,因为会话已打开,您只需从数据库加载支持对象,从而避免对会话进行脏检查。另一种选择是在检索foo之后从会话中驱逐foo,然后使用session.merge()在你要存储的更改时重新附加它。
通过hibernate,了解幕后的具体内容非常重要。在每个提交边界,它将尝试刷新当前会话中对象的所有更改,而不管是否在当前事务或任何事务中进行了更改。这样您实际上不需要为会话中已有的任何对象调用session.update()。
希望这有帮助
答案 2 :(得分:1)
这里有一个设计问题。您认为ORM是数据存储区的透明抽象,还是您认为它是一组数据操作库?我会说Hibernate是前者。它存在的全部原因是消除内存中对象状态和数据库状态之间的区别。它确实提供了低级机制,允许你将两者分开并单独处理它们,但这样做会消除很多Hibernate的价值。
非常简单 - Hibernate =您的数据库。如果您不想持久化某些内容,请不要更改持久对象。
在更新域对象之前验证数据。通过各种方式验证域对象,但这是最后一道防线。如果确实在持久对象上收到验证错误,请不要吞下该异常。除非你阻止它,否则Hibernate会做正确的事情,那就是在那里关闭会话。
答案 3 :(得分:0)
如何使用Session.clear()和/或Session.evict()?
答案 4 :(得分:0)
如何在过滤器上设置singleSession = false?这可能会将您的操作置于单独的会话中,因此您不必处理第1级缓存问题。否则,您可能希望手动分离/附加对象,如上面的用户所建议的那样。如果您不想自动刷新内容(FlushMode.MANUAL),也可以更改Session上的FlushMode。
答案 5 :(得分:0)
实现服务层,查看spring的@Transactional注释,并在适用的情况下将方法标记为@Transactional(readOnly = true)。
您的刷新模式可能设置为auto,这意味着您无法控制数据库提交何时发生。
您还可以将刷新模式设置为手动,并且您的服务/回购只会在您告诉他们时尝试将数据库与您的应用同步。