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(类型安全查询)

TypedQueryQuery 的泛型版本,避免了强制类型转换的问题,提升代码健壮性。

示例代码:

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:适合构建动态查询,但代码略显繁琐

在实际开发中,应根据项目需求、团队习惯和数据库依赖程度,合理选择合适的查询方式。保持良好的查询管理策略,有助于提升代码可维护性和性能表现。


原始标题:Types of JPA Queries | Baeldung