Join FETCH May Not Save You: checking seven JPA N+1 beliefs against the SQL A JPA N+1 detector run by ExoBench over three real codebases found that common folklore fixes for N+1 query problems often fail: a JOIN FETCH in a second query did not reduce an N+1 from 12 statements, fetching a lazy parent collection left EAGER grandchildren at 25 statements, setMaxResults next to a collection join fetch silently dropped its LIMIT on Hibernate 5.6 and 6.6, and EclipseLink 4.0.9 accepted a nested JOIN FETCH without generating a join. The detector, which compiles mappings and runs them against an ephemeral database on real ORMs, measured an 8-item cart in Shopizer 3.2.7 costing 33 SQL statements and a 6-row loan page in Apache Fineract costing 52, highlighting that while AI agents can find N+1 sites, verifying fixes requires measurement, not folklore. Summary for the Impatient I ran a JPA N+1 detector over three real codebases: Shopizer 3.2.7 on Hibernate 5.6, Apache Fineract on EclipseLink 4.0.9, and Spring PetClinic on Hibernate 6.6. It found the expected fan-outs, an 8-item cart costing 33 SQL statements, a 6-row loan page costing 52. Then I used it on the fixes, and that's where it got uncomfortable. A JOIN FETCH in a second query did nothing for entities already loaded: the N+1 stayed at 12. Fetching a lazy parent collection left the EAGER grandchildren alone: still 25 statements. setMaxResults next to a collection join fetch silently dropped its LIMIT on two different Hibernate major versions. And EclipseLink accepted a nested JOIN FETCH written through an alias, generated no join for it, and said nothing. Every one of these looked correct in the source. Only half of them were the fix. That gap is the thesis. A coding agent is genuinely good at finding where your N+1s live, it has read a million public codebases full of them. What it cannot do from reading is tell you what a fix will actually do, because its fix knowledge is the same folklore yours is. Finding the problem is cheap now. Knowing the fix worked is what has to be measured. An 8-item shopping cart. One GET request. 33 SQL statements. That's garden variety N+1 The victim is Shopizer 3.2.7, verified byte-identical to upstream by git diff. No planted bug, no stripped fetch graph. The cart read path resolves each item's SKU with a native query, loads the product by id through a wide fetch graph descriptions, availabilities, prices, categories, images, attributes, variants , then loads the variant and the attribute by id. Four statement shapes, eight repetitions each, plus the cart load itself. On the path a store exists to serve. The transcript came out of ExoBench /docs/01-getting-started/02-how-it-works 's JPA N+1 detector, an MCP server your coding agent calls. The agent hands it a mapping and an access path. It compiles them, runs them against an ephemeral database on the real ORM, and returns the SQL that was actually prepared, plus the statement shapes it flagged. No build of the target app, no runtime, no JVM agent, no specialized instrumentation annotations, no production traffic. It runs Hibernate 5.6 through 7 and EclipseLink 2.7 through 5.0, and every probe below names the engine it ran on. Three codebases went through it. Shopizer 3.2.7 on Hibernate 5.6.15. Apache Fineract, which is on EclipseLink 4.0.9 with static weaving, and that pairing matters below. Spring PetClinic, probed on Hibernate 6.6. Every decent foundation model can find N+1 fan-outs reliably well, and the findings are real but ordinary. An LLM agent with no access to a real compiler is already good at it, because public codebases are full of exactly these mistakes, and that's what the models are trained on. Point one at a repository and it will hand you a plausible list of N+1 sites by pure reading. The breakdown comes when you start asking your LLM to reason correctly about the right fixes. The same agent that finds the problem, will recommend a defunct folklore-based fix with the same confidence, because it was trained on folklore fixes My agent made most of the same bad predictions that I would have, and the transcripts falsified us both. Seven measurements are below, and each contradicts something a competent JPA developer tells his juniors over a campfire. In no particular order, here they are: A JOIN FETCH in a second query heals nothing you already hold The instinct: you loaded your loans, you see the installment-charge N+1, so you run a second query with JOIN FETCH on those collections and keep iterating the entities from the first query. Simplified, the pattern looks like this: List