The @ManyToMany relationship connects two sets of data where each item in one set can relate to many items in the other set, and vice versa. It helps model real-world connections like students enrolled in many courses.
@ManyToMany relationship in Spring Boot
Start learning this pattern below
Jump into concepts and practice - no test required
or
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Introduction
Syntax
Spring Boot
@ManyToMany @JoinTable( name = "table_name", joinColumns = @JoinColumn(name = "this_entity_id"), inverseJoinColumns = @JoinColumn(name = "other_entity_id") ) private Set<OtherEntity> relatedEntities;
Use @ManyToMany on both sides of the relationship to link the two entities.
The @JoinTable annotation defines the join table that holds the connections between the two entities.
Examples
Spring Boot
@Entity public class Student { @Id private Long id; @ManyToMany @JoinTable( name = "student_course", joinColumns = @JoinColumn(name = "student_id"), inverseJoinColumns = @JoinColumn(name = "course_id") ) private Set<Course> courses = new HashSet<>(); }
Spring Boot
@Entity public class Course { @Id private Long id; @ManyToMany(mappedBy = "courses") private Set<Student> students = new HashSet<>(); }
Sample Program
This program defines Student and Course entities linked with a @ManyToMany relationship. Alice is enrolled in Math and Science courses. The output lists Alice's courses.
Spring Boot
import jakarta.persistence.*; import java.util.*; @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 Set<Course> courses = new HashSet<>(); public Student() {} public Student(String name) { this.name = name; } public void addCourse(Course course) { courses.add(course); course.getStudents().add(this); } public Set<Course> getCourses() { return courses; } public String getName() { return name; } } @Entity public class Course { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String title; @ManyToMany(mappedBy = "courses") private Set<Student> students = new HashSet<>(); public Course() {} public Course(String title) { this.title = title; } public Set<Student> getStudents() { return students; } public String getTitle() { return title; } } // Usage example (in a service or main method): Student alice = new Student("Alice"); Course math = new Course("Math"); Course science = new Course("Science"); alice.addCourse(math); alice.addCourse(science); System.out.println(alice.getName() + " is enrolled in:"); for (Course c : alice.getCourses()) { System.out.println("- " + c.getTitle()); }
Important Notes
Always maintain both sides of the relationship to keep data consistent.
The join table stores the connections and usually has two foreign keys.
Use Set instead of List to avoid duplicate links.
Summary
@ManyToMany links two entities where each can have many of the other.
Use @JoinTable to define the join table that connects them.
Keep both sides updated to avoid data mismatches.
Practice
1. What does the
@ManyToMany annotation represent in Spring Boot?easy
Solution
Step 1: Understand the meaning of @ManyToMany
The annotation defines a link where each entity can be related to many instances of the other entity.Step 2: Compare with other relationship types
Unlike one-to-one or one-to-many, many-to-many allows multiple connections on both sides.Final Answer:
A relationship where each entity can have many of the other entity. -> Option DQuick Check:
@ManyToMany = many-to-many link [OK]
Hint: Think: many items linked to many others [OK]
Common Mistakes:
- Confusing with one-to-one or one-to-many
- Thinking it means inheritance
- Ignoring the bidirectional nature
2. Which of the following is the correct way to define a join table in a @ManyToMany relationship?
easy
Solution
Step 1: Identify correct @JoinTable usage
The join table must specify the table name and correctly assign joinColumns and inverseJoinColumns to the owning and inverse sides.Step 2: Check column names match entities
joinColumns should refer to the current entity's ID, inverseJoinColumns to the other entity's ID.Final Answer:
@JoinTable(name = "student_course", joinColumns = @JoinColumn(name = "student_id"), inverseJoinColumns = @JoinColumn(name = "course_id")) -> Option BQuick Check:
@JoinTable with correct joinColumns = A [OK]
Hint: joinColumns = this entity, inverseJoinColumns = other entity [OK]
Common Mistakes:
- Swapping joinColumns and inverseJoinColumns
- Using @JoinColumn instead of @JoinTable
- Omitting joinColumns or inverseJoinColumns
3. Given the following code snippet, what will be the output when printing
student.getCourses().size() after adding two courses to the student?
@Entity
class Student {
@ManyToMany
Set<Course> courses = new HashSet<>();
public Set<Course> getCourses() { return courses; }
}
@Entity
class Course {}
Student student = new Student();
Course c1 = new Course();
Course c2 = new Course();
student.getCourses().add(c1);
student.getCourses().add(c2);
System.out.println(student.getCourses().size());medium
Solution
Step 1: Understand the collection type and additions
The courses field is a HashSet, which allows unique elements. Adding two different Course objects increases size to 2.Step 2: Confirm no errors in adding elements
Adding elements to the set is valid and no exceptions occur here.Final Answer:
2 -> Option AQuick Check:
Two courses added = size 2 [OK]
Hint: HashSet size equals unique added elements [OK]
Common Mistakes:
- Assuming size is 0 because of missing persistence
- Confusing with list allowing duplicates
- Expecting compilation error due to missing annotations
4. Identify the error in this @ManyToMany mapping:
@Entity
class Author {
@ManyToMany
Set<Book> books;
}
@Entity
class Book {
@ManyToMany(mappedBy = "books")
Set<Author> authors;
}medium
Solution
Step 1: Check collection initialization
The sets 'books' and 'authors' are declared but not initialized, so they are null by default.Step 2: Understand impact of null collections
Trying to add or access elements will cause NullPointerException at runtime.Final Answer:
The collections are not initialized, causing NullPointerException. -> Option CQuick Check:
Uninitialized sets cause null errors [OK]
Hint: Always initialize collections to avoid null errors [OK]
Common Mistakes:
- Assuming @JoinTable is mandatory on both sides
- Confusing owning side with inverse side
- Thinking inheritance is required
5. You have two entities,
Student and Club, with a @ManyToMany relationship. You want to add a new club to a student and ensure both sides reflect this change. Which code snippet correctly updates both sides?hard
Solution
Step 1: Understand bidirectional @ManyToMany updates
Both sides must be updated to keep the relationship consistent in memory.Step 2: Check which code updates both sides
student.getClubs().add(club); club.getStudents().add(student); adds the club to the student's clubs and the student to the club's students, syncing both sides.Final Answer:
student.getClubs().add(club); club.getStudents().add(student); -> Option AQuick Check:
Update both sides for consistency [OK]
Hint: Always update both sides of @ManyToMany [OK]
Common Mistakes:
- Updating only one side causing stale data
- Replacing collections without adding
- Ignoring inverse side updates
