【问题标题】:Hibernate, second level cache and query cacheHibernate、二级缓存和查询缓存
【发布时间】:2011-12-18 04:11:30
【问题描述】:

我正在使用带有 Ehcache 的 Hibernate(不认为这很重要).. 当用户登录时,我通过他的电子邮件识别他,并使用他的电子邮件进行所有后续呼叫。 现在,由于电子邮件不是我的“休眠 ID”(我用 @Id 注释的那个),我必须使用查询加载它,因此将其存储在查询缓存中。但是,当用户注销时,我无法明确地将他从缓存中逐出,因为他存储在与其他已登录用户共享的区域内。

我的问题是:我可以将用户加载到二级缓存中(从而使驱逐更容易),而无需使用session().get(user) 方法再次访问数据库吗? 这里还有一点,如果用户被更新,查询缓存对于这个用户来说是错误的。我希望在更新用户时更新它

【问题讨论】:

    标签: hibernate second-level-cache query-cache


    【解决方案1】:
    1. 为什么要将用户从缓存中逐出?适当配置缓存,一段时间后不再使用时,它将从缓存中删除。

    2. 如果您通过休眠更新用户,二级缓存应该会看到并处理它。

    在我看来,您想了解 Hibernate 中的缓存:http://www.javalobby.org/java/forums/t48846.html

    【讨论】:

      【解决方案2】:

      第一个问题:为什么要在用户注销时从查询缓存中删除用户?我无法想象你的架构为什么需要这样。缓存应该是透明的,当不再需要或不适合缓存时,EhCache 可以轻松删除用户。

      如果用户更新,该用户的 Querycache 将变为错误

      不会,至少如果更新是通过 Hibernate 进行的。你知道查询缓存是如何工作的吗?在您的情况下,它存储 email -> user id 映射。当通过 email 查询时,Hibernate 将首先找到与该电子邮件关联的 user id,然后通过 id 查找用户。如果它存在于二级缓存中,则从那里加载。否则 Hibernate 通过 id 透明地加载用户。不过还是比邮件查询好。

      也就是说,如果您更新用​​户,Hibernate 将自动更新/驱逐 L2 缓存并为所有涉及用户的查询更新查询缓存。它只是工作。

      我希望在我更新用户时更新它

      什么意思?您会遇到的唯一陈旧数据是存储在 L1 缓存中的用户实例,L2 和查询缓存会无缝更新。

      另请参阅:Caching with Hibernate + Spring - some Questions!

      【讨论】:

      • 所以如果我更新一个用户,整个用户缓存将被驱逐?听起来不太好
      • 我认为是这样。如果您更新一个用户,Hibernate 将无法再确定任何涉及用户的查询的结果是正确的。例如。您有一个查询返回在给定时间范围内创建的所有用户。当更新单个用户或添加新用户时,Hibernate 不够聪明,无法猜测更新后哪些结果不再相关。这一切都在UpdateTimestampsCache 中跟踪。
      【解决方案3】:

      你让它变得比必要的更难。每次使用相同参数执行查询时,请使用查询缓存来避免访问数据库。如果用户注销,带有这些特定参数的查询随后将不会执行太多,缓存最终将根据其驱逐策略(LRU、超时等)从缓存中驱逐这个特定查询。缓存应该是透明的,你不应该驱逐任何东西。

      如果您使用 ID 来引用用户而不是使用他的电子邮件地址,则可以完全避免此用例的查询缓存。 ID 用于:唯一地引用一个实体,并且能够使用 session.get 和 session.load 来加载用户。使用邮箱登录就可以了。登录后,应用程序应该使用他的 ID 来指代用户,而不是他的电子邮件。

      关于最后一点:如果更新用户,Hibernate 会自动使缓存的结果无效。即使没有,查询缓存也只存储返回的实体的 ID。实体的状态从实体缓存中加载,实体更新时会失效(或更新)。

      【讨论】:

      • 实际上,我不太关心注销时的驱逐,而更关心二级缓存看不到用户的更新。那么让我正确理解这一点,如果我启用二级缓存,那么休眠将自动将我通过电子邮件检索到的实体存储在二级缓存中?
      • 没有。您还必须为 User 实体启用缓存。但是,如果您更新用​​户,查询“通过电子邮件选择用户”的缓存结果将被自动清除。它只是工作。