10

与这个问题有关

前提:

这些是我的假设,基于我的阅读、经验和理解,它们可能是错误的,如果是,请发表评论,我将编辑问题。

  • 查询缓存主要与二级缓存一起使用
  • 查询缓存缓存查询+参数的标识符结果
  • 如果数据库被更改并且它没有反映到缓存中,查询缓存是有风险的

问题:

我有一个不在二级缓存中的对象。由于一些糟糕的编程或其他限制,加载对象的代码在同一个休眠会话中被多次调用。检索使用 HQL 查找查询,例如

 hibernateTemplate.find("from Foo f where f.bar > ?", bar);

在添加查询缓存之前,如果上面的代码在同一个Hibernate Session中被调用N次,那么数据库有N次命中

然后我想看看如果我添加查询缓存会发生什么:

 Query query = session.createQuery("from Foo f where f.bar > ?");
 query.setCacheable(true);
 query.setParameter(bar);
 query.list();

当我添加查询缓存时,我注意到在同一个会话期间,休眠不再访问数据库 N 次,每个会话只有一次。

  1. 所以我的第一个假设是 Hibernate 首先在 Session Cache 中搜索,然后在 2nd Level Cache 中搜索。这个假设正确吗?
  2. 我还假设如果Foo不在二级缓存中的对象 () 在数据库中被更改,那么跨会话范围的查询缓存将返回错误的标识符,从而返回错误的对象。那是对的吗?
  3. 是否可以肯定地说,即使对于非 2L 缓存对象,对包含不可变信息的查询使用查询缓存是一种好习惯?(例如一个查询,它的 where 子句包含一个总是返回相同结果的条件,例如“select p.ser_num where p.id = ?” 当 ser_num 和 id 对一旦创建就不会改变)

顺便说一句,在相关问题中声称查询缓存不适用于会话缓存范围。我是否误解了该声明或其他任何内容?

4

3 回答 3

7

查询缓存是一种特殊类型的二级缓存。你所说的二级缓存我更喜欢称之为“对象缓存”。

对您的假设的评论:

  • 查询缓存主要与二级缓存(又名对象缓存)一起使用。

查询缓存仅将查询的原始结果作为主键保存,用休眠方式来说,就是 id。它不包含实际的水合物体。这是有道理的,因为当您使用 jdbc 执行查询时,它实际上只会在您遍历 ResultSet 时返回水合(填充)对象。该陈述不一定正确。如果查询非常复杂,因此需要很长时间才能运行,通过使用查询缓存可以节省时间。通过使用查询缓存,您不会节省从数据库加载对象所需的时间。

  • 如果数据库被更改并且它没有反映到缓存中,查询缓存是有风险的

这是真的,但它不是查询缓存所独有的,对于您所说的二级缓存也是如此,但通常称为对象缓存。

所以我的第一个假设是 Hibernate 首先在 Session Cache 中搜索,然后在 2nd Level Cache 中搜索。这个假设正确吗?

是的,当加载对象时,这是行为。

我还假设如果不在二级缓存中的对象(Foo)在数据库中被更改,那么跨会话范围的查询缓存将返回错误的标识符,从而返回错误的对象。那是对的吗?

是的,对象缓存和查询缓存都会受到影响。仅当数据库更改而不通过休眠时,这才值得关注。您可以通过设置查询缓存的超时来潜在地减轻这种影响。

是否可以肯定地说,即使对于非 2L 缓存对象,对包含不可变信息的查询使用查询缓存是一种好习惯?(例如一个查询,它的 where 子句包含一个总是返回相同结果的条件,例如“select p.ser_num where p.id = ?” 当 ser_num 和 id 对一旦创建就不会改变)

对于这类对象,没有理由不同时使用对象缓存和查询缓存。

是的,查询缓存在会话级别(即 1 级缓存)不起作用。因此,当您再次执行查询时,它再次命中数据库的原因。它不会将查询的结果(id 集)放入会话缓存中。

于 2010-01-04T11:04:50.760 回答
2

只有假设

根据 Hibernate 文档中的这篇文章

[查询缓存] 创建两个新的缓存区域:一个保存缓存的查询结果集(org.hibernate.cache.StandardQueryCache),另一个保存可查询表的最新更新的时间戳(org.hibernate.cache.UpdateTimestampsCache)。

我假设这意味着只要可查询表的时间戳比结果集更新 - 它会导致休眠在下一次调用该查询时接近数据库,并且在某些方面保持查询缓存安全。

但是在文档的同一段落后面有两句话说:

查询缓存应始终与二级缓存结合使用。

再一次,我的假设是,这是 hibernate 可以知道在此特定用户会话之外完成的数据库更改的唯一方法

于 2010-01-04T09:41:04.997 回答
1

对于您的问题 #3,我认为您不想在未缓存对象时使用查询缓存。您最终将获得所有主 ID,但它必须按每个键访问数据库一次以检索对象,这可能比在根本没有缓存的情况下运行查询要慢。无论如何,从 3.3 开始,也许在较新的版本中,它使用更少的查询来获取丢失的对象,例如 where id in (:id1,:id2,...)。

于 2010-01-04T20:04:23.900 回答