Spring Boot JPA Many to Many

Lets learn how to use Spring Boot JPA to implement Many to Many relationship.

In a many-to-many relationship, each record in the first table can be related to one or more records in the second table, and vice versa.

Bidirectional

We can have two entities Student and Course. A student can enroll in multiple courses and a course can have multiple students.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;

@ManyToMany
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private List<Course> courses;

// getters and setters
}

The @ManyToMany annotation is used to create a many-to-many relationship between the Student and Course entities. The @JoinTable annotation is used to specify the join table that will be used to store the relationship between the two entities. The joinColumns attribute specifies the foreign key column in the join table that refers to the primary key column in the Student entity, and the inverseJoinColumns attribute specifies the foreign key column in the join table that refers to the primary key column in the Course entity.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Entity
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;

@ManyToMany(mappedBy = "courses")
@JsonIgnore
@ToString.Exclude
private List<Student> students;

// getters and setters
}

A course can have multiple students. The mappedBy attribute is used to specify the property in the Student entity that owns the relationship.

Test

insert data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// create a student
Student student = new Student();
student.setName("John Doe");
studentRepository.save(student);

// another student
Student student2 = new Student();
student2.setName("Jane Doe");
studentRepository.save(student2);

// create a javaCourse
Course javaCourse = new Course();
javaCourse.setName("Java 101");
courseRepository.save(javaCourse);

// create a pythonCourse
Course pythonCourse = new Course();
pythonCourse.setName("Python 101");
courseRepository.save(pythonCourse);

// add the student to the javaCourse and pythonCourse
javaCourse.getStudents().add(student);
pythonCourse.getStudents().add(student);
student.getCourses().add(javaCourse);
student.getCourses().add(pythonCourse);
studentRepository.save(student);

// add the student2 to the javaCourse
javaCourse.getStudents().add(student2);
student2.getCourses().add(javaCourse);
studentRepository.save(student2);

Get a student’s courses

1
2
3
4
5
6
7
studentRepository.findById(1L)
.orElseThrow(() -> new RuntimeException("Student Not Found"))
.getCourses();

studentRepository.findById(2L)
.orElseThrow(() -> new RuntimeException("Student Not Found"))
.getCourses();

Unidirectional with @ManyToMany

It is possible to create a unidirectional Many-to-Many relationship as well.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;

@ManyToMany
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private List<Course> courses;

// getters and setters
}
1
2
3
4
5
6
7
8
9
@Entity
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;

// getters and setters
}

In this case, the Course entity does not have a reference to the Student entity, so the relationship is unidirectional. The join table is still used to store the relationship between the two entities.