1. 概述
本文将深入探讨JPA和Hibernate查询的使用场景及区别,重点分析Criteria、JPQL和HQL三种查询方式。Criteria查询允许开发者在不编写原生SQL的情况下构建查询语句。除Criteria查询外,我们还将研究Hibernate命名查询的实现方式,以及Spring Data JPA中@Query注解的使用技巧。
需要特别说明的是:Hibernate Criteria API自5.2版本起已被弃用。因此**本文所有示例均采用JPA Criteria API**,这是目前编写Criteria查询的推荐方案。下文提到的"Criteria API"均指JPA标准实现。
2. Criteria查询
Criteria API的核心功能是通过动态添加过滤条件和逻辑运算来构建查询对象。这是一种面向对象的数据操作方式,可直接从关系型数据库表中获取目标数据。
Hibernate的Session对象通过*createCriteria()*方法返回持久化实例,用于执行Criteria查询。简单来说,Criteria API通过叠加过滤条件和逻辑规则来构建最终查询语句。
2.1 Maven依赖
添加最新的JPA实现依赖到pom.xml:
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.4.2.Final</version>
</dependency>
2.2 Criteria查询与表达式使用
CriteriaBuilder负责控制查询结果,它通过CriteriaQuery的*where()*方法应用各种表达式。
先看本文使用的实体类:
public class Employee {
private Integer id;
private String name;
private Long salary;
// 标准getter/setter方法
}
下面是一个获取所有员工记录的简单Criteria查询:
Session session = HibernateUtil.getHibernateSession();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Employee> cr = cb.createQuery(Employee.class);
Root<Employee> root = cr.from(Employee.class);
cr.select(root);
Query<Employee> query = session.createQuery(cr);
List<Employee> results = query.getResultList();
session.close();
return results;
执行流程解析:
- SessionFactory创建Session实例
- Session通过getCriteriaBuilder()获取CriteriaBuilder实例
- CriteriaBuilder使用createQuery()创建CriteriaQuery对象
- 最终调用*getResultList()*获取结果集
再看一个带条件的表达式示例:
cr.select(root).where(cb.gt(root.get("salary"), 50000));
该查询返回薪资超过50000的所有员工记录。
3. JPQL查询
JPQL(Java Persistence Query Language)是Spring Data提供的查询方案之一。通过*@Query*注解可同时执行JPQL和原生SQL查询,默认使用JPQL语法。
在Spring中,**@Query定义的查询优先级高于@NamedQuery标注的命名查询**。
3.1 JPQL查询使用
构建动态JPQL查询示例:
@Query(value = "SELECT e FROM Employee e")
List<Employee> findAllEmployees(Sort sort);
带参数的JPQL查询中,Spring Data会按方法参数顺序传递参数:
@Query("SELECT e FROM Employee e WHERE e.salary = ?1")
Employee findAllEmployeesWithSalary(Long salary);
@Query("SELECT e FROM Employee e WHERE e.name = ?1 and e.salary = ?2")
Employee findEmployeeByNameAndSalary(String name, Long salary);
第二个查询中,name参数对应索引1,salary参数对应索引2。
3.2 JPQL原生查询
原生查询直接操作真实数据库表对象,需设置nativeQuery=true:
@Query(
value = "SELECT * FROM Employee e WHERE e.salary = ?1",
nativeQuery = true)
Employee findEmployeeBySalaryNative(Long salary);
使用命名参数可提升查询可读性并减少重构风险,下面是JPQL和原生格式的命名参数示例:
@Query("SELECT e FROM Employee e WHERE e.name = :name and e.salary = :salary")
Employee findEmployeeByNameAndSalaryNamedParameters(
@Param("name") String name,
@Param("salary") Long salary);
方法参数通过@Param注解映射到查询参数,注解值必须与查询中的参数名完全匹配。
原生查询的命名参数版本:
@Query(value = "SELECT * FROM Employee e WHERE e.name = :name and e.salary = :salary",
nativeQuery = true)
Employee findUserByNameAndSalaryNamedParamsNative(
@Param("name") String name,
@Param("salary") Long salary);
4. HQL查询
HQL(Hibernate Query Language)是面向对象的类SQL查询语言。主要缺点是代码可读性较差,通常通过命名查询将SQL语句与业务代码分离。
4.1 Hibernate命名查询
命名查询使用预定义的不可变查询字符串,在SessionFactory创建时即进行验证(快速失败机制)。使用org.hibernate.annotations.NamedQuery定义:
@NamedQuery(name = "Employee_FindByEmployeeId",
query = "from Employee where id = :id")
每个@NamedQuery仅关联一个实体类,使用@NamedQueries可组合多个查询:
@NamedQueries({
@NamedQuery(name = "Employee_findByEmployeeId",
query = "from Employee where id = :id"),
@NamedQuery(name = "Employee_findAllByEmployeeSalary",
query = "from Employee where salary = :salary")
})
4.2 存储过程与表达式
使用*@NamedNativeQuery*注解存储过程和函数:
@NamedNativeQuery(
name = "Employee_FindByEmployeeId",
query = "select * from employee emp where id=:id",
resultClass = Employee.class)
5. Criteria查询的优势
Criteria API相比HQL的最大优势是提供清晰、面向对象的API,能在编译阶段检测错误。
此外,JPQL和Criteria查询具有相同的性能表现。
Criteria查询在构建动态查询时比HQL/JPQL更灵活,但存在以下局限:
- 不支持原生查询(HQL/JPQL支持)
- 复杂关联查询实现较繁琐(JPQL原生查询更易处理)
6. 总结
本文系统介绍了JPA/Hibernate中三种核心查询方式:
- Criteria API:动态查询构建利器
- JPQL:Spring Data生态的首选查询方案
- HQL:Hibernate原生查询语言