Hibernate使用第一级或第二级缓存加载所有实体

时间:2010-08-31 22:31:42

标签: java hibernate orm caching ehcache

我们在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

虽然我们现在正在进行性能分析,但可能是时候尝试打开查询缓存了。

3 个答案:

答案 0 :(得分:1)

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

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

使用L2缓存,自上次任何人将表放入其中以来,事情可能已经过期或刷新。这可以受缓存提供者的支配,甚至可以通过MBean完全外部完成。因此,Hibernate在任何给定时间都无法真正知道该类型的缓存中的内容是否代表了表的全部内容。同样,您必须查看数据库以确定。

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

-

如果你确实想要它在L2缓存中,理论上你可以在一个集合的表成员中将所有实体放在其他虚假实体上,然后启用缓存集合并在DAO中秘密管理它。我不认为在你的代码中有这种奇怪的东西可能是值得的:)

答案 1 :(得分:1)

当且仅当基础表经常更改时,查询缓存才被视为有害。在您的情况下,表每天更换一次。因此查询将在缓存中保留24小时。相信我:使用查询缓存。它是查询缓存的完美用例。

有害查询缓存的示例:如果您有一个用户表,并且使用查询缓存“from User where username = ...”,则每次修改用户表时,此查询将从缓存中逐出(另一个用户更改) /删除他的帐户)。因此,对此表的任何修改都会触发缓存逐出。改善这种情况的唯一方法是使用natural-id查询,但这是另一个故事。

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

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

您的答案是重新实现查询缓存的方式,因为查询缓存只会缓存ID列表。在L1 / L2缓存中查找实际对象。因此,在使用查询缓存时仍需要缓存实体。

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

答案 2 :(得分:0)

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

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