# 2. The anatomy of a job, and why the code looks different from older tutorials [← Previous](01-the-problem-and-mental-model.md) | [README](../README.md) | [Next: Chunk-oriented processing →](03-chunk-oriented-processing.md) `BatchConfig` in this module builds jobs and steps like this: ```java new JobBuilder("productImportJob", jobRepository) .start(importStep) .next(reportStep) .build(); new StepBuilder("importStep", jobRepository) .chunk(10) .transactionManager(transactionManager) .reader(productReader) .processor(productProcessor) .writer(productWriter) .build(); ``` If you have seen Spring Batch code before 2023 or so, two things here might look unfamiliar, and it is worth being precise about which is a genuine Spring Boot 4.1 change and which is just old: - `JobBuilderFactory` / `StepBuilderFactory` autowired as beans, then called as `jobBuilderFactory.get("name")` — **this was removed in Spring Batch 5, years before this article.** It is not a Boot 4.1 surprise, it is a dead end that tutorials keep copy-pasting. `JobBuilder` and `StepBuilder` are plain classes you construct directly with a `JobRepository`, as above. - `.chunk(10, transactionManager)` — passing the transaction manager as a second argument to `chunk()` — **this compiles in 6.0.5, but it returns a different builder.** `StepBuilder.chunk(int)` returns a `ChunkOrientedStepBuilder`, the model this whole module uses; `StepBuilder.chunk(int, PlatformTransactionManager)` returns the older `SimpleStepBuilder`, kept for the pre-6.0 chunk-processing model. Real `javap` output for both overloads, from this project's own `spring-batch-core-6.0.5.jar`: [`docs/output/03-stepbuilder-chunk-overloads.txt`](output/03-stepbuilder-chunk-overloads.txt). The two builders overlap almost entirely in the methods you would reach for — `reader`/`processor`/`writer`/`faultTolerant`/`skip` exist on both — so the wrong choice usually still compiles and runs, and just quietly uses the legacy step implementation. Call `.chunk(10).transactionManager(tx)` (two calls) if you want `ChunkOrientedStep`.
The fingerprint of picking the wrong overload. Your step still builds, runs, reads, writes and commits correctly — there is no error. The tell is in what you can't reach: ChunkOrientedStepBuilder-only options like retryPolicy(RetryPolicy) using Spring Framework 7's retry API, or a stack trace mentioning ChunkOrientedStep versus the older TaskletStep wrapping a ChunkOrientedTasklet. If you copied a two-argument chunk(10, txManager) from a pre-6.0 example and it "just worked", this is why nothing complained.
## Where things moved Spring Batch 6.0 (bundled with Spring Boot 4.1.1 as `spring-batch.version` `6.0.5`, confirmed against `spring-boot-dependencies-4.1.1.pom`) reorganized packages fairly aggressively. The ones this module's code actually hits: | Old (Spring Batch 5.x) | New (6.0.5) | |---|---| | `org.springframework.batch.core.repeat.RepeatStatus` | `org.springframework.batch.infrastructure.repeat.RepeatStatus` | | `org.springframework.batch.item.*` (readers, writers, `ExecutionContext`) | `org.springframework.batch.infrastructure.item.*` | | `org.springframework.batch.core.JobParameters` (mutable-ish, `Map`-backed) | `org.springframework.batch.core.job.parameters.JobParameters` (immutable record, `Set`-backed) | The `RepeatStatus` move is the one this project's own `reportTasklet` hit directly — see [chapter 9](09-corrections.md) for the exact compiler error it produced before the import was fixed. It is a good example of the article's verification rule in practice: rather than trusting a description of where things moved, write the code from the old import, let `javac` say `package ... does not exist`, and fix the import. That is faster and more reliable than reading a migration guide's prose, and chapter 9 has a case where trusting the prose produced a wrong claim. ## JobRepository and JobOperator Two interfaces you inject rather than configure by hand in this module: - **`JobRepository`** — every `JobBuilder` and `StepBuilder` above takes one as a constructor argument. In 6.0 it also extends `JobExplorer` (querying past executions), so there is one bean to inject instead of two. - **`JobOperator`** — what `ImportRunner` calls to start the job (see [chapter 5](05-launching-and-jobparameters.md)). It extends `JobLauncher` and adds operational methods: `restart(executionId)`, `stop(executionId)`, `recover(execution)`, `getJobNames()`. `JobLauncher`/`JobExplorer` still exist but are the deprecated half of this pair now. Both are auto-configured by `spring-boot-starter-batch-jdbc` on this module's classpath; nothing in `BatchConfig` declares them as beans. [Chapter 10](10-resourceless-vs-jdbc.md) explains what you get instead if that starter is missing. [Next: Chunk-oriented processing →](03-chunk-oriented-processing.md)