Hibernate缓存:由缓存查询返回的对象是否存储在L2缓存中?

8
我们的项目中使用了hibernate4ehcache。我们主要处理不可变对象,因此缓存是一个很好的特性。在尝试启用查询缓存时,我们遇到了以下问题:
假设我们有以下实体:
@Entity 
@Table(name = "DOGS")
@Immutable 
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
class Dog {
  @Id @Column
  Long id;
  @Column
  String name;
}

以及查询语句:

Criteria criteria = session.createCriteria(Dog.class);
criteria.add(Restrictions.in("id", ids));
criteria.setCacheable(true);

查询缓存的timeToLive设置为Dog timeToLive的3/4左右。以下是情景描述(如果我有错误的假设,请纠正我):
  1. 第一次调用查询时(假设缓存为空),它会被执行并返回的Dog实例存储在二级缓存中。此外,Dog的id也存储在查询缓存中。
  2. 第二次调用查询时(Dog的id在查询缓存中,而Dog对象在L2缓存中),一切正常。查询缓存返回id,Dogs从L2中获取。
  3. 当查询缓存过期时(但L2缓存仍然有效),查询会重新运行并缓存Dog的id。
  4. 现在,Dog对象的L2缓存过期,并且所有对象都已从缓存中删除。查询缓存仍然具有缓存的id,因此Hibernate逐个逐个地获取Dog对象,这需要很长时间。
第三点让我感到困扰。查询缓存失效并在数据库上重新运行,获取Dog对象,但是Dog对象没有在L2缓存中更新。看起来查询只更新了查询缓存中的dog id,而没有更新L2缓存。
是否有一种方法可以强制查询同时更新L2缓存?也许应该以不同的方式处理这种情况?

您可以启用读/写缓存模式,这样在更新时EHCache会自动处理。 - Phani
该实体是只读的,所以那样做没有帮助。 - Wojciech Górski
3个回答

2
查看第二级缓存是为了获取缓存的对象/POJO。但查询缓存是为特定查询创建的。因此,两者似乎没有关联,当您的查询缓存被更新时,第二级缓存不会被更新。这就是为什么它们有不同的配置。您可以参考Hibernate文档this linkthis link来帮助您理解这些内容。

我知道这是两个不同的缓存,但查询缓存使用域缓存按ID检索对象,对吗?这导致了所描述的问题,到目前为止我还没有找到任何解决方案。 - Wojciech Górski
朋友,你需要接受这个设计。这不是问题,它就是被设计成这样的。 - Ajay Bhojak
我希望你访问了我提供的链接。如果我遇到任何可能帮助你解决问题的东西,我会让你知道。 - Ajay Bhojak
我知道这些链接,我已经谷歌了很多次以找到解决这个问题的答案 :) 然而,如果这个设计按照预期工作,那么这个设计真的非常糟糕。假设缓存查询返回1000个ID,则将向数据库发送1000个选择。 - Wojciech Górski
这是这样的方式:0 - Ajay Bhojak

1
我曾尝试过这个方法,清除L2缓存,效果不错。
//clear the cache entity
sf.getCache().evictEntity(Dog.class,12345); //Entity with 12345 id

//or clear a collection 
sf.getCache().evictCollection("com.package.Dog.getCol",12345); //Collections
//Note: the collection contains the name of the fully qualified class.

//then, run the query

希望它有所帮助。

你建议我什么时候清除缓存?如果在每个请求之前清除它,那么拥有缓存就没有意义了。 - Wojciech Górski
我在预定事件上执行了清除缓存的操作(由于项目需求)。因此,我们会定期(由系统团队安排)清理缓存。 - victoriza
因此,这真的取决于您的项目数据库大小和使用情况。没有准确数据是无法猜测的。 - victoriza
问题在于我不想添加任何启发式算法来清除缓存,因为我无法百分之百确定所描述的情况不会发生。 - Wojciech Górski

0
在我的情况下,我禁用了Hibernate中的最小放置设置,然后查询缓存将自动更新每次发出SQL时所有相关的二级缓存。
我认为这是你的问题“强制查询也更新L2缓存”的解决方案(这也困扰了我很长时间......)
<property name="hibernate.cache.use_minimal_puts" value="false"/>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接