JPA:缓存查询

And*_*rey 15 java caching hibernate jpa

我正在使用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. Statement或PreparedStatement实例都已创建并初始化.
  3. 语句实例用参数填充并执行.

我相信上述第1步和第2步应该在每个应用程序生命周期内执行一次.但是怎么做呢?换句话说,我需要缓存Query实例.

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

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

Pas*_*ent 18

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

使用@NamedQuery通常在结果的实体类上使用的注释定义命名查询.在您的情况下,在Order实体上:

@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 {
    ...
}
Run Code Online (Sandbox Code Playgroud)

还建议使用实体名称为命名查询添加前缀(以获得某种名称空间并避免冲突).

然后在DAO中:

class OrderDao {
    EntityManager em;

    List getOrders(Long customerId) {
        return em.createNamedQuery("Order.findByCustomerId")
                 .setParameter("customerId", customerId);
                 .getResultList();
    }
}
Run Code Online (Sandbox Code Playgroud)

PS:我重复使用你建议作为例子的查询但是有点奇怪的customerIdOrder,我希望有一个Customer.

参考

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

  • 命名查询还具有在服务器启动期间进行验证的优点,当查询因模型更改而无法工作时,这将导致错误.这可以保护您免受一个罕见的查询感到惊讶,该查询在以后的实际使用时不能正常工作. (4认同)

Thi*_*Roy 6

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