# 1. The problem, and the smallest correct mental model [← README](../README.md) | [Next: Anatomy of a job →](02-anatomy-of-a-job.md) A batch job processes a lot of records without a person watching each one: importing a CSV of products into a database, closing out a day's transactions, re-indexing a search table. Two things make this harder than writing a `for` loop over the rows: - **It has to survive being interrupted.** A process gets killed, a database connection drops, a row three-quarters of the way through is bad. A `for` loop that dies at row 4,700,000 has no idea it already wrote 4,699,999 rows, and re-running it from the top either duplicates work or duplicates data. - **It has to report what happened**, precisely: how many rows were read, how many were written, how many were skipped and why, whether it finished. "The import ran" is not an answer anyone building on top of this can use. Spring Batch's job is to own both of those problems so your code only has to describe three things: where the rows come from, what to do to each one, and where they go. Everything else — tracking progress, committing in batches, remembering where a failed run stopped — is the framework's job, provided you tell it enough for it to do that job. Most of this article is about that "provided." ## The five nouns
Job "productImportJob" start(importStep).next(reportStep) Step: importStep Step: reportStep importStep is chunk-oriented ItemReader ItemProcessor ItemWriter JobRepository every Job/Step execution, read/write/skip counts, ExecutionContext
The picture above names everything this article uses: - **Job** — a named, ordered list of steps. `productImportJob` in this repository has two: import the rows, then report a count. - **Step** — one unit of work within a job. A step is either a single **Tasklet** (run once, do one thing — `reportStep` here) or **chunk-oriented**: read one item, process it, repeat until you have a chunk's worth, then write and commit the whole chunk in one transaction. - **ItemReader / ItemProcessor / ItemWriter** — the three interfaces chunk-oriented processing is built from. This module's reader is a `FlatFileItemReader` over a CSV, the processor validates and filters rows, the writer is a `JdbcBatchItemWriter`. - **JobRepository** — the thing that makes the first two bullet points of this page possible. It persists every job and step execution: status, timestamps, and the read / write / filter / skip / commit / rollback counts you saw in the transcripts above. Chapter [10](10-resourceless-vs-jdbc.md) is entirely about a trap hiding in how this gets configured on Spring Boot 4.1. Hold onto one fact from this page, because it is the one everything else cashes in later: **a chunk commits or it doesn't, as a whole.** There is no partial chunk. That single sentence is the entire explanation for why restarting a failed job does not reprocess everything, and it is why the "poisoned" row in this repository's demo data fails nine other, perfectly good rows along with it — see [chapter 7](07-restartability.md). ## What this repository demonstrates, and how to run it Four Spring profiles configure the same job three different ways plus one infrastructure variant; see the [README](../README.md) for the full table and the exact commands. Everything under `docs/output/` was produced by `scripts/capture-scenarios.sh` and `scripts/capture-javap.sh`, which `scripts/run-all.sh` runs in sequence. [Next: Anatomy of a job →](02-anatomy-of-a-job.md)