Logo
Blog
Fixing the N+1 Query Problem in JPA with Fetch Joins and EntityGraph

Fixing the N+1 Query Problem in JPA with Fetch Joins and EntityGraph

Avatar
Tejas Parmar
September 16, 2025

Introduction

When working with JPA and Hibernate, developers often encounter the N+1 query problem.

This issue occurs when lazy-loaded associations cause excessive SQL queries, leading to performance bottlenecks.

❌ The Problem: Lazy Loading

Consider the following code:

java
@Entity
public class Author {
@OneToMany(mappedBy = "author", fetch = FetchType.LAZY)
private List<Book> books;
}
@Service
public class AuthorService {
public List<AuthorDto> getAllAuthorsWithBooks() {
List<Author> authors = authorRepository.findAll(); // 1 query
return authors.stream()
.map(author -> new AuthorDto(
author.getName(),
author.getBooks().size() // N queries! One per author
))
.collect(Collectors.toList());
}
}

What happens here?

  • First, findAll() executes 1 query to fetch all authors.
  • For each author, author.getBooks() triggers another query.
  • If you have 100 authors → that’s 1 + 100 = 101 queries!

This is the N+1 query problem.

✅ Solution 1: Use Fetch Joins

With JPQL fetch joins, you can load related entities in a single query.

java
@Repository
public interface AuthorRepository extends JpaRepository<Author, Long> {
@Query("SELECT a FROM Author a LEFT JOIN FETCH a.books")
List<Author> findAllWithBooks();
}

Now, when you call findAllWithBooks(), Hibernate fetches both authors and their books in one optimized query.

✅ Solution 2: Use @EntityGraph

java
@EntityGraph is a clean alternative that avoids writing custom JPQL.
@Repository
public interface AuthorRepository extends JpaRepository<Author, Long> {
@EntityGraph(attributePaths = {"books"})
List<Author> findAll();
}

This tells JPA to eagerly fetch books whenever findAll() is called, reducing the N+1 issue.

✅ Solution 3: Use Custom Projection

Sometimes you don’t need the full entity, only summary data. Instead of loading all books, fetch only what you need.

java
public interface AuthorBookCountProjection {
String getName();
Long getBookCount();
}
@Repository
public interface AuthorRepository extends JpaRepository<Author, Long> {
@Query("SELECT a.name as name, COUNT(b) as bookCount " +
"FROM Author a LEFT JOIN a.books b GROUP BY a.id, a.name")
List<AuthorBookCountProjection> findAuthorsWithBookCount();
}

This avoids fetching unnecessary data while still giving book counts per author.

🔑 Key Takeaways

  • Lazy loading is useful but can cause hidden performance issues.
  • Always check your logs (Hibernate shows SQL queries) to spot N+1 problems.
  • Use fetch joins or @EntityGraph for eager loading when needed.
  • For summaries, use projections instead of fetching entire entities.

🚀 Benefits

  • Significant performance improvement by reducing SQL queries.
  • Cleaner, more maintainable code with EntityGraph and projections.
  • Avoids unnecessary database round-trips.

Conclusion

The N+1 query problem is one of the most common performance pitfalls in JPA/Hibernate.

By applying fetch joins, @EntityGraph, or projections, you can optimize database access and ensure your application scales smoothly.

Contact Us

Thank you for reading our comprehensive guide on "Fixing the N+1 Query Problem in JPA with Fetch Joins and EntityGraph" We hope you found it insightful and valuable. If you have any questions, need further assistance, or are looking for expert support in developing and managing your projects. our team is here to help!

Reach out to us for Your Project Needs:

🌐 Website: https://www.prometheanz.com

📧 Email: [email protected]


Copyright © 2025 PrometheanTech. All Rights Reserved.