Files
spring-boot-demo/spring-batch
Claude b81af72bc3 Add spring-batch: jobs, steps, chunk processing and restartability on Boot 4.1
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
2026-09-13 06:37:46 +00:00
..

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

  1. The problem, and the smallest correct mental model
  2. The anatomy of a job
  3. Chunk-oriented processing
  4. The item processor as a filter
  5. Launching a job, and why restart is not a separate API
  6. The JDBC writer, and why it is not beanMapped()
  7. Restartability: what actually resumes, and from where
  8. Skip vs. restart
  9. Corrections found while writing this
  10. Resourceless vs. JDBC-backed job repositories
  11. 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.