# spring-batch Companion code for **[Spring Batch on Boot 4.1: Jobs, Steps, Chunk Processing and Restartability](https://ankurm.com/)** on [ankurm.com](https://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](docs/03-chunk-oriented-processing.md), [ch. 4](docs/04-item-processor-as-filter.md) | | `broken` | No fault tolerance: a bad row fails the whole chunk and the job; restart resumes exactly where it left off | [ch. 7](docs/07-restartability.md) | | `skip` | `faultTolerant().skip(...)`: the same bad row is skipped instead, job completes in one pass | [ch. 8](docs/08-skip-vs-restart.md) | | `--spring.autoconfigure.exclude=...BatchJdbcAutoConfiguration` | Resourceless job repository: a restart forgets everything happened | [ch. 10](docs/10-resourceless-vs-jdbc.md) | ## Documentation chapters 1. [The problem, and the smallest correct mental model](docs/01-the-problem-and-mental-model.md) 2. [The anatomy of a job](docs/02-anatomy-of-a-job.md) 3. [Chunk-oriented processing](docs/03-chunk-oriented-processing.md) 4. [The item processor as a filter](docs/04-item-processor-as-filter.md) 5. [Launching a job, and why restart is not a separate API](docs/05-launching-and-jobparameters.md) 6. [The JDBC writer, and why it is not `beanMapped()`](docs/06-jdbc-writer-and-records.md) 7. [Restartability: what actually resumes, and from where](docs/07-restartability.md) 8. [Skip vs. restart](docs/08-skip-vs-restart.md) 9. [Corrections found while writing this](docs/09-corrections.md) 10. [Resourceless vs. JDBC-backed job repositories](docs/10-resourceless-vs-jdbc.md) 11. [Production checklist](docs/11-production-checklist.md) ## Captured output Everything under [`docs/output/`](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. ```bash 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: ```bash 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 ` wraps that for repeat use. Each scenario gets its own file-based H2 database under `scenario-data//`, 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](docs/10-resourceless-vs-jdbc.md) suggests checking your own job repository in production.