skip to content

Launching, Parameters & Metadata

Starting jobs and remembering what happened: the launcher, job parameters and identity, the metadata repository, restartability, idempotency, the operator API, and step-scoped late binding. This is the operational half of Spring Batch.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

explore

questions

page 1 of 2

What happens when you launch a Spring Batch job a second time with the exact same identifying job parameters, and why?

level: juniorimportance: must knowfreq 70%

answer

  1. job name + identifying params = JobInstance
  2. COMPLETED instance -> JobInstanceAlreadyCompleteException
  3. FAILED/STOPPED -> restart, same instance
  4. vary a param (incrementer) for fresh runs
  5. metadata in JobRepository tables

basics

~10 s

Spring Batch treats the same identifying parameters as the same JobInstance. If that instance already completed successfully, re-launching throws JobInstanceAlreadyCompleteException, so the job does not run again and duplicate work is avoided.

solid answer

~40 s

A JobInstance is identified by the job name plus its identifying job parameters. Spring Batch records every instance and its executions in the JobRepository (metadata tables). Re-launching with identical identifying parameters resolves to the same JobInstance. If that instance previously finished with COMPLETED status, the JobLauncher throws JobInstanceAlreadyCompleteException and refuses to run it — a built-in guard against accidental duplicate runs. If the previous execution FAILED or STOPPED, the same instance can be restarted, creating a new JobExecution that resumes from where it left off. To run genuinely fresh work each time (e.g. a daily job), you must vary an identifying parameter, typically via a JobParametersIncrementer.

code

java · 16 lines
java
// Launch #1: creates JobInstance for date=2026-07-22, runs to COMPLETED
JobParameters params = new JobParametersBuilder()
        .addString("date", "2026-07-22") // identifying by default
        .toJobParameters();
jobLauncher.run(billingJob, params); // OK

// Launch #2 with the SAME identifying params:
jobLauncher.run(billingJob, params);
// -> throws JobInstanceAlreadyCompleteException:
//    "A job instance already exists and is complete for parameters={date=2026-07-22}"

// To run again for genuinely new work, change an identifying parameter:
JobParameters tuesday = new JobParametersBuilder()
        .addString("date", "2026-07-23")
        .toJobParameters();
jobLauncher.run(billingJob, tuesday); // new JobInstance, runs fine

go deeper

for a junior

Must know that identical identifying params = same instance and that a completed instance can't be re-run.

for a middle

Should distinguish JobInstance vs JobExecution and know restart vs new-instance behavior.

for a senior

Explains the identifying flag, restart semantics, and how to deliberately produce fresh instances.

for a principal

Frames the guard as a correctness/idempotency contract and discusses parameter design (business key vs timestamp).

**Core model.** Spring Batch separates a *JobInstance* (a logical run of a job for a given set of inputs) from a *JobExecution* (a single physical attempt to run that instance). A `JobInstance` is uniquely identified by the **job name + the identifying job parameters**. All of this is persisted by the `JobRepository` into metadata tables (`BATCH_JOB_INSTANCE`, `BATCH_JOB_EXECUTION`, `BATCH_JOB_EXECUTION_PARAMS`, etc.). **Identifying vs non-identifying parameters.** Each `JobParameter` has an `identifying` boolean flag (default `true`). Only identifying parameters contribute to a JobInstance's identity. So launching with `date=2026-07-22` (identifying) creates one instance; launching again with the same `date` refers to the *same* instance. **The idempotency guard.** When you launch a job whose identifying parameters map to an existing JobInstance, Spring Batch inspects that instance's last execution: - If it **COMPLETED** successfully → `JobInstanceAlreadyCompleteException` is thrown (wrapped/surfaced by the launcher). The job body never executes. This is the framework preventing a duplicate run. - If it **FAILED** or was **STOPPED** → this is treated as a **restart**: a new JobExecution is created for the *same* JobInstance, and restartable steps resume from their saved state. - If an execution is still **running** for that instance → you get a `JobExecutionAlreadyRunningException`. **Why this exists.** It makes 'launch the monthly billing job for July' safe to invoke twice — the second invocation is rejected rather than double-charging customers. **How to run fresh work anyway.** For a job meant to run repeatedly (daily ETL), you deliberately change an identifying parameter each time. The idiomatic way is a `JobParametersIncrementer` (e.g. `RunIdIncrementer`, which adds/bumps a `run.id` parameter), or supplying a timestamp/business date. Then each launch is a new JobInstance and no exception occurs. **Gotchas.** - Passing a wall-clock `System.currentTimeMillis()` as identifying makes every run unique but pollutes the metadata and defeats the safety net — prefer a meaningful business key. - If you *want* restart behavior, do **not** change identifying parameters; changing them creates a brand-new instance instead of resuming the failed one. - The check is per JobInstance, not global — two different jobs with the same parameters are independent.

  • What is the difference between an identifying and a non-identifying job parameter?
    Only identifying parameters (the default, `identifying=true`) contribute to a JobInstance's identity. Non-identifying parameters (e.g. metadata like a `verbose` flag) are stored and passed to the job but do not create a new instance, so they can change between restarts without breaking resume.
  • If the first execution failed, does re-launching with the same parameters throw the exception?
    No. A FAILED or STOPPED instance is restartable, so re-launching creates a new JobExecution on the same JobInstance and resumes. The exception is only thrown when the instance already COMPLETED successfully.

