You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用JPA Criteria API筛选拥有3门及以上课程的学生

实体类参考(多对多关联配置)

先确保你的Student和Course实体正确配置多对多关联:

@Entity
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
        name = "student_course",
        joinColumns = @JoinColumn(name = "student_id"),
        inverseJoinColumns = @JoinColumn(name = "course_id")
    )
    private Set<Course> courses = new HashSet<>();

    // 构造器、getter、setter省略
}
@Entity
public class Course {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String courseName;

    @ManyToMany(mappedBy = "courses")
    private Set<Student> students = new HashSet<>();

    // 构造器、getter、setter省略
}
正确的JPA Criteria API实现

要查询拥有3门及以上课程的学生,核心是通过分组(GROUP BY) + 课程数量统计(COUNT) + HAVING条件过滤实现,以下是可运行的完整代码:

public List<Student> findStudentsWithAtLeast3Courses(EntityManager em) {
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Student> cq = cb.createQuery(Student.class);
    Root<Student> studentRoot = cq.from(Student.class);

    // 关联Course表,用INNER JOIN只保留有课程的学生
    Join<Student, Course> courseJoin = studentRoot.join("courses", JoinType.INNER);

    // 按Student主键分组,确保每个学生仅被统计一次
    cq.groupBy(studentRoot.get("id"));

    // 过滤课程数量≥3的学生
    cq.having(cb.ge(cb.count(courseJoin), 3L));

    return em.createQuery(cq).getResultList();
}
常见错误分析
  1. 未添加分组逻辑:如果漏掉groupBy,会统计所有学生的课程总数,而非单个学生的课程数,完全不符合需求。
  2. 重复计数问题:若关联时用LEFT JOIN且未去重,可能出现重复计数(多对多的Set集合不会重复,但SQL层面逻辑不当仍可能触发),此时可改用cb.countDistinct(courseJoin)确保计数准确。
  3. 分组字段不完整:如果仅按学生姓名分组,会导致同姓名的学生被合并,必须按主键或唯一标识分组。
  4. 关联类型错误:用LEFT JOIN会包含无课程的学生,虽然后续HAVING也能过滤,但INNER JOIN更高效,因为我们只需要有课程的学生。
扩展:同时获取学生与课程数量

如果需要同时返回学生信息和对应的课程数量,可修改查询返回Tuple:

public List<Tuple> findStudentsWithCourseCount(EntityManager em) {
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Tuple> cq = cb.createTupleQuery();
    Root<Student> studentRoot = cq.from(Student.class);
    Join<Student, Course> courseJoin = studentRoot.join("courses", JoinType.INNER);

    cq.groupBy(studentRoot.get("id"), studentRoot.get("name"));
    cq.having(cb.ge(cb.count(courseJoin), 3L));
    // 同时选择学生对象和课程数量
    cq.multiselect(studentRoot, cb.count(courseJoin).alias("courseCount"));

    return em.createQuery(cq).getResultList();
}

使用时通过tuple.get(Student.class)获取学生,tuple.get("courseCount")获取对应课程数量。

内容的提问来源于stack exchange,提问作者Petr Kostroun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 11:33:24