Bird
Raised Fist0
Spring Bootframework~5 mins

N+1 query problem in Spring Boot

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
Introduction

The N+1 query problem happens when your app asks the database too many times for related data. This slows down your app and wastes resources.

When loading a list of items and their related details from a database.
When you see your app making many small database calls instead of one big call.
When you want to improve app speed by reducing database queries.
When using ORM tools like JPA or Hibernate to fetch related entities.
When debugging slow page loads caused by database access.
Syntax
Spring Boot
List<Entity> entities = repository.findAll();
for (Entity e : entities) {
    Related related = e.getRelated(); // triggers extra query per entity
}

This code fetches all entities first, then for each entity fetches related data separately.

Each call to getRelated() can cause a new database query, leading to many queries.

Examples
This example shows fetching orders, then for each order fetching its customer separately, causing N+1 queries.
Spring Boot
List<Order> orders = orderRepository.findAll();
for (Order order : orders) {
    Customer customer = order.getCustomer();
    System.out.println(customer.getName());
}
This example uses a JOIN FETCH query to get orders and customers in one query, avoiding N+1 problem.
Spring Boot
@Query("SELECT o FROM Order o JOIN FETCH o.customer")
List<Order> findAllWithCustomers();
Sample Program

This Spring Boot example shows how to avoid the N+1 query problem by using a JOIN FETCH query. It loads orders and their customers in one database call, then prints customer names.

Spring Boot
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import jakarta.persistence.*;
import java.util.List;

@Entity
class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    private Customer customer;

    public Customer getCustomer() { return customer; }
}

@Entity
class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    public String getName() { return name; }
}

@Repository
interface OrderRepository extends JpaRepository<Order, Long> {
    @Query("SELECT o FROM Order o JOIN FETCH o.customer")
    List<Order> findAllWithCustomers();
}

public class NPlusOneDemo {
    private final OrderRepository orderRepository;

    public NPlusOneDemo(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    public void showOrders() {
        List<Order> orders = orderRepository.findAllWithCustomers();
        for (Order order : orders) {
            System.out.println(order.getCustomer().getName());
        }
    }
}
OutputSuccess
Important Notes

Lazy loading causes the N+1 problem because related data loads only when accessed.

Using JOIN FETCH or entity graphs helps load related data eagerly in one query.

Check your SQL logs or use a profiler to spot N+1 queries in your app.

Summary

The N+1 query problem happens when your app makes one query for a list, then one query per item for related data.

This slows down your app and wastes database resources.

Use JOIN FETCH or similar techniques to load related data in one query and avoid this problem.

Practice

(1/5)
1. What is the N+1 query problem in Spring Boot applications?
easy
A. Not using any database queries at all
B. Making only one query to fetch all data including related entities
C. Using incorrect SQL syntax in queries
D. Making one query to fetch a list, then one query per item to fetch related data

Solution

  1. Step 1: Understand the query pattern

    The N+1 problem occurs when the app first fetches a list (1 query), then fetches related data for each item separately (N queries).
  2. Step 2: Identify the problem impact

    This causes many queries, slowing down the app and wasting resources.
  3. Final Answer:

    Making one query to fetch a list, then one query per item to fetch related data -> Option D
  4. Quick Check:

    N+1 query problem = multiple queries instead of one [OK]
Hint: N+1 means 1 query + N queries for related data [OK]
Common Mistakes:
  • Thinking N+1 means only one query is made
  • Confusing it with syntax errors
  • Assuming it is about missing queries
2. Which of the following is the correct way to use JOIN FETCH in a Spring Data JPA query to avoid the N+1 problem?
easy
A. @Query("SELECT o FROM Order o JOIN FETCH o.items")
B. @Query("SELECT o FROM Order o JOIN o.items")
C. @Query("SELECT o FROM Order o LEFT JOIN o.items")
D. @Query("SELECT o FROM Order o WHERE o.items IS NOT NULL")

Solution

  1. Step 1: Understand JOIN FETCH usage

    JOIN FETCH tells JPA to fetch related entities eagerly in one query, avoiding multiple queries.
  2. Step 2: Identify correct syntax

