2010-08-31 95 views
2

我们,我们需要一个Hibernate会话,我知道加载的所有实体的唯一途径过程中加载实体的整个表的所有实体是通过HQL查询:休眠装载利用第一或第二级缓存

public <T> List<T> getAllEntities(final Class<T> entityClass) { 
    if (null == entityClass) 
     throw new IllegalArgumentException("entityClass can't be null"); 

    List<T> list = castResultList(createQuery(
      "select e from " + entityClass.getSimpleName() + " e ").list()); 


    return list; 
} 

我们使用EHcache进行二级缓存。

问题在于,这被称为100次在给定的交易会话中占用了相当一部分总时间。有什么办法可以加载给定类型的所有实体(加载整个表),并且还能从第一级会话缓存或第二级ehcache中受益。

我们被告知要远离查询缓存,因为它们的潜在性能损失与其收益相关。 * Hibernate Query Cache considered harmful

尽管我们现在正在执行性能分析,所以可能是时候尝试启用查询缓存。

+1

为什么您的代码首先在同一个事务中重新加载整个表100次?如果是因为其他线程可能正在插入,那么即使您以某种不可思议的方式重写代码以使用它们,L1和L2缓存也不能做太多的事情来帮助您。 – Affe 2010-08-31 23:31:49

+0

它是一个静态表(每24小时至少静态),更多的是,在给定的transacation中该方法被称为100次,我们可以轻松地在该DAO中“缓存”该事务的方法,但我很好奇,如果我正在重新创建已经完成的缓存,如果我以不同的方式加载表。 – Dougnukem 2010-09-01 18:04:06

回答

0

我们最终解决这个问题的方法是将主键存储在我们需要加载的表中的所有实体(因为他们是模板数据,没有新的模板被添加/删除)。

然后,我们可以使用这个主键列表来查找每个实体并利用Hibernates第一和第二级缓存。

1

对于“获取整个表”的问题,L1和L2缓存无法帮助您。

L1缓存装备不足,因为如果别人插入了某些东西,它不在那里。 (您可能“知道”在系统的业务规则中没有其他人会这样做,但Hibernate会话不会)。因此,您必须查看数据库以确保安全。

有了L2缓存,事情可能已经过期或刷新自从上次有人把表放在那里。这可以由缓存提供者支配,甚至可以通过一个MBean在外部完成。因此,Hibernate在任何时候都无法真正了解该类型的缓存中的内容是否代表该表的全部内容。再次,你必须在数据库中查看一下。

由于您对此实体有特殊的知识(从未创建过新的实体),因此没有实际的方法来传递L1或L2缓存,因此您需要使用由Hibernate提供的工具关于结果集的特殊业务规则级知识,查询缓存或自己缓存信息。

-

如果你真的想在L2高速缓存,理论上你可以做一个集合的表成员的所有实体上的一些其他虚假的实体,然后启用缓存的收集和管理偷偷它在DAO中。我不认为这可能是值得在你的代码中有这种奇怪的东西:)

1

当且仅当基础表经常变化时,查询缓存才被认为是有害的。在你的情况下,表格每天更换一次。所以查询会在缓存中保持24小时。相信我:使用它的查询缓存。这是查询缓存的完美用例。

有害查询缓存示例:如果您有一个用户表,并且您使用“来自User where username = ...”的查询缓存,那么每次修改用户表时,此查询都会从缓存中逐出(另一个用户更改/删除他的帐户)。所以对这个表的任何修改都会触发缓存逐出。改善这种情况的唯一方法是用自然标识查询,但这是另一回事。

如果您知道您的表格每天只能修改一次,那么查询缓存将只会每天驱逐一次!

但修改表格时要注意你的逻辑。如果你通过休眠来实现,一切都很好。如果您使用直接查询,则必须告诉hibernate您已修改表(类似query.addSynchronizedEntity(..))。如果通过shell脚本执行此操作,则需要调整基础缓存区域的生存时间。

您的答案是通过重新实现查询缓存的方式,因为查询缓存只是缓存了id列表。在L1/L2缓存中查找实际对象。所以当你使用查询缓存时你仍然需要缓存实体。

请将此标记为正确答案,以供进一步参考。