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

Spring Boot JPA多对多查询:查找指定学生共同参加的课程

问题描述

现有学生S1、S2、S3、S4,课程C1、C2、C3。其中S1、S2参加C1,S1、S2、S3参加C2。需求:查找所有S1和S2共同参加的课程(预期结果为C1、C2),以下是基于Spring JPA/CRUD Repository的几种实现方案。

实体类代码如下:

class Course {
    @Id
    private String id;
    private String name;
    
    @ManyToMany(fetch = FetchType.EAGER) //debugging purpouses
    @JoinTable(name = "course_students",
            joinColumns = @JoinColumn(name = "course_id"),
            inverseJoinColumns = @JoinColumn(name = "student_id"))
    Set<Student> students;
}

class Student {
    @Id
    String id;
    String firstName;
    String middleName;
    String lastName;
    String phoneNumber;
    String email;
    String avatar;
    int age;

    @ManyToMany(fetch = FetchType.EAGER, mappedBy = "students")
    Set<Course> courses;
}

方案1:JPQL动态查询(支持任意数量学生)

在CourseRepository中定义通用查询方法,通过JPQL筛选包含所有指定学生的课程:

public interface CourseRepository extends JpaRepository<Course, String> {

    @Query("SELECT c FROM Course c JOIN c.students s WHERE s.id IN :studentIds GROUP BY c.id HAVING COUNT(DISTINCT s.id) = :studentCount")
    List<Course> findCommonCoursesByStudentIds(@Param("studentIds") List<String> studentIds, @Param("studentCount") int studentCount);
}

调用示例(假设S1的ID为"1",S2的ID为"2"):

List<String> targetStudentIds = Arrays.asList("1", "2");
List<Course> commonCourses = courseRepository.findCommonCoursesByStudentIds(targetStudentIds, 2);

原理:通过关联课程与学生表,筛选出关联指定学生的课程后分组,统计每组的不同学生数量等于目标学生总数,确保课程包含所有指定学生。


方案2:Spring Data方法命名(仅适用于固定2个学生)

如果仅需查询两个学生的共同课程,可直接利用Spring Data的方法命名规则:

public interface CourseRepository extends JpaRepository<Course, String> {
    List<Course> findByStudentsContainsAndStudentsContains(Student student1, Student student2);
}

调用示例:

Student s1 = studentRepository.findById("1").orElseThrow();
Student s2 = studentRepository.findById("2").orElseThrow();
List<Course> commonCourses = courseRepository.findByStudentsContainsAndStudentsContains(s1, s2);

注意:该方法简洁但灵活性差,无法支持动态数量的学生查询。


方案3:Specification动态查询(复杂场景适配)

若需支持更灵活的动态条件(比如动态选择多个学生),可使用Spring Data的Specification:

  1. 定义Specification实现:
public class CourseSpecifications {
    public static Specification<Course> containsAllStudents(List<Student> targetStudents) {
        return (root, query, criteriaBuilder) -> {
            Join<Course, Student> studentJoin = root.join("students");
            // 按课程分组,统计关联的不同学生数量等于目标学生数
            query.groupBy(root.get("id"));
            query.having(criteriaBuilder.equal(criteriaBuilder.countDistinct(studentJoin.get("id")), targetStudents.size()));
            // 筛选关联学生在目标列表中的课程
            return studentJoin.in(targetStudents);
        };
    }
}
  1. 让Repository继承JpaSpecificationExecutor:
public interface CourseRepository extends JpaRepository<Course, String>, JpaSpecificationExecutor<Course> {
}
  1. 调用示例:
List<Student> targetStudents = Arrays.asList(s1, s2);
List<Course> commonCourses = courseRepository.findAll(CourseSpecifications.containsAllStudents(targetStudents));

内容的提问来源于stack exchange,提问作者Nenad Jovanovski

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 23:20:45