JPA:缓存查询

2022-09-03 00:05:05

我正在使用JPA在基于Java EE的Web应用程序中加载和保存实体。Hibernate被用作JPA的实现,但我不使用Hibernate特定的功能,只使用纯JPA。

下面是一些DAO类,注意方法:getOrders

class OrderDao {
  EntityManager em;

  List getOrders(Long customerId) {
    Query q = em.createQuery(
      "SELECT o FROM Order o WHERE o.customerId = :customerId");
    q.setParameter("customerId", customerId);
    return q.getResultList();
  }
}

方法很简单,但它有一个很大的缺点。每次调用该方法时,都会在 JPA 实现中的某个位置执行以下操作:

  1. JPQL 表达式被解析并编译为 SQL。
  2. 创建并初始化语句或预准备语句实例。
  3. 语句实例填充参数并执行。

我认为上述步骤 1 和 2 应该在每个应用程序生存期内实现一次。但是如何制作它呢?换句话说,我需要缓存查询实例。

当然,我可以在我这边实现这样的缓存。但是等等,我正在使用现代强大的ORM!他们不是已经为我做了这个吗?

请注意,我没有提到像Hibernate查询缓存这样的东西,它缓存查询的结果。在这里,我想更快地执行我的查询。


答案 1

使用静态定义的命名查询。它们效率更高,因为 JPA 持久性提供程序可以在应用程序启动时将 JP QL 字符串转换为 SQL 一次,而不是每次执行查询时,并且特别建议用于频繁执行的查询。

命名查询是使用通常在结果的实体类上使用的批注定义的。在您的情况下,在实体上:@NamedQueryOrder

@Entity
@NamedQueries({
    @NamedQuery(name="Order.findAll",
                query="SELECT o FROM Order o"),
    @NamedQuery(name="Order.findByPrimaryKey",
                query="SELECT o FROM Order o WHERE o.id = :id"),
    @NamedQuery(name="Order.findByCustomerId",
                query="SELECT o FROM Order o WHERE o.customerId = :customerId")
})
public class Order implements Serializable {
    ...
}

还建议在命名查询前面加上实体名称前缀(以便具有某种名称空间并避免冲突)。

然后在DAO中:

class OrderDao {
    EntityManager em;

    List getOrders(Long customerId) {
        return em.createNamedQuery("Order.findByCustomerId")
                 .setParameter("customerId", customerId);
                 .getResultList();
    }
}

PS:我重用了您建议的查询作为示例,但是将 它放在上有点奇怪,我会期望一个。customerIdOrderCustomer

引用

  • JPA 1.0 规范
    • 第 3.6.4 节 “命名查询”

答案 2

休眠中有一个查询计划缓存。因此,每次调用 DAO 时都不会解析 HQL(因此 #1 在应用程序生命周期中只出现一次)。这是QueryPlanCache。它没有大量记录,因为它“只是工作”。但你可以在这里找到更多信息。