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
75 lines
3.9 KiB
Markdown
75 lines
3.9 KiB
Markdown
# 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 <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](docs/10-resourceless-vs-jdbc.md) suggests checking your
|
|
own job repository in production.
|