Spring Data Jpa 中的 Query Hint

1、简介

本文将带你了解 Spring Data JPA 中 Query Hint (查询提示)的功能、基本原理以及如何有效地应用它们。

这些提示有助于优化数据库查询,并通过影响优化器的决策过程来改善应用性能。

2、理解 Query Hint

Spring Data JPA 中的 Query Hint 是一种强大的工具,可帮助优化数据库查询并提高应用性能。与直接控制执行不同,Query Hint 会影响优化器(Optimizer)的决策过程。

在 Spring Data JPA 中,可以在 org.hibernate.annotations 包中找到这些 Hint,同时还有与 Hibernate 相关的各种注解和类。需要注意的是,这些 Hint 的解释和执行通常取决于底层持久化层实现(Persistence Provider),如 HibernateEclipseLink,因此它们是特定于厂商的。

3、使用 Query Hint

Spring Data JPA 提供了多种利用 Query Hint 优化数据库查询的方法。

3.1、基于注解的配置

Spring Data JPA 提供了一种使用注解为 JPA 查询添加 Query Hint 的简便方法。通过 @QueryHints 注解,可以指定用于生成 SQL 查询的 JPA @QueryHint 数组。

示例如下,设置了 JDBC fetch size Hint,以限制结果返回的大小:

@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
    @QueryHints(value = { @QueryHint(name = "org.hibernate.fetchSize", value = "50") })
    List<Employee> findByGender(String gender);
}

如上,在 EmployeeRepository 接口的 findByGender() 方法中添加了 @QueryHints 注解,以控制一次获取的实体数量。

此外,还可以在 Repository 级别应用 @QueryHints 注解,以影响 Repository 中的所有查询:

@Repository
@QueryHints(value = { @QueryHint(name = "org.hibernate.fetchSize", value = "50") })
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
    // Repository 方法。。。
}

此操作可确保指定的 Query Hint 适用于 EmployeeRepository 接口中的所有查询,从而促进整个 Repository 查询的一致性。

3.2、编程式配置 Query Hint

除了基于注解和动态的方法外,还可以使用 EntityManager 对象以编程方式配置 Query Hint。这种方法提供了对 Query Hint 配置的细粒度控制。下面是一个以编程方式设置自定义 Hint 的示例:

@PersistenceContext
private EntityManager entityManager;

@Override
List<Employee> findRecentEmployees(int limit, boolean readOnly) {
    Query query = entityManager.createQuery("SELECT e FROM Employee e ORDER BY e.joinDate DESC", Employee.class)
      .setMaxResults(limit)
      .setHint("org.hibernate.readOnly", readOnly);
    return query.getResultList();
}

如上,传递了一个 boolean 标志作为参数,用于指示应将 Hint 设置为 true 还是 false。这种灵活性使我们可以根据运行时的条件调整查询行为。

3.3、在实体中定义命名查询(Named Query)

Query Hint 可以直接在实体类中使用 @NamedQuery 注解来应用。这样,就可以在定义命名查询的同时定义特定的 Hint。如下:

@Entity
@NamedQuery(name = "selectEmployee", query = "SELECT e FROM Employee e",
  hints = @QueryHint(name = "org.hibernate.fetchSize", value = "50"))
public class Employee {
    // Entity 属性和方法
}

一旦在实体类中定义了命名查询 selectEmployee 和相关 Hint,就可以使用 EntityManagercreateNamedQuery() 方法来调用:

List<Employee> employees = em.createNamedQuery("selectEmployee").getResultList();

4、Query Hint 使用场景

Query Hint 可用于各种情况,以优化查询性能。下面是一些常见的使用案例。

4.1、超时控制

在查询可能长时间运行的情况下,实施有效的超时管理策略变得至关重要。通过使用 javax.persistence.query.timeout Hint,为查询设置最长执行时间。这种做法可确保查询不会超过指定的时间阈值。

该 Hint 接受以毫秒为单位的值,如果查询时间超过限制,就会抛出 LockTimeoutException

示例如下,设置了 5000 毫秒的超时:

@QueryHints(value = {@QueryHint(name = "javax.persistence.query.timeout<em>"</em>, value = "5000")})
List<Employee> findActiveEmployees(long inactiveDaysThreshold);

4.2、缓存查询结果

可以使用 jakarta.persistence.cache.retrieveMode Hint 来启用查询结果的缓存。当设置为 USE 时,JPA 会首先尝试从缓存中检索实体,然后才会访问数据库。当设置为 BYPASS 时,JPA 会忽略缓存并直接从数据库获取实体。

此外,还可以使用 jakarta.persistence.cache.storeMode 来指定 JPA 在二级缓存中存储实体的处理方式。当设置为 USE 时,JPA 会向缓存中添加实体并更新现有实体。BYPASS 模式指示 Hibernate 只更新缓存中的现有实体,而不添加新实体。最后,REFRESH 模式会在检索缓存中的实体前刷新它们,确保缓存数据是最新的。

这些 Hint 的用法如下:

@QueryHints(value = {
    @QueryHint(name = "jakarta.persistence.cache.retrieveMode", value = "USE"),
    @QueryHint(name = "jakarta.persistence.cache.storeMode", value = "USE")
})
List<Employee> findEmployeesByName(String name);

在检索模式(retrieveMode)和存储模式(storeMode)都被配置为 USE 的情况下,Hibernate 在检索和存储实体时都会积极利用二级缓存。

4.3、优化查询执行计划

Query Hint 可用于影响数据库优化器生成的执行计划。例如,当不需要修改数据时,可以使用 org.hibernate.readOnly 提示来表示查询是只读的:

@QueryHints(@QueryHint(name = "org.hibernate.readOnly", value = "true"))
User findByUsername(String username);

4.4、自定义 SQL 注释

org.hibernate.comment Hint 允许在查询中添加自定义 SQL 注释,从而有助于查询分析和调试。当我们想在生成的 SQL 语句中提供上下文或注释时,这一功能尤其有用。

示例如下:

@QueryHints(value = { @QueryHint(name = "org.hibernate.comment", value = "Retrieve employee older than specified age\"") })
List findByAgeGreaterThan(int age);

5、总结

本文介绍了在 Spring Data JPA 中 Query Hint(查询提示)的重要性以及它们对优化数据库查询以提升应用性能的重要影响。还介绍了多种技术,包括基于注解的配置和直接的 JPQL 操作,来有效地应用 Query Hint。


Ref:https://www.baeldung.com/spring-data-jpa-query-hints