I used Spring batch to build a ETL job. My main job is simply read from a db and write to another one. Before my main job, I need to check a status in the source db to see if it is ready or not. I will proceed with the main job only if the source db is ready. I implemented the check status logic as a Tasklet and I build my job with the simple logic that if check status step failed repeat this step util the step succeed, then proceed with the main job. I build the job as follows:
@Bean
public Job myJob(MyListener listener) {
return jobBuilderFactory.get(jobName)
.incrementer(new RunIdIncrementer())
.listener(listener)
.start(checkStatusStep())
.on("*").to(mainStep())
.on(ExitStatus.FAILED.getExitCode())
.to(checkStatusStep())
.end()
.build();
}
The check status step is a tasklet as follows:
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws InterruptedException {
//isReady method check the db to see if it is ready
if (isReady()) {
//contribution.setExitStatus(ExitStatus.COMPLETED);
return RepeatStatus.FINISHED;
} else {
//contribution.setExitStatus(ExitStatus.FAILED);
return RepeatStatus.CONTINUABLE;
}
}
I read the spring batch document and get that the ON method in the conditional follow will check the exit status of the step so that I set the exit status in the StepContribution. What makes me confused is that when I comment those two lines out and the code still works.
So my question is, first, if the conditional flow checks the exit status why my code works without explicitly change the exit status? Second, why tasklet return a repeat status and by whom this repeat status is consumed. Third, is there a better way to achieve my goal?
if the conditional flow checks the exit status why my code works without explicitly change the exit status?
Because by default if the tasklet returns RepeatStatus.FINISHED
, its exit code will be set to COMPLETED
Second, why tasklet return a repeat status and by whom this repeat status is consumed.
A tasklet is called repeatedly by the TaskletStep
until it either returns RepeatStatus.FINISHED
or throws an exception to signal a failure. Each call to a Tasklet
is wrapped in a transaction. The structure is therefore that of a loop with transaction boundary inside the loop.
Third, is there a better way to achieve my goal?
IMO, your Tasklet
implementation is ok as is, you don't need the exit status. Something like:
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws InterruptedException {
//isReady method check the db to see if it is ready
if (isReady()) {
return RepeatStatus.FINISHED;
} else {
return RepeatStatus.CONTINUABLE;
}
}
This will continuously run until isReady
is true, so you don't need to implement the iteration with a flow at the job level. The job definition would be:
@Bean
public Job myJob(MyListener listener) {
return jobBuilderFactory.get(jobName)
.incrementer(new RunIdIncrementer())
.listener(listener)
.start(checkStatusStep())
.next(mainStep())
.build();
}
Hope this helps.