skip to content

How do you construct a Job and a Step using the JobBuilder/StepBuilder DSL in Spring Batch 5?

level: middleimportance: must knowfreq 78%

answer

  1. 5.x: no more JobBuilderFactory/StepBuilderFactory
  2. new JobBuilder(name, jobRepository)
  3. new StepBuilder(name, jobRepository).chunk(n, txManager)
  4. chunk/tasklet need PlatformTransactionManager
  5. start().next() -> SimpleJob; flow/split -> FlowJob

basics

~20 s

In Spring Batch 5 you instantiate builders directly: new JobBuilder(name, jobRepository) and new StepBuilder(name, jobRepository). The Step builder then picks chunk or tasklet (passing a transaction manager), and the Job builder chains steps with start()/next().

solid answer

~30 s

Spring Batch 5 removed `JobBuilderFactory`/`StepBuilderFactory`; you now construct builders directly and inject the collaborators. A Step: `new StepBuilder("step", jobRepository).chunk(100, txManager).reader(r).processor(p).writer(w).build()` for chunk work, or `.tasklet(myTasklet, txManager).build()` for a single action. A Job: `new JobBuilder("job", jobRepository).start(step1).next(step2).build()`. Key points: both builders require a `JobRepository` (for persisting execution metadata); chunk/tasklet builders additionally require a `PlatformTransactionManager` because each chunk/tasklet runs in a transaction. `JobBuilder.start(step)` returns a `SimpleJobBuilder` whose `.build()` yields a `SimpleJob`; using `.flow(...)`/`.split(...)` instead yields a `FlowJobBuilder` → `FlowJob`. You typically define these as `@Bean`s so Spring wires the JobRepository and transaction manager, which `@EnableBatchProcessing` or the Boot batch autoconfiguration provides.

code

java · 30 lines
java
@Bean
public Step loadStep(JobRepository jobRepository,
                     PlatformTransactionManager txManager,
                     ItemReader<Order> reader,
                     ItemWriter<Order> writer) {
    return new StepBuilder("loadStep", jobRepository)
            .<Order, Order>chunk(100, txManager) // chunk size + tx manager required in 5.x
            .reader(reader)
            .writer(writer)
            .build();
}

@Bean
public Step reportStep(JobRepository jobRepository, PlatformTransactionManager txManager) {
    return new StepBuilder("reportStep", jobRepository)
            .tasklet((contribution, chunkContext) -> {
                // single action
                return RepeatStatus.FINISHED;
            }, txManager)
            .build();
}

@Bean
public Job importJob(JobRepository jobRepository, Step loadStep, Step reportStep) {
    return new JobBuilder("importJob", jobRepository)
            .incrementer(new RunIdIncrementer())
            .start(loadStep)   // SimpleJobBuilder
            .next(reportStep)
            .build();          // SimpleJob
}

go deeper

for a junior

Know that JobBuilder/StepBuilder create jobs and steps and you chain steps with start()/next().

for a middle

Know the 5.x direct-instantiation form and that chunk/tasklet need a transaction manager while the job needs a JobRepository.

for a senior

Explain SimpleJobBuilder vs FlowJobBuilder branching, incrementer/validator/listener hooks, and the 4→5 factory removal.

for a principal

Reason about @EnableBatchProcessing vs Boot autoconfig interactions and when to bypass the DSL for framework-level customization.

The **builder DSL** is the idiomatic way to define Jobs and Steps in Java config. There are two builders: **`JobBuilder`** and **`StepBuilder`** (package `org.springframework.batch.core.job.builder` / `...step.builder`). **Historical note (important for version questions):** In Spring Batch 4.x you obtained builders from `JobBuilderFactory` and `StepBuilderFactory` beans. **Spring Batch 5.0 removed those factories.** You now `new` the builders yourself and pass the collaborators explicitly. **Building a Step.** Start with `new StepBuilder("stepName", jobRepository)`. The builder is a *fluent state machine*: your next call chooses the step type and returns a more specialized builder: - `.chunk(chunkSize, transactionManager)` → a `SimpleStepBuilder` for chunk-oriented processing, on which you set `.reader(...)`, `.processor(...)`, `.writer(...)`. - `.tasklet(tasklet, transactionManager)` → a `TaskletStepBuilder` for a single arbitrary action. Both ultimately `.build()` to a `Step` (concretely a `TaskletStep`, since chunk processing is implemented as a chunk-oriented tasklet). Note the **`PlatformTransactionManager` is mandatory** here: every chunk (or tasklet invocation) executes inside a transaction, so the builder needs the transaction manager. **Building a Job.** Start with `new JobBuilder("jobName", jobRepository)`. The **`JobRepository`** is required because the job must persist `JobExecution`/`StepExecution` metadata. Then: - `.start(step)` begins a linear sequence and returns a **`SimpleJobBuilder`**; chain `.next(step2).next(step3)` and finish with `.build()` → a **`SimpleJob`**. - `.start(flow)` / `.flow(flow)` / `.split(taskExecutor)` return a **`FlowJobBuilder`** → a **`FlowJob`**, enabling conditional transitions (`.on("FAILED").to(...)`), parallel splits, and decisions. You may also set `.incrementer(new RunIdIncrementer())`, `.validator(...)`, `.listener(...)`, and `.preventRestart()` on the job builder. **Wiring.** These are normally `@Bean` methods. The `JobRepository` and a `PlatformTransactionManager` are provided by `@EnableBatchProcessing` (or Spring Boot's batch autoconfiguration), so you just declare them as method parameters and Spring injects them. **Gotchas:** 1. Forgetting the transaction manager on `.chunk()`/`.tasklet()` won't compile in 5.x (it's a required argument) — a common surprise when migrating 4.x code that relied on the factory to supply it. 2. `.start(...).next(...)` gives strictly sequential execution (a `SimpleJob`). Conditional or parallel behavior requires switching into the flow builder. 3. A `StepBuilder` instance is single-use per step; reusing state across builds leads to confusing configuration. Create a fresh builder per step bean. 4. In 5.x `@EnableBatchProcessing` is optional under Spring Boot (Boot autoconfigures batch); adding it explicitly can actually *disable* Boot's autoconfiguration — know your setup. **When to use:** the builder DSL is the standard; only drop to instantiating `SimpleJob`/`TaskletStep` directly in rare framework-level customization.

  • Why does the Step builder require a PlatformTransactionManager but the Job builder does not?
    Steps execute chunks/tasklets inside transactions (commit per chunk), so the step needs a transaction manager. The Job only orchestrates steps and persists metadata via the JobRepository, so it doesn't run its own business transaction.
  • What changed between Spring Batch 4 and 5 in how you obtain these builders?
    4.x used injected JobBuilderFactory/StepBuilderFactory beans; 5.x removed them, so you instantiate JobBuilder/StepBuilder directly and pass the JobRepository (and transaction manager for steps) yourself.

saying these in an interview costs you the question

  • Still using JobBuilderFactory/StepBuilderFactory and claiming it's current 5.x
  • Saying the Job builder needs a PlatformTransactionManager
  • Thinking .start().next() can express conditional branching
  • Forgetting that chunk() needs both a size and a transaction manager

context