1. 概述
本文将深入讲解 Java 持久化 API(JPA)中支持的三种主要查询类型:
- JPQL 查询(Query)
- 原生 SQL 查询(NativeQuery)
- Criteria API 查询
我们将逐一分析它们的使用方式、适用场景、优缺点,并通过代码示例帮助你更好地理解和选择。
2. 环境准备
为统一示例背景,我们先定义一个 UserEntity
实体类,所有查询操作都将基于它进行演示:
@Table(name = "users")
@Entity
public class UserEntity {
@Id
private Long id;
private String name;
// 标准构造函数、getter 和 setter 省略
}
JPA 中的查询主要分为以下三类:
Query
:使用 JPQL 编写NativeQuery
:使用原生 SQL 编写Criteria API Query
:通过方法链构建的类型安全查询
接下来我们逐一介绍。
3. JPQL 查询(Query)
JPQL(Java Persistence Query Language)是一种面向对象的查询语言,语法与 SQL 类似,但操作的是实体对象而非数据库表。
示例代码:
public UserEntity getUserByIdWithPlainQuery(Long id) {
Query jpqlQuery = getEntityManager().createQuery("SELECT u FROM UserEntity u WHERE u.id=:id");
jpqlQuery.setParameter("id", id);
return (UserEntity) jpqlQuery.getSingleResult();
}
✅ 优点:
- 支持面向对象查询,与实体类绑定
- 可移植性强,不依赖特定数据库
❌ 缺点:
- 动态拼接复杂查询时可读性差
- 查询语句散落在 Java 代码中,不利于维护
3.1 TypedQuery(类型安全查询)
TypedQuery
是 Query
的泛型版本,避免了强制类型转换的问题,提升代码健壮性。
示例代码:
public UserEntity getUserByIdWithTypedQuery(Long id) {
TypedQuery<UserEntity> typedQuery = getEntityManager()
.createQuery("SELECT u FROM UserEntity u WHERE u.id=:id", UserEntity.class);
typedQuery.setParameter("id", id);
return typedQuery.getSingleResult();
}
✅ 优点:
- 强类型支持,避免类型转换异常
- 更易进行单元测试
3.2 NamedQuery(命名查询)
NamedQuery
允许我们在实体类或配置文件中定义命名查询,集中管理常用查询语句,提升可维护性。
示例代码(定义):
@Table(name = "users")
@Entity
@NamedQuery(name = "UserEntity.findByUserId", query = "SELECT u FROM UserEntity u WHERE u.id=:userId")
public class UserEntity {
// 字段和方法省略
}
使用方式:
public UserEntity getUserByIdWithNamedQuery(Long id) {
Query namedQuery = getEntityManager().createNamedQuery("UserEntity.findByUserId");
namedQuery.setParameter("userId", id);
return (UserEntity) namedQuery.getSingleResult();
}
✅ 优点:
- 查询集中管理,易于维护
- 启动时即被验证,避免运行时错误
❌ 缺点:
- 查询静态固定,难以动态拼接
- 不支持动态排序(如 Spring Data JPA 中)
4. 原生 SQL 查询(NativeQuery)
NativeQuery
是 JPA 中执行原生 SQL 的方式,适用于需要直接操作数据库表或使用特定数据库功能的场景。
示例代码:
public UserEntity getUserByIdWithNativeQuery(Long id) {
Query nativeQuery = getEntityManager()
.createNativeQuery("SELECT * FROM users WHERE id=:userId", UserEntity.class);
nativeQuery.setParameter("userId", id);
return (UserEntity) nativeQuery.getSingleResult();
}
✅ 优点:
- 可以使用数据库专有功能
- 查询性能更可控
❌ 缺点:
- 丧失数据库可移植性
- 需要手动处理字段映射(若不使用实体类)
⚠️ 建议: 除非必须使用数据库特性,否则优先使用 JPQL 查询,以保持代码可移植性。
5. Query、NamedQuery 与 NativeQuery 对比
类型 | 创建方式 | 优点 | 缺点 |
---|---|---|---|
Query |
entityManager.createQuery() |
动态构建,可移植 | 可读性差,维护困难 |
NamedQuery |
entityManager.createNamedQuery() |
集中管理,启动验证 | 静态,不支持动态排序 |
NativeQuery |
entityManager.createNativeQuery() |
高性能,支持专有特性 | 可移植性差 |
6. Criteria API 查询(类型安全查询)
Criteria API
是一种通过 Java 代码链式构建查询的方式,具有类型安全和编译时检查的优点,适合构建动态查询。
示例代码:
public UserEntity getUserByIdWithCriteriaQuery(Long id) {
CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<UserEntity> criteriaQuery = criteriaBuilder.createQuery(UserEntity.class);
Root<UserEntity> userRoot = criteriaQuery.from(UserEntity.class);
return getEntityManager().createQuery(
criteriaQuery.select(userRoot)
.where(criteriaBuilder.equal(userRoot.get("id"), id))
).getSingleResult();
}
✅ 优点:
- 类型安全,编译时错误检测
- 支持动态构建查询条件
❌ 缺点:
- 语法冗长,学习成本高
- 可读性不如 JPQL
⚠️ 提示: 可结合 JPA Metamodel
使用,提高类型安全性。
7. 总结
JPA 提供了多种查询方式以适应不同场景:
- ✅ Query / TypedQuery:适用于大多数业务场景,推荐优先使用
- ✅ NamedQuery:适合集中管理常用查询逻辑
- ⚠️ NativeQuery:适合复杂查询或需使用数据库专有特性时使用
- ✅ Criteria API:适合构建动态查询,但代码略显繁琐
在实际开发中,应根据项目需求、团队习惯和数据库依赖程度,合理选择合适的查询方式。保持良好的查询管理策略,有助于提升代码可维护性和性能表现。