ExerciseChallenge
Challenge
Six repository methods, four are N+1
25 minintermediate1–15 yrs
Edge cases. You have to reason, and two valid fixes differ.
What this teaches
- Reading a repository method is not enough; the mapping decides the query count
- Two of the six are correct and one of those is still the wrong shape for its screen
- Fewer queries is not the goal — rows on the wire matter as much
- Pagination rules out the fix that works everywhere else
Starter
Starter.java
import java.util.*;
/**
* Challenge: six repository methods from one service. Four cause N+1 queries.
*
* Two do not, and one of those two is still the wrong shape for the screen it
* serves. Work out the query count for each as a formula in N before you run
* anything.
*/
public class Starter {
// ── 1 ─────────────────────────────────────────────────────────────────
// List<Order> findAll();
// ...then the caller does order.getItems().size() for each.
// Order.items is @OneToMany(fetch = LAZY), no @BatchSize.
// ── 2 ─────────────────────────────────────────────────────────────────
// @Query("select distinct o from Order o join fetch o.items where o.status = :s")
// List<Order> findWithItems(Status s);
// ── 3 ─────────────────────────────────────────────────────────────────
// @Query("select o from Order o join fetch o.items")
// Page<Order> findAllWithItems(Pageable pageable);
// ── 4 ─────────────────────────────────────────────────────────────────
// @EntityGraph(attributePaths = {"items"})
// List<Order> findByStatus(Status s);
// ...and the caller then reads order.getCustomer().getName() for each.
// ── 5 ─────────────────────────────────────────────────────────────────
// List<Order> findByCustomerId(Long id);
// Order.items is @OneToMany(fetch = LAZY) @BatchSize(size = 50).
// The caller reads items for all of them.
// ── 6 ─────────────────────────────────────────────────────────────────
// List<Order> findRecent(); // returns entities
// ...used by a dashboard that renders only id, reference and item count.
public static void main(String[] args) {
// TODO 1: for each of the six, write the query count as a formula in N
// (the number of orders returned). Do this before running anything —
// then model each one and check yourself.
// TODO 2: four of them are N+1. Name them, and for each give the
// specific fix and why the other three fixes are worse here.
// TODO 3: method 3 issues exactly one query. Count the ROWS it reads
// for a page of 20 out of 100,000 orders. Explain the number, and name
// the log line that would have told you.
// TODO 4: method 4 has an entity graph and still does N+1. Where?
// This is the most common way a "fixed" method stays broken.
// TODO 5: method 5 is not 1 + N and not 1. Give its formula and say
// what setting would give every other method the same treatment
// without touching any of them.
// TODO 6: method 6 is not an N+1 at all and is still the wrong tool.
// Say what it should return instead, and what that removes besides
// queries.
// TODO 7: rank all six by rows read for N = 100,000, not by query
// count. The ranking changes. Say what that tells you about "fewer
// queries is better".
// TODO 8: write the test that would have caught methods 1, 3 and 4 —
// one test, asserting an exact number. Say why asserting "fewer than
// 10" would have been useless.
}
}Run it locally:
cd exercises/java/jpa-hibernate/n-plus-one/02-challenge
javac Starter.java -d /tmp/out && java -cp /tmp/out StarterHints
Hint 1
For each method, write down the query count as a formula in N before you run anything. Then check yourself.
Hint 2
One method has one query and reads more rows than any of the others. Find it by counting rows, not statements.
Hint 3
One is a correct fetch join that becomes wrong the moment a Pageable is added. Add one and watch what changes.
Hint 4
One needs no entities at all. Ask what the screen actually renders.
Done when
- A query-count formula in N for each of the six
- The four N+1s identified, each with a specific fix
- The one-query method that reads the most rows is identified
- You said which method should not be returning entities, and why
- You wrote a test that fails when a fix is reverted
← Back to What is the N+1 problem, and how do you detect it?