11

OK,

I know this has been asked before, but I still can't find a definite answer to my question. And my question is this: I am using spring batch to export data to SOLR search server. It needs to run every minute, so I can export all the updates. The first execution passes OK, but the second one complains with:

2014-10-02 20:37:00,022 [defaultTaskScheduler-1] ERROR: catching
org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException: A job     instance already exists and is complete for parameters={catalogVersionPK=3378876823725152,
type=UPDATE}.  If you want to run this job again, change the parameters.
    at org.springframework.batch.core.repository.support.SimpleJobRepository.createJobExecution(SimpleJobRepository.java:126)
    at 

Of course I can add a date-time parameter to the job like this:

.addLong("time", System.getCurrentTimeMillis())

and then the job can be run more than once. However, I also want to query the last execution of the job, so I have code like this:

DateTime endTime = new DateTime(0);
JobExecution je = jobRepository.getLastJobExecution("searchExportJob", new JobParametersBuilder().addLong("catalogVersionPK", catalogVersionPK).addString("type", type).toJobParameters());
if (je != null && je.getEndTime() != null) {
   endTime = new DateTime(je.getEndTime());
}

and this returns nothing, because I didn't provide the time parameter. So seems like I can run the job once and get the last execution time, or i can run it multiple times and not get the last execution time. I am really stuck :(

Sathiamoorthy
  • 8,831
  • 9
  • 65
  • 77
Petar Tahchiev
  • 4,336
  • 4
  • 35
  • 48

6 Answers6

7

Assumption

Spring Batch use some tables to store each JOB executed with its parameters. If you run twice the job with the same parameters, the second one fails, because the job is identified by jobName and parameters.

1# Solution

You could use JobExecution when run a new Job.

JobExecution execution = jobLauncher.run(job, new JobParameters());
.....
// Use a JobExecutionDao to retrieve the JobExecution by ID
JobExecution ex = jobExecutionDao.getJobExecution(execution.getId());

2# Solution

You could implement a custom JobExecutionDao and perform a custom query to find your JobExecution on BATCH_JOB_EXECUTION table. See here the reference of Spring.

I hope my answer is helpful to you.

Xstian
  • 8,184
  • 10
  • 42
  • 72
  • I can't really get hold of the execution, because at that point the execution is lost. If I don't think of some better way I might end up using your solution 2- create a custom DAO. – Petar Tahchiev Oct 06 '14 at 14:05
  • @PetarTahchiev Let me know if works fine. In order to solve this topic :) – Xstian Oct 06 '14 at 19:11
6

Use the Job Explorer as suggested by Luca Basso Ricci.

Because you do not know the job parameters you need to look up by instance.

  1. Look for the last instance of job named searchExportJob
  2. Look for the last execution of the instance above

This way you use Spring Batch API only

//We can set count 1 because job instance are ordered by instance id descending
//so we know the first one returned is the last instance 
List<JobInstance> instances = jobExplorer.getJobInstances("searchExportJob",0,1);
JobInstance lastInstance = instances.get(0);
List<JobExecution> jobExecutions = jobExplorer.getJobExecutions(lastInstance);

//JobExcectuin is ordered by execution id descending so first 
//result is the last execution
JobExecution  je = jobExecutions.get(0);
if (je != null && je.getEndTime() != null) {
     endTime = new DateTime(je.getEndTime());
 }

Note this code only work for Spring Batch 2.2.x and above in 2.1.x the API was somewhat different

Haim Raman
  • 11,508
  • 6
  • 44
  • 70
  • I think `jobExplorer.getLastJobInstance("")` should do the same trick for the first three lines of the code in place of jobExplorer.getJobExecutions(lastInstance); !! – Prasanna Sep 16 '21 at 17:12
2

There is another interface you can use: JobExplorer
From its javadoc:

Entry point for browsing executions of running or historical jobs and steps. Since the data may be re-hydrated from persistent storage, it may not contain volatile fields that would have been present when the execution was active

Luca Basso Ricci
  • 17,829
  • 2
  • 47
  • 69
2

If you are debugging your batch job and terminate your batch job before completing, then it will give this error if you try to start it again.

To start it again either you need to update the name of your job so that it will create another execution id.

or you can update below tables.

BATCH_JOB_EXECUTION
BATCH_STEP_EXECUTION

You need to update Status and End_time columns with non null values.

Pirate
  • 2,886
  • 4
  • 24
  • 42
1

Create new Job RunId everytime.

If your code involves creating Job object using Jobfactory then below snippet would be useful for the problem:

    return jobBuilderFactory
    .get("someJobName")
    .incrementer(new RunIdIncrementer())   // solution lies here- creating new job id everytime
    .flow(                                 // and here
        stepBuilderFactory
            .get("someTaskletStepName")
            .tasklet(tasklet)   // u can replace it with step
            .allowStartIfComplete(true)    // this will make the job run even if complete in last run 
            .build())
    .end()
    .build();
Bablu
  • 161
  • 7
1

This issue could occur when you are doing the debugging.

use the below query to reset all previous jobs.

Execute the below query for Oracle

UPDATE BATCH_JOB_EXECUTION bje SET bje.STATUS = 'FAILED', bje.END_TIME = SYSTIMESTAMP WHERE bje.END_TIME IS NULL 
UPDATE BATCH_STEP_EXECUTION bse SET bse.STATUS = 'FAILED', bse.END_TIME = SYSTIMESTAMP  WHERE bse.END_TIME IS NULL

This query updates the status of any jobs that have not yet ended, setting them to "FAILED" and recording the current time as the end time. By doing this, you can effectively reset any previous jobs and ensure that your debugging process can proceed smoothly.

Sathiamoorthy
  • 8,831
  • 9
  • 65
  • 77