OHDSI Home | Forums | Wiki | Github

Working with JPA Repositories inside a Spring Batch execution step

@alfranke: I’m hoping you can help me with this.

I’m facing a roadblock where I have cohort generation working as a Tasklet (using the example application job service that uses Tasklets), but now I’d like to be able to manipulate the cohrot definition via CohortDefinitionRepository during the task execution. The steps would go like this:

  1. Load Cohort Definition
  2. Set generation status to RUNNING **
  3. Build cohort definition query
  4. Execute query
  5. Set generation Status to COMPLETE **

The ** represents where I’d want to cohortDefRepo.save(definition) after modifying the generation status.

My understanding about spring is: In order to inject the repository using @Autowired, the class has to be annotiated by some type of @Component annotation (or @Component itself). I tried doing this and got an exception that no default constructor was defined in the Tasklet. Which reminded me that we’re not using Spring injections at all when kicking off the job: we’re creating instances ourselves that spring knows nothing about.

So, I feel like a design change is required when dealing with Jobs. OR: I read that people that needed to to do this sort of thing encapsulated the task logic in a separate bean (that would manage it’s own transactions because manipulaiting the repository will work in it’s own transactions cope, and I read there are special challenges related to transaction scopes when working inside Spring Batch). I just don’t know how’d access a bean from inside the tasklet.

I’d appreciate any help or advice you have on this.

-Chris

Since you are instantiating your tasklet (not spring managed), you need to inject the repository into the tasklet as a dependency yourself (e.g. constructor arg). Make sense?

Yes, that’s what I settled on. The next problem I had was that manipulating the object during the job was not commiting ‘intermediate state’ back to the database between saves because the jpa interaction was probably flowing with the active transaction. So, I found out how to initiate a new transaction that the repository interaction will use and the saves showed up after the tx was commited. Like so:

DefaultTransactionDefinition initTx = new DefaultTransactionDefinition();
initTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus initStatus = this.transactionTemplate.getTransactionManager().getTransaction(initTx);
CohortDefinition df = this.cohortDefinitionRepository.findOne(this.task.getCohortDefinition().getId());
CohortGenerationInfo info = df.getGenerationInfo();
if (info == null)
{
  info = new CohortGenerationInfo().setCohortDefinition(df);
  df.setGenerationInfo(info);
}

info.setIsValid(false);
info.setStartTime(startTime);
info.setStatus(GenerationStatus.RUNNING);    
df = this.cohortDefinitionRepository.save(df);
this.transactionTemplate.getTransactionManager().commit(initStatus);

This may be useful for anyone else wanting to use a repository during the execution of a job.

-Chris

I added another TransactionTemplate bean and getter if you need a “new” transaction. I also added some comments in the example service. You can autowire/inject the transactionTemplateRequiresNew bean, or getTransactionTemplateRequiresNew() as necessary.

t