Companion code for "Spring Batch on Boot 4.1: Jobs, Steps, Chunk Processing and Restartability". A productImportJob configured three ways by profile against a poisoned CSV row, run as real java -jar processes (not just JUnit) so the restart story is genuine: a chunk fails and rolls back, the process exits, a brand-new JVM against the same file-based H2 database resumes at the exact next unread row (READ_COUNT 20, not 60) and completes. Findings the build pins: - StepBuilder.chunk(int, PlatformTransactionManager) still compiles in Batch 6.0.5 but returns the legacy SimpleStepBuilder; chunk(int) returns the new ChunkOrientedStepBuilder, and only the latter is used here. - Two different ExecutionContext classes now exist in two different packages (infrastructure.item vs core.repository.persistence) with different shapes. - spring-boot-starter-batch alone gives a resourceless JobRepository that forgets every JobInstance the moment the JVM exits; spring-boot-starter- batch-jdbc is what makes the restart demo possible at all, demonstrated by excluding BatchJdbcAutoConfiguration and watching a "restart" collide with the previous run's own data instead of resuming it. - A migration-guide summary claiming CommandLineJobRunner was removed in 6.0 is wrong -- javap against the real jar shows @Deprecated(forRemoval=true), not removed. - RepeatStatus moved from core.repeat to infrastructure.repeat, caught by the compiler rather than by reading docs. 11 documentation chapters, 10 captured transcripts (unit tests, javap output, and real two-JVM scenario runs), all regenerated by scripts/run-all.sh. Fixed after push: three dead docs.spring.io links in the doc chapters (readersAndWriters/* and chunk-oriented-processing/*.html paths moved when Spring Batch 6 reorganized its reference docs; corrected to the current readers-and-writers/*, processor.html and chunk-oriented-processing.html paths, verified 200 via curl before committing). Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019DXsJ1zpikbA1MQJN6RqFA
3.9 KiB
spring-batch
Companion code for Spring Batch on Boot 4.1: Jobs, Steps, Chunk Processing and Restartability on ankurm.com.
Verified against Spring Boot 4.1.1, Spring Batch 6.0.5, Spring Framework 7.0.9, on Temurin JDK 25.0.4.1+1.
One job (productImportJob: import a CSV of products, then report a count), configured three
different ways by Spring profile, plus one infrastructure variant that is not a profile:
| Profile / flag | What it demonstrates | Docs |
|---|---|---|
clean |
The happy path: chunk-oriented reading, filtering, writing | ch. 3, ch. 4 |
broken |
No fault tolerance: a bad row fails the whole chunk and the job; restart resumes exactly where it left off | ch. 7 |
skip |
faultTolerant().skip(...): the same bad row is skipped instead, job completes in one pass |
ch. 8 |
--spring.autoconfigure.exclude=...BatchJdbcAutoConfiguration |
Resourceless job repository: a restart forgets everything happened | ch. 10 |
Documentation chapters
- The problem, and the smallest correct mental model
- The anatomy of a job
- Chunk-oriented processing
- The item processor as a filter
- Launching a job, and why restart is not a separate API
- The JDBC writer, and why it is not
beanMapped() - Restartability: what actually resumes, and from where
- Skip vs. restart
- Corrections found while writing this
- Resourceless vs. JDBC-backed job repositories
- Production checklist
Captured output
Everything under docs/output/ was produced by a real run and is quoted verbatim
in the article and the chapters above:
| File | What produced it |
|---|---|
01-processor-filter.txt, 02-exception-hierarchy.txt |
JUnit tests, via mvn test |
03-stepbuilder-chunk-overloads.txt, 04-two-executioncontext-classes.txt, 09-commandlinejobrunner-deprecated-not-removed.txt |
javap against the real 6.0.5 jars, via scripts/capture-javap.sh |
05-happy-path.txt |
The clean profile, via scripts/capture-scenarios.sh |
07-restart-run1-fails.txt, 08-restart-run2-resumes.txt |
The broken profile, run twice in separate JVMs against the same database |
10-skip-instead-of-fail.txt |
The skip profile |
11-resourceless-forgets-everything.txt |
The clean profile with BatchJdbcAutoConfiguration excluded, run twice |
Running it
Needs a JDK 25 and Maven 3.9.
export JAVA_HOME=/path/to/jdk-25
mvn -DskipTests package
./scripts/run-all.sh # regenerates everything under docs/output/
Or run one scenario by hand:
java -jar target/spring-batch-1.0.0.jar \
--spring.profiles.active=clean \
--import.file=file:./scenario-data/clean-demo/input.csv \
--spring.datasource.url=jdbc:h2:file:./scenario-data/clean-demo/db
scripts/run-scenario.sh <name> <profile> <source-csv> wraps that for repeat use. Each scenario
gets its own file-based H2 database under scenario-data/<name>/, gitignored, so runs never
interfere with each other and a restart persists across separate java -jar invocations the way
it would across a real process restart.
There is a diagnostic-adjacent endpoint nowhere in this module by design — everything
observable here comes from the standard BATCH_STEP_EXECUTION table via a plain SQL query,
which is deliberately how chapter 10 suggests checking your
own job repository in production.