Hibernate事务问题

时间:2008-10-28 18:47:25

标签: java hibernate spring

我们正在使用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/

6 个答案:

答案 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,这意味着您无法控制数据库提交何时发生。

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