DEV Community

Sadiul Hakim
Sadiul Hakim

Posted on

Spring Batch Tutorial Part #4

Understanding Spring Batch Job and Step Management

Spring Batch provides a robust framework for managing jobs, handling their state, and enabling restartability. This tutorial will explain key concepts like JobInstance, JobExecution, StepExecution, and ExecutionContext, and how they are used to control a job's lifecycle.


JobInstance

A JobInstance is a logical representation of a batch job. It's uniquely identified by the combination of a Job name and the Job Parameters used when it was launched. For example, a "DailyReport" job with a parameter for "reportDate=2025-09-16" will always correspond to the same JobInstance. When you run a job with the same parameters, Spring Batch recognizes that it's the same logical job and reuses the existing JobInstance.

By default, Spring Batch will not re-execute any Step that has already completed successfully within a given JobInstance.


JobExecution and StepExecution

While a JobInstance represents the logical job, a JobExecution represents a single attempt to run that job. Every time you launch a job, a new JobExecution is created. Similarly, a StepExecution represents a single attempt to run a step. JobExecution and StepExecution are never reused. They track the start time, end time, status, and other runtime details of a specific execution attempt.


ExecutionContext

An ExecutionContext is a collection of key-value pairs that are persisted between job runs and executions. It acts as a storage space for data you want to preserve for a future restart.

  • Job-scoped ExecutionContext: Data is persisted to the database after each step completes.
  • Step-scoped ExecutionContext: Data is persisted after each chunk is committed.

ExecutionContext is a powerful tool for maintaining state. For example, you can store a file name, a counter, or a database cursor position in the ExecutionContext so that when a job is restarted, it can resume from where it left off.


Restarting a Job

Restartability is a core feature of Spring Batch. However, there are some rules:

  • Successful Jobs Are Not Restartable: By default, if a JobInstance (identified by its parameters) has a final ExitStatus of COMPLETED, Spring Batch will throw a JobExecutionException if you try to run it again. This prevents re-processing data that has already been successfully handled.
  • Empty Parameters: A job launched without any parameters is considered a new JobInstance each time, so it can be re-run even if the previous run was successful.
  • Preventing Restart: You can use .preventRestart() on the JobBuilder to explicitly disallow a job from being restarted.

When a job is restarted after a failure, Spring Batch automatically skips any steps that completed successfully in the previous attempt and only re-executes the step where the failure occurred.


Restarting a Step

By default, when a job restarts, any previously successful steps are skipped. They do not throw an exception, they simply don't run. This behavior can be changed using .allowStartIfComplete(true) on the StepBuilder, which will force a step to be re-executed even if it completed successfully in a previous run.

  • Execution Limit: To prevent infinite loops or excessive retries, you can set a restart limit for a step using .startLimit(int). If the number of attempts exceeds this limit, a JobExecutionException is thrown.
  • Resuming from Failure: When a step fails, it automatically starts from the exact point of failure on restart. For example, if an ItemReader fails on the 100th line, it will resume reading from the 100th line on the next run. You can override this behavior and force the step to restart from the beginning by calling .saveState(false) on the ItemReader.

Lifecycle Listeners

Spring Batch provides a way to execute custom logic at key points in a job's lifecycle using listeners.

  • JobExecutionListener: Use this to execute code before (@BeforeJob) and after (@AfterJob) a job starts and ends.
  • StepExecutionListener: Use this to execute code before (@BeforeStep) and after (@AfterStep) a step starts and ends.

You can implement these interfaces or use the corresponding annotations (@BeforeJob, @AfterJob, @BeforeStep, @AfterStep) to attach custom logic to your jobs and steps. You then register these listeners using the .listener() method on the JobBuilder or StepBuilder.

Top comments (0)