All Articles
Deep Diveยท5 min read

Why Your JPA Queries Fire 100 Times (The N+1 Problem)

One innocent-looking loop over entities can trigger hundreds of SQL queries. Here is why JPA does it and the three ways to stop it.

Part of: Advanced Java Development

Your endpoint returns 100 orders and somehow runs 101 SQL queries. Your database CPU is pinned, latency is terrible, and the code looks completely normal. Welcome to the N+1 problem โ€” the single most common JPA performance bug.

The Symptom

List<Order> orders = orderRepository.findAll(); // 1 query for (Order order : orders) { System.out.println(order.getCustomer().getName()); // 1 query EACH }

One query loads the orders. Then each getCustomer() triggers another query to lazily load that order's customer. One hundred orders means one hundred extra queries โ€” N+1 total.

Why It Happens

JPA relationships are lazy by default (a good default โ€” it avoids loading data you never use). But "lazy" means "load it the moment you touch it." Touching it inside a loop means loading it once per iteration. Hibernate is faithfully doing what you asked; you just asked one row at a time.

Fix 1: Fetch Join

Tell JPQL to load the association in the same query:

@Query("SELECT o FROM Order o JOIN FETCH o.customer") List<Order> findAllWithCustomer();

One query, one join, all customers loaded. This is the most direct fix.

Fix 2: Entity Graph

Keep your method declarative and attach a graph:

@EntityGraph(attributePaths = {"customer"}) List<Order> findAll();

Same effect as a fetch join, without hand-writing JPQL.

Fix 3: Batch Fetching

When you truly need lazy loading, tell Hibernate to load lazy associations in batches instead of one at a time:

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

Now 100 customers load in 2 queries (50 each) instead of 100.

Prevention

  • โ–ธEnable SQL logging in dev (spring.jpa.show-sql=true) and watch the query count on every endpoint.
  • โ–ธAdd an assertion in tests that a request issues a bounded number of queries.
  • โ–ธTreat any per-row query inside a loop as a red flag in review.

The takeaway: N+1 is not a bug in JPA โ€” it is lazy loading meeting a loop. Fetch the association up front with a join or entity graph, or batch it, and 101 queries collapse back into one or two.

#Java#JPA#Hibernate#Performance

Want to go deeper?

Advanced Java Development

View Course