4

我正在处理一个带有大量、相当混乱的代码库的遗留 Java 应用程序。有一个相当标准的“用户”对象存储在请求之间的 HttpSession 中,因此 servlet 在顶部执行以下操作:

HttpSession session = request.getSession(true);
User user = (User)session.getAttribute("User");

旧的用户身份验证层(我不会描述;可以说,它没有使用数据库)正在被替换为使用 Hibernate 映射到数据库的代码。所以“用户”现在是一个 Hibernate 实体。

我对 Hibernate 对象生命周期的理解有点模糊,但现在在 HttpSession 中存储“用户”似乎成为一个问题,因为它将在下一次请求期间在不同的事务中检索。在这里做什么是正确的?下次我可以使用 Hibernate Session 对象的 update() 方法重新附加 User 实例吗?我需要吗?

4

3 回答 3

2

假设您正在为每个请求-响应周期创建一个新的休眠会话,可以将分离的对象合并到新的休眠会话中,但我会完全避免这种方法。

而是尝试在 HttpSession 上存储一个密钥,该密钥可用于通过休眠为每个传入请求查找用户。如果您担心访问数据库以检索可以存储在 HttpSession 中的内容的性能后果,请不要担心 - 您始终可以使用 hibernate 支持的缓存框架来减少数据库访问次数。另一种提高性能的方法是使用乐观锁定。

虽然我没有看过hibernate的源代码,但我认为hibernate使用了“Identity Map”模式。这是一个 Map,它使用实体的 id 作为 Map 中的键,并将关联的实体对象作为 Map 中的值。每当从休眠会话中检索实体时,休眠将查看会话的身份映射以查看它是否存在。如果它在那里,它将从地图中返回实体。如果不存在,它将从数据库中检索实体并将其放在地图上,然后返回实体。这意味着对于给定休眠会话使用相同键(即 id、userId 等)访问给定用户的连续查询将收到对相同用户对象的引用,因此每个查询将能够“看到”对另一个查询的用户对象。出于这个原因,为每个传入请求创建一个新的休眠会话是绝对必要的,这样给定用户的并发请求就不必将它们各自的线程锁定在它们的公共用户对象上。不同的休眠会话每个都有自己的身份映射,因此不会返回对同一用户对象的引用。

通过尝试将 HttpSession 中的 User 对象合并到您的休眠会话中,您基本上是在尝试直接操作休眠的 IdentityMap,用其他东西替换休眠“认为”应该存在的任何东西,并且可以理解这可能会导致问题。正如我所说,虽然可以将分离的对象附加回休眠会话,但我会避免它。不管你采取什么方法,祝你好运。

我强烈建议您阅读以下内容,尤其是关于长时间对话和分离对象的部分:

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/transactions.html

于 2010-07-22T09:24:28.343 回答
0

采用session.merge(..)

(来自Session的文档):

瞬态或分离实例的状态也可以通过调用 merge() 作为新的持久实例持久化。

只要确保覆盖hashCode()equals()正确。

于 2010-05-29T18:30:00.250 回答
0

取决于用户是否在会话期间发生更改,或者您是否只需要 id、name 等进行快速查找。您始终可以 merge() 数据库中的用户再次将其放入会话中。

您不需要每次都合并用户,因为它是您并不总是需要的数据库命中。但是通过合并,您可以将用户重新附加到当前会话。

于 2010-05-29T18:30:25.137 回答