# 5. Launching a job, and why restart is not a separate API [← Previous](04-item-processor-as-filter.md) | [README](../README.md) | [Next: The JDBC writer and Java records →](06-jdbc-writer-and-records.md) [`ImportRunner`](../src/main/java/com/ankurm/batch/runner/ImportRunner.java) launches the job with: ```java var params = new JobParametersBuilder() .addString("batch.run", "demo") // identifying .toJobParameters(); try { var execution = jobOperator.start(job, params); } catch (JobInstanceAlreadyCompleteException e) { // already succeeded; nothing to do } ``` `JobParametersBuilder` and `addString(...)` look exactly like Spring Batch 4 and 5. What changed underneath is the type it builds: `JobParameters` is now an immutable record holding a `Set>`, and each `JobParameter` is itself a record carrying its own name, value, type, and an `identifying` flag (default `true` for the `addX` methods used here). None of that shows up in this code — it matters if you ever construct a `JobParameters` by hand instead of through the builder, or serialize one. ## "Identifying" is the whole restart mechanism A **JobInstance** is identified by a job name plus the set of parameters marked `identifying`. Calling `jobOperator.start(job, params)` with parameters that match an existing JobInstance does not create a second, independent run: - If that instance's last execution is not `COMPLETED` (it `FAILED`, or never finished), Spring Batch creates a new `JobExecution` **against the same instance** and each step resumes from its own last-committed position. This is a restart, and it happens through the exact same `start()` call as a first attempt — see [chapter 7](07-restartability.md) for what "resumes from" means concretely. - If that instance's last execution `COMPLETED`, `start()` throws `JobInstanceAlreadyCompleteException` (unless the step allows re-running — `allowStartIfComplete`, used on `reportStep`; see [chapter 8](08-skip-vs-restart.md)). There is a separate `JobOperator.restart(long executionId)` method for restarting by execution ID explicitly, useful for an operational tool that lists failed executions and lets someone pick one. This module does not need it: every run uses the same identifying parameter (`batch.run=demo`), so plain `start()` already does the right thing whether this is attempt one or attempt two. If you want a fresh JobInstance on every run instead — the common pattern for "run this daily" jobs — add a parameter that changes each time, typically a timestamp or an incrementer, and mark it identifying (the default). ## `spring.batch.job.enabled` Spring Boot auto-configures a `JobLauncherApplicationRunner` that launches every `Job` bean it finds using empty parameters, on every application startup. This module turns that off (`spring.batch.job.enabled: false` in [`application.yml`](../src/main/resources/application.yml)) because `ImportRunner` needs to control the parameters (the identifying `batch.run` value) and which profile's `Job` bean is active. Leaving both runners on would launch the job twice per startup with two different parameter sets. If you only ever need "run the one job on startup with no arguments," the auto-configured runner is simpler and this whole class is unnecessary. ## Going deeper - `JobParameters` and identifying parameters: [Spring Batch reference — running a job](https://docs.spring.io/spring-batch/reference/job/running.html) (`rel="nofollow"`). - `spring.batch.job.name` for selecting which job the auto-configured runner launches, when you have more than one `Job` bean and want to keep using it. [Next: The JDBC writer and Java records →](06-jdbc-writer-and-records.md)