saying these in an interview costs you the question

  • Thinking every job launch always creates a new run regardless of parameters
  • Believing the exception is thrown for failed jobs too (it's only for COMPLETED instances)
  • Confusing JobInstance with JobExecution

context

open as a page

What is Spring Batch's JobLauncher, and how do you use it to start a job?

level: juniorimportance: must knowfreq 70%

basics

~10 s

JobLauncher is the interface that starts a batch Job. You call jobLauncher.run(job, jobParameters). It returns a JobExecution describing that run (status, id, timestamps).

open as a page

What is Spring Batch's JobOperator, and how does it differ from JobLauncher?

level: juniorimportance: must knowfreq 50%

basics

~10 s

JobOperator is an operations-facing facade for controlling jobs — start, stop, restart, and abandon executions using simple types like job names and execution ids. JobLauncher just runs a Job you already have wired.

open as a page

What are JobParameters in Spring Batch, and how do you build them?

level: juniorimportance: must knowfreq 70%

basics

~10 s

JobParameters are typed key/value inputs passed to a job at launch (Strings, Longs, Doubles, Dates). You build them with JobParametersBuilder and pass them to JobLauncher.run(job, params).

open as a page

What is the JobRepository in Spring Batch, and what does it store?

level: juniorimportance: must knowfreq 70%

basics

~10 s

The JobRepository is the component that saves batch execution state to a database. It records which jobs and steps ran, their status, timestamps, and parameters, so a job can be tracked and restarted.

open as a page

What does it mean to restart a Spring Batch job, and which jobs can be restarted?

level: juniorimportance: must knowfreq 60%

basics

~20 s

Restarting means re-running a job that previously FAILED or STOPPED. Spring Batch reuses the same JobInstance (same identifying parameters) and continues where it left off instead of starting from scratch. A COMPLETED job cannot normally be restarted.

open as a page

What is @StepScope in Spring Batch and why would you annotate a reader or writer bean with it?

level: juniorimportance: must knowfreq 68%

basics

~20 s

@StepScope is a custom Spring scope where a new bean instance is created for each step run. You use it so a reader or writer can pick up values (like job parameters) that only exist when the job actually runs, not at startup.

open as a page

How do you make a Spring Batch job re-runnable on demand (e.g. every night) without hitting JobInstanceAlreadyCompleteException?

level: middleimportance: must knowfreq 60%

basics

~10 s

Add a JobParametersIncrementer, such as RunIdIncrementer, so each launch gets a new identifying parameter (like run.id). That makes every launch a new JobInstance, so the completed-instance guard never triggers.

open as a page

By default is jobLauncher.run() blocking or non-blocking, and how do you make it asynchronous?

level: middleimportance: must knowfreq 68%

basics

~20 s

By default run() is blocking: TaskExecutorJobLauncher uses a SyncTaskExecutor, so it runs the job in the caller thread and returns only when it finishes. Give the launcher an async TaskExecutor to make run() return immediately.

open as a page

What is the difference between identifying and non-identifying JobParameters, and how does it affect JobInstance identity?

level: middleimportance: must knowfreq 65%

basics

~10 s

Identifying parameters (the default) contribute to a JobInstance's identity — the same set means the same instance. Non-identifying parameters are ignored when computing identity, so you can change them without creating a new instance.

open as a page

How does the Spring Batch metadata schema get created, and what is the role of the schema-*.sql files and @EnableBatchProcessing?

level: middleimportance: must knowfreq 60%

basics

~10 s

Spring Batch ships DDL scripts named schema-<database>.sql inside its jar. Spring Boot runs the right one automatically based on spring.batch.jdbc.initialize-schema. @EnableBatchProcessing wires up the batch infrastructure (JobRepository, etc.).

open as a page

How does a chunk-oriented step resume at the last good chunk on restart? What role does ExecutionContext play?

level: middleimportance: must knowfreq 55%

basics

~20 s

Each committed chunk saves progress (like the current read position) into the step's persisted ExecutionContext. On restart, the reader is re-opened and reads that saved state, so processing continues from just after the last successfully committed chunk instead of the beginning.

open as a page

How does late binding of job parameters into a Spring Batch bean work, and what SpEL expressions can you use?

level: middleimportance: must knowfreq 60%

basics

~10 s

You inject runtime values with SpEL like @Value("#{jobParameters['x']}") on a bean annotated @StepScope or @JobScope. Spring resolves the expression when the step/job runs instead of at startup. Available roots include jobParameters, stepExecutionContext, and jobExecutionContext.

open as a page

Explain the difference between JobOperator.stop and JobOperator.abandon, including the semantics of a stop request.

level: seniorimportance: must knowfreq 48%

basics

~20 s

stop asks a running execution to halt gracefully — it sets the status to STOPPING and the job stops at the next safe point, ending STOPPED. abandon marks a non-running execution as ABANDONED so it's skipped on restart. stop is cooperative, not a kill.

open as a page

How does Spring Boot automatically run batch jobs on startup, and how do you control which jobs run?

level: middleimportance: should knowfreq 55%

basics

~10 s

Boot registers a JobLauncherApplicationRunner that, after the context starts, calls JobLauncher.run() on the Job beans it finds. It's on by default; set spring.batch.job.enabled=false to disable, or spring.batch.job.name to pick a specific job.

open as a page

What does JobOperator.startNextInstance do, and what must the Job provide for it to work?

level: middleimportance: should knowfreq 45%

basics

~20 s

startNextInstance runs a new instance of a job automatically. It uses the job's JobParametersIncrementer to derive the next parameters from the previous run — for example bumping a run.id counter — so each call is a distinct JobInstance.

open as a page

What is a JobParametersIncrementer (e.g. RunIdIncrementer) and when do you need one?

level: middleimportance: should knowfreq 55%

basics

~20 s

A JobParametersIncrementer generates the next set of JobParameters from the previous run, typically by bumping an identifying run.id. RunIdIncrementer is the built-in one. It lets you re-run the same job repeatedly without a JobInstanceAlreadyComplete error.

open as a page

What does the job-level restartable flag do, and what happens if you try to restart a non-restartable job?

level: middleimportance: should knowfreq 35%

basics

~10 s

Setting a job's restartable property to false marks it as run-once-per-instance. If its execution fails and you relaunch with the same identifying parameters, Spring Batch refuses and throws JobRestartException instead of resuming.

open as a page

Even with restart support, why must a Spring Batch ItemWriter be idempotent, and how do you make one idempotent?

level: seniorimportance: should knowfreq 50%

basics

~20 s

On restart after a failure, Spring Batch re-reads and re-writes the chunk that was in flight when the job crashed, so a writer can see the same items twice. Making writes upserts (insert-or-update on a key) keeps re-runs safe.

open as a page

What checked exceptions can JobLauncher.run() throw, and what do they tell you about job identity and restart?

level: seniorimportance: should knowfreq 45%

basics

~10 s

run() throws four checked exceptions: JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException, and JobParametersInvalidException. They signal that the run couldn't even start because of the job's identity, state, or bad parameters.

open as a page

How do getRunningExecutions and restart work in JobOperator, and how would you use them to safely manage a repeating job?

level: seniorimportance: should knowfreq 38%

basics

~20 s

getRunningExecutions(jobName) returns the ids of executions currently in flight for that job, so you can avoid launching overlapping runs. restart(executionId) re-runs a failed or stopped execution of the same instance, picking up per its restart semantics.

open as a page

Explain how JobParameters drive the difference between restarting a failed job and starting a new instance.

level: seniorimportance: should knowfreq 45%

basics

~10 s

Same identifying parameters as a failed run means restart: Spring Batch finds the incomplete JobInstance and resumes it. Different identifying parameters (or an incremented run.id) means a brand-new JobInstance that starts from scratch.

open as a page

Walk through the relationships between BATCH_JOB_INSTANCE, JOB_EXECUTION, STEP_EXECUTION, and EXECUTION_CONTEXT, and how they enable restart.

level: seniorimportance: should knowfreq 45%

basics

~20 s

One JobInstance (name + identifying params) has many JobExecutions (one per attempt). Each JobExecution has many StepExecutions. ExecutionContext rows store saved state per job and per step. On restart, Spring reuses the instance and reloads the ExecutionContext to resume.

open as a page

Why does the JobRepository use a SERIALIZABLE isolation level when creating a JobExecution, and when would you change it?

level: seniorimportance: should knowfreq 40%

basics

~20 s

When starting a job, the repository first checks whether an instance/execution already exists, then inserts one. It uses SERIALIZABLE isolation so two launchers can't both create the same JobInstance at once. You lower it only if your DB struggles with SERIALIZABLE.

open as a page

Explain the step-level startLimit and allowStartIfComplete settings and how they interact on restart.

level: seniorimportance: should knowfreq 30%

basics

~20 s

startLimit caps how many times a step may be started across all executions of a JobInstance (default effectively unlimited); exceeding it fails with StartLimitExceededException. allowStartIfComplete=true forces a step that already COMPLETED to run again on every restart instead of being skipped.

open as a page

When would you choose @JobScope over @StepScope, and how do the late-binding roots (jobParameters vs stepExecutionContext vs jobExecutionContext) differ between them?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Use @JobScope when a bean must live for the whole job (across steps); use @StepScope when it's per step or needs the step's ExecutionContext. Both can read jobParameters and jobExecutionContext, but only @StepScope can read stepExecutionContext.

open as a page

How does @StepScope actually work under the hood — why can a singleton Step inject a step-scoped reader, and what role does the scoped proxy play?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Spring injects a scoped proxy, not the real bean. The singleton Step holds this proxy; on each method call the proxy looks up the real step-scoped instance bound to the currently running step and delegates to it. That deferral is what makes late binding possible.

open as a page

Design the launch/idempotency strategy for a nightly financial reconciliation job that must be safe to re-trigger by an operator, resumable after a crash, and must never double-post entries. What are the trade-offs?

level: principalimportance: should knowfreq 35%

basics

~20 s

Key the job by business date (identifying param). Same date + completed = rejected (no double-run); same date + failed = restart and resume. Make writers idempotent via upserts so replayed chunks never double-post. Reserve an incrementer only for deliberately advancing to a new date.

open as a page

You must trigger a long-running batch job from a REST endpoint. Design the launching mechanism and justify the trade-offs.

level: principalimportance: should knowfreq 35%

basics

~20 s

Don't use the default blocking launcher on an HTTP thread. Configure a TaskExecutorJobLauncher with a bounded ThreadPoolTaskExecutor so run() returns immediately with an execution id; the client polls a status endpoint that reads the JobRepository.

open as a page

A developer sees 'Scope "step" is not active for the current thread' and, separately, notices a @StepScope reader captured a stale job parameter across launches. Diagnose both and explain the underlying threading/lifecycle model.

level: principalimportance: should knowfreq 24%

basics

~20 s

The 'scope not active' error means the step-scoped bean was touched when no StepExecution was bound to the thread — e.g. in @PostConstruct, from another thread, or before launching. Stale parameters usually mean the bean isn't actually @StepScope (a singleton captured the value once), or state leaked because the instance was shared.

open as a page

showing 1–30 of 34