I have Spring Batch job that writes to the database (it has a step with a JpaItemWriter
). I have an integration test such as follows:
@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("integrationTest")
public class LoadApplicationTests {
@Autowired
private Job job;
@Autowired
private JobRepository jobRepository;
@Autowired
private JobLauncher jobLauncher;
private JobLauncherTestUtils jobLauncherTestUtils;
@Before
public void setUp() throws IOException, java.text.ParseException, Exception {
jobLauncherTestUtils = new JobLauncherTestUtils();
jobLauncherTestUtils.setJob(job);
jobRepository = new MapJobRepositoryFactoryBean(new ResourcelessTransactionManager()).getObject();
jobLauncherTestUtils.setJobRepository(jobRepository);
jobLauncherTestUtils.setJobLauncher(jobLauncher);
}
@Test
public void testJob() throws Exception {
JobParametersBuilder j = new JobParametersBuilder();
JobParameters jobParameters = j.addDate("runDate", new Date())
.addString("file", testFile.getAbsolutePath())
.addString("override", "false")
.addString("weekly", "false")
.toJobParameters();
JobExecution jobExecution = jobLauncherTestUtils.launchJob(jobParameters);
Assert.assertEquals("COMPLETED", jobExecution.getExitStatus().getExitCode());
}
}
When running the job in the test, it commits to the database. How can I prevent committing to the database? Normally, I could add @Transactional
to rollback the transaction after each test. However, when I add the annotation the test class, I receive:
java.lang.IllegalStateException: Existing transaction detected in JobRepository. Please fix this and try again (e.g. remove @Transactional annotations from client).
Update
I have tried to add @Rollback
to the test class. However, the JpaItemWriter
still commits.
Here's the configuration for the transaction manager in the application code:
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
@Bean
public Step stepLoadFile(StepBuilderFactory stepBuilderFactory,
PlatformTransactionManager transactionManager,
ItemReader<MyClass> reader, ItemProcessor<MyClass,
MyClass> processor,
ItemWriter<MyClass> writer,
ReadFailureHandler readListenerSupport,
WriteFailureHandler writeListenerSupport) {
Step step = stepBuilderFactory.get("stepPersistFile")
.transactionManager(transactionManager)
.<MyClass, MyClass> chunk(1000)
.reader(reader)
.processor(processor)
.listener(writeListenerSupport)
.listener(readListenerSupport)
.writer(writer)
.build();
return step;
}
One approach is tasklet-based, where a Tasklet supports a simple interface with a single execute() method. The other approach, **chunk-oriented processing**, refers to reading the data sequentially and creating "chunks" that will be written out within a transaction boundary.
The second and preferred way to stop execution is to set a stop flag in the step execution object. To set this stop flag, call the method StepExecution. setTerminateOnly() , which is equivalent to sending a stop message. As soon as Spring Batch gets control of the processing, it stops the job execution.
By default , if there's an uncaught exception when processing the job, spring batch will stop the job. If the job is restarted with the same job parameters, it will pick up where it left off. The way it knows where the job status is by checking the job repository where it saves all the spring batch job status.
The JobExecutionDecider is an interface provided by Spring Batch that, when implemented, gives you the ability to examine a condition during job execution and return a FlowExecutionStatus value to determine the next step in the job.
To overcome this my group has simply written an @After
hook to clear the data that was written. It is not pretty, nor desired, but it appears to be getting us through our issues.
Keep in mind, that this will still write your job executions to batch_job_execution
, batch_job_execution_context
, etc.
Also recognize, that you'll probably want to ensure your spring.batch.job.enabled
should be set to false
in your test/resources/application.[properties|yml]
.
Fun times 🤦♂️
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With