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.4 KiB
3.4 KiB
11. Production checklist
A short list, aimed at the gap between "this demo works" and "this is safe to point at a real feed." Each item links back to the chapter that explains the why.
- Confirm the job repository is actually persistent — query for
BATCH_JOB_INSTANCEafter startup, don't assume from the dependency list. Chapter 10. - Pick chunk size from measurement, not habit. Smaller costs more round trips; larger costs more rework per failure. Chapter 3.
- Decide skip vs. fail-and-restart per failure type, not per job. A step can have a
skipLimitfor genuinely expected bad rows and still fail hard past that limit — the two are not mutually exclusive. Chapter 8. - Use
chunk(int).transactionManager(tx), notchunk(int, tx), unless you specifically need the legacySimpleStepBuilderfor something the new model does not yet cover. Chapter 2. - Do not use
beanMapped()against a Java record without checking your exact Spring Framework version's record support; preferitemPreparedStatementSetterwhen in doubt. Chapter 6. - Give every step you want to always re-run on restart
allowStartIfComplete(true)explicitly — a reporting or notification step that silently gets skipped after a restart is a surprising, hard-to-notice gap. Chapter 8. - A custom
ItemReaderthat does not implementItemStreamrestarts from scratch every time, which is fine for an idempotent reader and a bug for anything else — check this deliberately rather than by finding out during an incident. Chapter 7. spring.batch.job.enabled=falseif you launch jobs yourself throughJobOperator, so the auto-configured runner does not also try. Chapter 5.- Delete or secure any diagnostic endpoint you add while building this out. This module has
none, but the pattern (an endpoint dumping live
JobExplorerstate) is common enough to call out: it is invaluable while developing and a liability left in production.
Should you even build a custom batch job for this?
Sometimes the honest answer is no. If the volume is small enough to fit in a single request-response
cycle and does not need to survive a crash mid-way, a scheduled @Component method with its own
try/catch is less machinery than a full Job/Step/JobRepository setup, and easier for the next
person to read. Spring Batch earns its complexity when you actually need the things this article
demonstrates — durable progress tracking, transactional chunking, a real restart story
— not by default just because the word "batch" is in the requirements.
Further reading
- Spring Batch reference documentation (
rel="nofollow") - What's new in Spring Batch 6 (
rel="nofollow") - Spring Batch 6.0 Migration Guide (
rel="nofollow", and see chapter 9 for where this specific document was wrong) - Spring Boot 4.1.0 release announcement (
rel="nofollow")