# The naive resolver, and exactly how it becomes N+1 `NaiveAuthorResolver` is the resolver nobody would flag in code review: ```java @Profile("naive") @Controller public class NaiveAuthorResolver { private final AuthorRepository authorRepository; public NaiveAuthorResolver(AuthorRepository authorRepository) { this.authorRepository = authorRepository; } @SchemaMapping(typeName = "Book", field = "author") public Author author(Book book) { return authorRepository.findById(book.getAuthorId()).orElse(null); } } ``` Source: [`resolver/NaiveAuthorResolver.java`](../src/main/java/com/ankurm/graphqldataloader/resolver/NaiveAuthorResolver.java) One method, one repository call, correct output for any individual `Book`. `@SchemaMapping` targets a specific field of a specific type (`Book.author`), and Spring GraphQL calls that method once per `Book` instance GraphQL Java needs to resolve the field for — independently, with no visibility into what the other invocations are doing. If the query asks for 5 books, this method runs 5 times; if it asks for 20, it runs 20 times. That's not a framework limitation being worked around badly, it's simply what `@SchemaMapping` is: a per-object field resolver, full stop. ## The evidence, at two scales Test class: [`NaiveResolverSqlLogTest`](../src/test/java/com/ankurm/graphqldataloader/NaiveResolverSqlLogTest.java), run with `@ActiveProfiles("naive")`. Every query in these transcripts came from a real HTTP POST to `/graphql` on a live, randomly-ported embedded Tomcat instance under `@SpringBootTest`, captured by the JDK dynamic proxy in `StatementLoggingDataSource` — the same mechanism from the sdjpa4-demo companion project — not from Hibernate's own SQL logging, so what's counted is what actually reached H2. **5 books, 5 distinct authors** (`a_fiveBooksFromFiveDistinctAuthorsIssueOneAuthorQueryPerBook`): ``` 1. select b1_0.id,b1_0.author_id,b1_0.title from book b1_0 2. select a1_0.id,a1_0.name from author a1_0 where a1_0.id=? 3. select a1_0.id,a1_0.name from author a1_0 where a1_0.id=? 4. select a1_0.id,a1_0.name from author a1_0 where a1_0.id=? 5. select a1_0.id,a1_0.name from author a1_0 where a1_0.id=? 6. select a1_0.id,a1_0.name from author a1_0 where a1_0.id=? total statements: 6 ``` Source: [docs/output/naive-a-five-distinct-authors.txt](output/naive-a-five-distinct-authors.txt) One `books` query, five `author` queries. That's the "N+1" in its cleanest form: 1 query for the list, N queries for the thing every item in the list needs. **20 books, but only 5 distinct authors** (`b_twentyBooksFromFiveAuthorsStillIssuesTwentyAuthorQueries`) — each author now has 4 books: ``` 1. select b1_0.id,b1_0.author_id,b1_0.title from book b1_0 2. select a1_0.id,a1_0.name from author a1_0 where a1_0.id=? 3. select a1_0.id,a1_0.name from author a1_0 where a1_0.id=? ... 21. select a1_0.id,a1_0.name from author a1_0 where a1_0.id=? total statements: 21 ``` Full transcript: [docs/output/naive-b-twenty-books-five-authors.txt](output/naive-b-twenty-books-five-authors.txt) This is the case worth sitting with. There are only 5 *distinct* authors involved — a batched lookup could satisfy the whole request with a single `WHERE id IN (...)` query. The naive resolver issues 21 statements anyway, because `findById()` inside the per-`Book` callback has no way to know that the author it's about to fetch for book #6 is the same author it already fetched for book #2. Repetition in the data doesn't help this resolver at all — it costs exactly as much as 20 books by 20 different authors would have. That's the concrete cost `@BatchMapping` fixes in docs/03, and the `.distinct()` call that makes the fix worth having.