    @Query("SELECT o FROM Order o JOIN FETCH o.items") uses JOIN FETCH correctly to fetch orders with their items in one query.
  3. Final Answer:

    @Query("SELECT o FROM Order o JOIN FETCH o.items") -> Option A
  4. Quick Check:

    JOIN FETCH = eager fetch to avoid N+1 [OK]
Hint: Use JOIN FETCH to load related data in one query [OK]
Common Mistakes:
  • Using JOIN without FETCH causes lazy loading
  • Using WHERE instead of JOIN FETCH
  • Missing FETCH keyword
3. Given this Spring Data JPA repository method:
@Query("SELECT c FROM Customer c")
List<Customer> findAllCustomers();

And assuming Customer has a lazy-loaded orders collection, what happens when you call findAllCustomers() and then access orders for each customer?
medium
A. One query to get customers, then one query per customer to get orders (N+1 problem)
B. One query to get customers and all orders in one go
C. No queries are made until orders are accessed
D. An error occurs because orders are not fetched

Solution

  1. Step 1: Analyze the query and lazy loading

    The query fetches customers only; orders are lazy-loaded, so not fetched initially.
  2. Step 2: Accessing orders triggers queries

    Accessing orders for each customer triggers one query per customer, causing N+1 queries total.
  3. Final Answer:

    One query to get customers, then one query per customer to get orders (N+1 problem) -> Option A
  4. Quick Check:

    Lazy loading causes N+1 queries [OK]
Hint: Lazy loading causes one query per item when accessed [OK]
Common Mistakes:
  • Assuming all data loads in one query
  • Thinking no queries run until orders accessed
  • Confusing lazy and eager loading
4. You have this code snippet causing N+1 queries:
List<Author> authors = authorRepository.findAll();
for (Author a : authors) {
    System.out.println(a.getBooks().size());
}

How can you fix it to avoid the N+1 problem?
medium
A. Add @Transactional annotation to the method
B. Call getBooks() inside a separate thread
C. Change repository method to use @Query("SELECT a FROM Author a JOIN FETCH a.books")
D. Remove the loop and print authors only

Solution

  1. Step 1: Identify cause of N+1

    Calling getBooks() inside loop triggers one query per author due to lazy loading.
  2. Step 2: Use JOIN FETCH to load books eagerly

    Changing repository query to use JOIN FETCH loads authors and books in one query, avoiding N+1.
  3. Final Answer:

    Change repository method to use @Query("SELECT a FROM Author a JOIN FETCH a.books") -> Option C
  4. Quick Check:

    JOIN FETCH fixes N+1 by eager loading [OK]
Hint: Use JOIN FETCH in query to load related data eagerly [OK]
Common Mistakes:
  • Adding @Transactional does not fix N+1
  • Using threads does not solve query count
  • Removing loop hides problem but does not fix it
5. You have entities Post and Comment with a one-to-many lazy relationship. You want to fetch all posts with their comments efficiently. Which approach best avoids the N+1 problem and handles posts with no comments?
hard
A. Use native SQL without JOIN FETCH and map manually
B. @Query("SELECT p FROM Post p LEFT JOIN FETCH p.comments") to fetch posts and comments in one query
C. Fetch posts first, then fetch comments in a separate query for each post
D. Fetch posts only and ignore comments to reduce queries

Solution

  1. Step 1: Understand lazy loading and N+1

    Lazy loading comments causes one query per post when accessed, causing N+1 problem.
  2. Step 2: Use LEFT JOIN FETCH to include posts without comments

    LEFT JOIN FETCH fetches posts and their comments in one query, including posts with no comments.
  3. Final Answer:

    @Query("SELECT p FROM Post p LEFT JOIN FETCH p.comments") to fetch posts and comments in one query -> Option B
  4. Quick Check:

    LEFT JOIN FETCH avoids N+1 and includes empty collections [OK]
Hint: Use LEFT JOIN FETCH to include all posts and comments [OK]
Common Mistakes:
  • Using INNER JOIN FETCH excludes posts without comments
  • Fetching comments separately causes N+1
  • Ignoring comments loses needed data