1. 概述

本文将深入探讨 JPA 实现数据排序的各种方式,涵盖基础用法到高级场景。

2. 使用 JPA/JQL API 排序

通过 JQL 排序的核心是 Order By 子句,简单粗暴:

String jql = "Select f from Foo as f order by f.id";
Query query = entityManager.createQuery(jql);

JPA 会生成对应的 原生 SQL 语句

Hibernate: select foo0_.id as id1_4_, foo0_.name as name2_4_ 
    from Foo foo0_ order by foo0_.id

⚠️ 注意:JQL 中的 SQL 关键字不区分大小写,但实体名和属性名区分大小写。

2.1. 设置排序方向

默认升序排序,但可显式指定。支持 asc(升序)和 desc(降序):

String jql = "Select f from Foo as f order by f.id desc";
Query sortQuery = entityManager.createQuery(jql);

生成的 SQL 查询 会包含排序方向:

Hibernate: select foo0_.id as id1_4_, foo0_.name as name2_4_ 
    from Foo foo0_ order by foo0_.id desc

2.2. 多属性排序

order by 子句中添加多个属性即可实现多级排序:

String jql = "Select f from Foo as f order by f.name asc, f.id desc";
Query sortQuery = entityManager.createQuery(jql);

SQL 查询 会包含所有排序条件:

Hibernate: select foo0_.id as id1_4_, foo0_.name as name2_4_ 
    from Foo foo0_ order by foo0_.name asc, foo0_.id desc

2.3. NULL 值排序优先级

数据库对 NULL 的默认排序规则各不相同,可通过 NULLS FIRSTNULLS LAST 自定义:

Query sortQuery = entityManager.createQuery(
    "Select f from Foo as f order by f.name desc NULLS LAST");

生成的 SQL 会包含 NULL 处理逻辑(第3行):

Hibernate: select foo0_.id as id1_4_, foo0_.BAR_ID as BAR_ID2_4_, 
    foo0_.bar_Id as bar_Id2_4_, foo0_.name as name3_4_, from Foo foo0_ order 
    by case when foo0_.name is null then 1 else 0 end, foo0_.name desc

2.4. 一对多关系排序

Bar 实体包含 Foo 集合为例,需同时排序父实体和子集合:

  1. 排序子集合:在 Bar 实体中使用 @OrderBy 注解:
    @OrderBy("name ASC")
    List<Foo> fooList;
    
  2. 排序父实体
    String jql = "Select b from Bar as b order by b.id";
    Query barQuery = entityManager.createQuery(jql);
    List<Bar> barList = barQuery.getResultList();
    

@OrderBy 注解是可选的,此处用于强制排序 Foo 集合。

实际执行的 SQL 查询

Hibernate: select bar0_.id as id1_0_, bar0_.name as name2_0_ from Bar bar0_ order by bar0_.id

Hibernate: 
select foolist0_.BAR_ID as BAR_ID2_0_0_, foolist0_.id as id1_4_0_, 
foolist0_.id as id1_4_1_, foolist0_.BAR_ID as BAR_ID2_4_1_, 
foolist0_.bar_Id as bar_Id2_4_1_, foolist0_.name as name3_4_1_ 
from Foo foolist0_ 
where foolist0_.BAR_ID=? order by foolist0_.name asc

第一条查询排序父实体 Bar,第二条查询排序其关联的 Foo 集合。

3. 使用 JPA Criteria API 排序

JPA Criteria API 的 orderBy 方法可一站式设置 排序方向排序属性

  • orderBy(CriteriaBuilder.asc):升序
  • orderBy(CriteriaBuilder.desc):降序

每个 Order 实例通过 CriteriaBuilderasc/desc 方法创建:

CriteriaQuery<Foo> criteriaQuery = criteriaBuilder.createQuery(Foo.class);
Root<Foo> from = criteriaQuery.from(Foo.class);
CriteriaQuery<Foo> select = criteriaQuery.select(from);
criteriaQuery.orderBy(criteriaBuilder.asc(from.get("name")));

⚠️ get() 方法的参数区分大小写,需与属性名完全匹配。

与 JQL 不同,Criteria API 强制显式指定排序方向。上述代码生成 SQL:

Hibernate: select foo0_.id as id1_4_, foo0_.name as name2_4_
    from Foo foo0_ order by foo0_.name asc

3.1. 多属性排序

为每个排序属性创建 Order 实例并传入 orderBy

CriteriaQuery<Foo> criteriaQuery = criteriaBuilder.createQuery(Foo.class);
Root<Foo> from = criteriaQuery.from(Foo.class); 
CriteriaQuery<Foo> select = criteriaQuery.select(from); 
criteriaQuery.orderBy(criteriaBuilder.asc(from.get("name")),
    criteriaBuilder.desc(from.get("id")));

对应的 SQL 查询:

Hibernate: select foo0_.id as id1_4_, foo0_.name as name2_4_ 
    from Foo foo0_ order by foo0_.name asc, foo0_.id desc

4. 总结

本文系统梳理了 JPA 的排序方案,从基础实体到一对多关系场景。这些方法将排序压力转移至数据库层,是高效处理数据排序的实用技巧。

完整实现代码可在 GitHub 项目 中获取(Maven 项目,可直接导入运行)。


原始标题:Sorting with JPA | Baeldung

« 上一篇: Baeldung周报16
» 下一篇: Baeldung Weekly Review 17