我正在尝试使用 JPA Criteria API 创建一个过滤器查询,以查找组中具有最低值的所有实体。
例如,给定一个 JPA 实体“学生”映射类似于以下内容的表:
id | 名字 | 年级 | 班级 |
---|---|---|---|
1 | 约翰 | 1 | A |
2 | 吉姆 | 2 | A |
3 | 詹姆斯 | 4 | B |
4 | 乔丹 | 3 | B |
我想将其过滤到班上成绩最低的学生,在本例中为
John
和Jordan
。
在 SQL 中,这可以通过像这样的自连接来完成:
SELECT *
FROM students s1
inner join(
select class, Min(grade) as max_grade from students group by class
) s2 on s1.class = s2.class and s1.grade = s2.max_grade;
但是,我无法找到使用 JPA Criteria API 创建类似的自连接方法的方法。
我不能仅使用 JPQL 来实现此目的,因为必须将此谓词添加到大型现有条件谓词中。
SQL 查询的编写方式,它将获取班级中成绩等于最低成绩的所有学生。 如果这不是问题,那么您可以考虑将查询重写为 JPQL,如下所示(接下来我们将讨论 Criteria API:
SELECT s1 FROM Student s1
WHERE s1.grade IN (
SELECT min(s2.grade) FROM Student s2 GROUP BY s2.class
);
上面的查询也有同样的缺点,它会获取班级中成绩较低的所有学生。但是,在这种形式中,它可以转换为 JPA Criteria API,如下所示,假设 (1) 您已经为方便起见构建了元模型和类型安全以及 (2) 列
class
实际上映射到名为 clazz
的属性,因为 class
是 Java 中的保留名称;变量名称将尽可能与 JPQL 匹配:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Student> query = cb.createQuery(Student.class);
Root<Student> s1 = query.from(Student.class);
Subquery<Integer> subquery = query.subquery(Integer.class);
Root<Student> s2 = subquery.from(Student.class);
subquery
.select(cb.min(s2.get(Student_.grade)))
.groupBy(s2.get(Student_.grade));
query
.select(s1)
.where(cb.in(s1.get(Student_.grade)).in(subquery));
List<Student> result = em.createQuery(query).getResultList();