Bird
Raised Fist0
Spring Bootframework~8 mins

@ManyToMany relationship in Spring Boot - Performance & Optimization

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Performance: @ManyToMany relationship
MEDIUM IMPACT
This affects database query performance and page load speed when fetching related data in web applications.
Fetching related entities in a @ManyToMany relationship
Spring Boot
@Entity
public class Student {
  @ManyToMany(fetch = FetchType.LAZY)
  private Set<Course> courses;
}
Lazy fetching loads related entities only when accessed, reducing initial query size and speeding up page load.
📈 Performance GainReduces initial database load and speeds up LCP by deferring related data fetching.
Fetching related entities in a @ManyToMany relationship
Spring Boot
@Entity
public class Student {
  @ManyToMany(fetch = FetchType.EAGER)
  private Set<Course> courses;
}
Eager fetching loads all related entities immediately, causing large joins or multiple queries that slow down page load.
📉 Performance CostBlocks rendering until all related data loads; can trigger multiple heavy database queries increasing LCP.
Performance Comparison
PatternDatabase QueriesData SizePage Load ImpactVerdict
Eager Fetching @ManyToManyMultiple or large join queriesLarge data fetched upfrontSlower LCP due to blocking[X] Bad
Lazy Fetching @ManyToManyQueries only when accessedSmaller initial data sizeFaster LCP by deferring load[OK] Good
Rendering Pipeline
When a page requests data with @ManyToMany eager fetch, the backend queries all related entities before sending response, delaying HTML rendering.
Data Fetching
Backend Processing
HTML Rendering
⚠️ BottleneckData Fetching due to large or multiple SQL queries caused by eager loading
Core Web Vital Affected
LCP
This affects database query performance and page load speed when fetching related data in web applications.
Optimization Tips
1Use FetchType.LAZY for @ManyToMany to avoid loading large related data sets upfront.
2Avoid accessing lazy collections in the view layer without pagination or limits.
3Consider DTO projections or custom queries to fetch only needed data.
Performance Quiz - 3 Questions
Test your performance knowledge
What is the main performance issue with using FetchType.EAGER on a @ManyToMany relationship?
AIt loads all related entities immediately, causing slow database queries and page load.
BIt delays loading related entities until accessed, causing UI lag.
CIt caches related entities in the browser, increasing memory usage.
DIt prevents any related entities from loading automatically.
DevTools: Network
How to check: Open DevTools, go to Network tab, reload page, and observe backend API calls and their response times.
What to look for: Look for large or multiple slow API calls caused by fetching many related entities upfront.

Practice

(1/5)
1. What does the @ManyToMany annotation represent in Spring Boot?
easy
A. A relationship where entities inherit from each other.
B. A relationship where one entity has only one of the other entity.
C. A relationship where entities are unrelated.
D. A relationship where each entity can have many of the other entity.

Solution

  1. 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.
  2. Step 2: Compare with other relationship types

    Unlike one-to-one or one-to-many, many-to-many allows multiple connections on both sides.
  3. Final Answer:

    A relationship where each entity can have many of the other entity. -> Option D
  4. Quick 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
A. @JoinColumn(name = "student_course")
B. @JoinTable(name = "student_course", joinColumns = @JoinColumn(name = "student_id"), inverseJoinColumns = @JoinColumn(name = "course_id"))
C. @Table(name = "student_course")
D. @JoinTable(name = "student_course", joinColumns = @JoinColumn(name = "course_id"), inverseJoinColumns = @JoinColumn(name = "student_id"))

Solution

  1. 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.
  2. Step 2: Check column names match entities

    joinColumns should refer to the current entity's ID, inverseJoinColumns to the other entity's ID.
  3. Final Answer:

    @JoinTable(name = "student_course", joinColumns = @JoinColumn(name = "student_id"), inverseJoinColumns = @JoinColumn(name = "course_id")) -> Option B
  4. Quick 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
A. 2
B. 0
C. 1
D. Compilation error

Solution

  1. 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.
  2. Step 2: Confirm no errors in adding elements

    Adding elements to the set is valid and no exceptions occur here.
  3. Final Answer:

    2 -> Option A
  4. Quick 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
A. The @ManyToMany annotation is missing @JoinTable on both sides.
B. The mappedBy attribute is incorrectly used on the owning side.
C. The collections are not initialized, causing NullPointerException.
D. The entities must extend a common superclass.

Solution

  1. Step 1: Check collection initialization

    The sets 'books' and 'authors' are declared but not initialized, so they are null by default.
  2. Step 2: Understand impact of null collections

    Trying to add or access elements will cause NullPointerException at runtime.
  3. Final Answer:

    The collections are not initialized, causing NullPointerException. -> Option C
  4. Quick 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
A. student.getClubs().add(club); club.getStudents().add(student);
B. student.getClubs().add(club);
C. club.getStudents().add(student);
D. student.setClubs(Set.of(club));

Solution

  1. Step 1: Understand bidirectional @ManyToMany updates

    Both sides must be updated to keep the relationship consistent in memory.
  2. 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.
  3. Final Answer:

    student.getClubs().add(club); club.getStudents().add(student); -> Option A
  4. Quick 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