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

时间:2010-01-04 08:57:49

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

与此question

相关

前提:

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

  • 查询缓存主要与二级缓存一起使用
  • 查询缓存缓存查询+参数的标识符结果
  • 如果数据库已更改,则查询缓存存在风险,并且未将其反映到缓存

问题:

我有一个不在二级缓存中的对象。由于一些糟糕的编程或其他约束,加载对象的代码在同一个休眠会话中被多次调用。回顾是使用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首先在Session Cache中搜索,然后在2nd Level Cache中搜索。这个假设是否正确?
  2. 我还假设如果在数据库中更改了不在第二级缓存中的对象(Foo),那么查询缓存(跨会话范围)将返回错误的标识符,因此错误的对象。这是对的吗?
  3. 是否可以肯定地说,对于包含不可变信息的查询,即使对于非2L缓存对象,使用查询缓存也是一种好习惯? (例如,一个查询,其where子句包含一个总会返回相同结果的条件,例如,当ser_num和id对在创建后不会改变时,“select p.ser_num where p.id =?”)
  4. 顺便说一下,在相关的question中声称查询缓存不适用于会话缓存范围。我是否想念这个说法或其他什么?

3 个答案:

答案 0 :(得分:6)

查询缓存是特定类型的二级缓存。您所谓的二级缓存我更喜欢称之为“对象缓存”。

对您的假设的评论:

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

查询缓存仅将查询的原始结果保存为主键,在hibernate中,id为。它不能容纳实际的水合物。这是有道理的,因为当您使用jdbc执行查询时,它实际上只会在您迭代ResultSet时返回水合(填充)对象。声明不一定正确。如果查询非常复杂并因此需要很长时间才能运行,那么通过使用查询缓存,您将节省该时间。您不会通过使用查询缓存来节省从数据库加载对象所需的时间。

  
      
  • 如果数据库已更改,则查询缓存存在风险,并且未反映出来   到缓存
  •   

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

  

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

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

  

我还假设如果对象(Foo)   它不在二级缓存中,   然后在数据库中被更改了   查询缓存,即交叉会话   范围内,将返回错误   标识符,因此是错误的   对象。这是对的吗?

是的,对象缓存和查询缓存都会受到影响。如果在不经过休眠的情况下更改数据库,这只是一个问题。您可以通过设置查询缓存的超时来减轻这种影响。

  

说使用是否安全   包含查询的查询缓存   即使对于非2L也是不可变的信息   缓存对象,是一个很好的做法?   (例如,查询其where子句   包含一个永远的条件   返回相同的结果,例如“选择   p.ser_num,其中p.id =?“当ser_num时   并且id夫妻不会改变一次   创建)

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

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

答案 1 :(得分:2)

仅有假设

根据Hibernate文档中的article

  

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

我认为这意味着每当可查询表的时间戳比结果集更新时 - 它将导致hibernate在下一次调用该查询时接近数据库,并且这使得查询缓存在某些方面保持安全方面。

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

  

应始终使用查询缓存   与二级相结合   高速缓存中。

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

答案 2 :(得分:1)

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