一尘不染

休眠查询缓存-对于不在第二级缓存中的对象-有风险吗?有用?不好的做法?

hibernate

前提:

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

  • 查询缓存通常与第二级缓存一起很好
  • 查询缓存缓存查询+参数的标识符结果
  • 如果数据库已更改,并且没有反映到缓存中,则查询缓存存在风险

题:

我有一个不在二级缓存中的对象。由于某些不良的编程或其他约束,导致在同一hibernate会话中多次调用加载对象的代码。检索使用HQL查找查询,例如

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

在添加查询缓存之前,如果以上代码在同一Hibernate会话中被调用了N次,则数据库有N次点击

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

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

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

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

顺便说一句,在相关问题中,声称查询缓存在会话缓存范围上不起作用。我是否会误解该声明,还是其他?


阅读 201

收藏
2020-06-20

共1个答案

一尘不染

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

关于您的假设的评论:

  • 查询缓存通常与第二级缓存(也称为对象缓存)一起很好。

查询缓存仅将查询的原始结果作为主键保存,在hibernate状态下为id。它不容纳实际的水合物体。这是有道理的,因为当您使用jdbc执行查询时,实际上只在迭代ResultSet时才给您水合(填充)的对象。该陈述不一定正确。如果查询非常复杂,因此运行时间很长,那么使用查询缓存可以节省时间。通过使用查询缓存,您将不会节省从数据库加载对象所花费的时间。

  • 如果数据库已更改,并且没有反映到缓存中,则查询缓存存在风险

这是正确的,但查询缓存并不是唯一的,对于您所说的第二级缓存(通常称为对象缓存)也是如此。

因此,我的第一个假设是,Hibernate首先在会话缓存中搜索,然后在第二级缓存中搜索。 这个假设正确吗?

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

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

是的,对象缓存和查询缓存都将受到影响。仅当更改数据库而不通过hibernate时才需要考虑这一点。您可以通过设置查询缓存的超时来减轻这种影响。

那么可以肯定地说,即使对于非2L缓存的对象,也对包含不可变信息的查询使用查询缓存是一种好习惯吗?(例如,查询中的where子句包含始终返回相同结果的条件,例如,一旦创建后ser_num和id对不变,则“选择select
p.ser_num where p.id =?”)

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

是的,查询缓存不适用于会话级别(也称为级别1缓存)。因此,当您再次执行查询时,它再次命中数据库的原因。它不会将查询的结果(标识集)放入会话缓存中。

2020-06-20