Add graphql-dataloader: Spring GraphQL 2.0 DataLoader batching vs naive N+1, and non-null propagation on a dangling FK

- naive @SchemaMapping resolver: 6 statements (5 books/5 authors), 21 statements (20 books/5 authors)
- batched @BatchMapping resolver: flat 2 statements in both cases, via DataLoader + .distinct()
- dangling authorId nulls the entire GraphQL response via non-null propagation, byte-identical under both resolver strategies
- 6-test suite over real HTTP against a live embedded Tomcat instance, SQL captured via a JDK dynamic proxy (StatementLoggingDataSource, reused from sdjpa4-demo)
- docs/05: two Boot 4.1 packaging changes hit along the way (DataSourceAutoConfiguration's new package, Jackson 3 by default)
- root README: add row for graphql-dataloader; fix openapi-versioning's placeholder link now that post 7477 is live
This commit is contained in:
2026-09-17 19:57:41 +00:00
parent 8d0efb0d4b
commit baff043748
29 changed files with 1454 additions and 1 deletions
+100
View File
@@ -0,0 +1,100 @@
# Two Boot 4.1 packaging changes this module ran into directly
Neither of these is about GraphQL or DataLoader batching — they're genuine build-time surprises
from targeting Spring Boot 4.1.1 that are worth recording here because they cost real debugging
time and because searching for the old, Boot-3-era package names will still turn up plenty of
now-stale documentation and Stack Overflow answers.
## `DataSourceAutoConfiguration` moved packages
The very first `mvn clean package` on this module failed with:
```
[ERROR] package org.springframework.boot.autoconfigure.jdbc does not exist
[ERROR] cannot find symbol
symbol: class DataSourceAutoConfiguration
```
`@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)` is used here for the same
reason it's used in the sdjpa4-demo companion project: to stop Boot's own auto-configuration from
building a `DataSource` from `application.yml` properties, so the custom `@Bean DataSource` method
wrapping H2 in `StatementLoggingDataSource` is the only one that ever runs. In Spring Boot 3, that
class lived at `org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration`, inside
the single, large `spring-boot-autoconfigure` artifact.
As of Boot 4.1.1, `spring-boot-autoconfigure` only contains the generic auto-configuration
*infrastructure*`AutoConfigurationImportSelector`, `AutoConfigurations`, and similar — confirmed
directly by listing the jar:
```
$ unzip -l spring-boot-autoconfigure-4.1.1.jar | grep -i jdbc
(no output)
```
The actual per-feature auto-configuration classes now live in their own per-feature artifacts, in
new packages. `DataSourceAutoConfiguration` specifically is in `spring-boot-jdbc-4.1.1.jar`, at
`org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration` — the `jdbc` and
`autoconfigure` segments swapped relative to the old package name. The fix was a one-line import
change:
```diff
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
```
The sdjpa4-demo companion project (for the Spring Data JDBC vs. JPA article) already used the
correct new package — this module's `GraphqlDataloaderApplication.java` was written from habit
before that was double-checked here directly.
## Jackson 3 by default
Writing the HTTP-level test assertions for docs/02docs/04 initially used
`com.fasterxml.jackson.databind.{ObjectMapper,JsonNode}`, which failed to compile:
```
[ERROR] package com.fasterxml.jackson.databind does not exist
```
`mvn dependency:tree` on this module shows why:
```
+- org.springframework.boot:spring-boot-starter-jackson:jar:4.1.1:compile
| \- org.springframework.boot:spring-boot-jackson:jar:4.1.1:compile
| \- tools.jackson.core:jackson-databind:jar:3.1.5:compile
| +- com.fasterxml.jackson.core:jackson-annotations:jar:2.21:compile
| \- tools.jackson.core:jackson-core:jar:3.1.5:compile
```
Spring Boot 4.1's default Jackson integration is Jackson 3, whose `jackson-core` and
`jackson-databind` modules moved to the `tools.jackson.core` Maven group and the
`tools.jackson.databind` / `tools.jackson.core` Java packages — a deliberate break from the
`com.fasterxml.jackson.*` namespace Jackson 2 used. `jackson-annotations` is the one piece that
stayed on the old `com.fasterxml.jackson.core` group (at 2.21), since annotations are largely
shared/compatible across the 2.x/3.x line. The fix in this module's test classes was the same shape
as the `DataSourceAutoConfiguration` one — swap the import, not the code:
```diff
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import tools.jackson.databind.JsonNode;
+import tools.jackson.databind.ObjectMapper;
```
`ObjectMapper.writeValueAsString(...)` and `.readTree(...)` behave identically once the import is
fixed — this module didn't need any further changes to its JSON-handling code. Projects still on
`spring-boot-starter-web`/`spring-boot-starter-json` from a Boot 3.x background, or projects that
pull in `com.fasterxml.jackson.core:jackson-databind` directly rather than through a Boot starter,
should expect to hit this the first time they add any code that touches `ObjectMapper` or
`JsonNode` directly after upgrading to Boot 4.1.
## What was, and wasn't, independently verified here
Every SQL statement count and every GraphQL error message in docs/02docs/04 came from this
module's own test runs against real H2 and a real embedded Tomcat instance — nothing in those three
chapters is asserted from documentation or memory. The two packaging changes above were confirmed
directly by inspecting the actual `.jar` contents downloaded into the local Maven repository for
this build (`unzip -l`), not by trusting a changelog summary. What was *not* independently
re-derived from first principles here is the general architectural claim that Spring Boot 4
modularized auto-configuration into per-feature artifacts — that's stated as context, based on what
this module's own dependency tree and jar contents show, not as an exhaustively researched survey
of every module that moved.