8

我目前正在尝试使用 EJB3 作为工作中的一个主要项目的预研究。我正在研究的一件事是查询缓存。

我已经制作了一个非常简单的域模型,其中包含 JPA 注释、@Local 业务接口和 EJB-JAR 中的 @Stateless 实现,部署在 EAR 中以及非常简单的 webapp 以进行一些基本测试。EAR 部署在 JBoss 5.0.1 默认配置中,没有进行任何修改。这非常简单,并且按预期工作。

但是,我最近的测试涉及查询缓存,我得到了一些奇怪的结果:

  • 我有一个域类,它只映射一个 ID 和一个字符串值,并在该特定表中创建了大约 10000 行
  • 在业务 bean 中,有一个非常简单的查询,SELECT m FROM MyClass m
  • 在没有缓存的情况下,平均执行时间约为 400 毫秒
  • 启用查询缓存(通过查询提示),第一次执行当然需要更长的时间,大约 1200 毫秒。下一次执行平均需要 3500 毫秒!

这让我很困惑,所以我启用了 Hibernate 的 show_sql 来查看日志。未缓存,并且在启用缓存的第一次执行时,如预期的那样记录了一个 SELECT。当我应该获得缓存命中时,Hibernate 为数据库表中的每一行记录一个 SELECT。

这当然可以解释执行时间缓慢,但谁能告诉我为什么会发生这种情况?

4

1 回答 1

17

查询缓存的工作方式是它只缓存查询返回的对象的ID。因此,您最初的 SELECT 语句可能会返回所有对象,而 Hibernate 会将它们返回给您并记住 ID。

然而,下一次发出查询时,Hibernate 会检查 ID 列表并意识到它需要具体化实际数据。因此,它会返回数据库以获取其余部分。它每行执行一次 SELECT,这正是您所看到的。

现在,在您认为“此功能显然已损坏”之前,它以这种方式工作的原因是查询缓存旨在与二级缓存协同工作。如果对象在第一次查询之后存储在 L2 缓存中,那么 Hibernate 将改为查找那里以满足每个 ID 的请求。

我强烈建议您阅读Java Persistence with Hibernate一书以了解更多信息。第 13 章特别介绍了优化查询,以及如何有效地使用缓存。

于 2009-05-20T20:28:17.360 回答