Restart Ability
Restart Ability
api steps bellow: Restartability on spring batch job - create spring batch job exposed via a
REST API
ChatGPT
Creating a Spring Boot application with Java 21, Spring Tool Suite (STS), MySQL, and a
REST API that supports restartability for a Spring Batch job involves several steps. Below is
a step-by-step guide:
Prerequisites:
Steps:
Click Finish.
properties
spring.datasource.url=jdbc:mysql://localhost:3306/spring_batch_db
spring.datasource.username=root
spring.datasource.password=yourpassword
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
spring.batch.job.enabled=false
sql
CREATE DATABASE spring_batch_db;
Spring Batch will automatically create the necessary tables for batch processing.
java
package com.example.springbatchrestart.config;
import com.example.springbatchrestart.model.Person;
import com.example.springbatchrestart.processor.PersonItemProcessor;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.Step;
import
org.springframework.batch.core.configuration.annotation.EnableBatchProcessi
ng;
import
org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import
org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.core.step.builder.StepBuilderHelper;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.database.JpaPagingItemReader;
import
org.springframework.batch.item.database.builder.JpaPagingItemReaderBuilder;
import org.springframework.batch.item.file.FlatFileItemReader;
import
org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import
org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import
org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import javax.persistence.EntityManagerFactory;
@Configuration
@EnableBatchProcessing
public class BatchConfig {
@Autowired
public JobBuilderFactory jobBuilderFactory;
@Autowired
public StepBuilderFactory stepBuilderFactory;
@Autowired
private EntityManagerFactory entityManagerFactory;
@Bean
public FlatFileItemReader<Person> reader() {
return new FlatFileItemReaderBuilder<Person>()
.name("personItemReader")
.resource(new ClassPathResource("sample-data.csv"))
.delimited()
.names(new String[]{"firstName", "lastName"})
.fieldSetMapper(new BeanWrapperFieldSetMapper<Person>() {{
setTargetType(Person.class);
}})
.build();
}
@Bean
public PersonItemProcessor processor() {
return new PersonItemProcessor();
}
@Bean
public JpaPagingItemReader<Person> writer() {
return new JpaPagingItemReaderBuilder<Person>()
.name("personItemWriter")
.entityManagerFactory(entityManagerFactory)
.queryString("SELECT p FROM Person p")
.build();
}
@Bean
public Job importUserJob(JobCompletionNotificationListener listener,
Step step1) {
return jobBuilderFactory.get("importUserJob")
.listener(listener)
.start(step1)
.build();
}
@Bean
public Step step1(ItemReader<Person> reader, ItemProcessor<Person,
Person> processor, ItemWriter<Person> writer) {
return stepBuilderFactory.get("step1")
.<Person, Person> chunk(10)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}
}
java
package com.example.springbatchrestart.listener;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.listener.JobExecutionListenerSupport;
import org.springframework.stereotype.Component;
@Component
public class JobCompletionNotificationListener extends
JobExecutionListenerSupport {
@Override
public void afterJob(JobExecution jobExecution) {
if (jobExecution.getStatus() == BatchStatus.COMPLETED) {
System.out.println("Job completed successfully");
}
}
}
java
package com.example.springbatchrestart.processor;
import com.example.springbatchrestart.model.Person;
import org.springframework.batch.item.ItemProcessor;
@Override
public Person process(final Person person) throws Exception {
final String firstName = person.getFirstName().toUpperCase();
final String lastName = person.getLastName().toUpperCase();
return transformedPerson;
}
}
java
package com.example.springbatchrestart.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
public Person() {}
Create a controller BatchJobController.java to expose the batch job via REST API:
java
package com.example.springbatchrestart.controller;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/batch")
public class BatchJobController {
@Autowired
JobLauncher jobLauncher;
@Autowired
Job importUserJob;
@GetMapping("/start")
public ResponseEntity<String> startBatchJob() {
try {
jobLauncher.run(importUserJob, new JobParametersBuilder()
.addLong("startAt", System.currentTimeMillis())
.toJobParameters());
return ResponseEntity.ok("Batch job has been invoked");
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.status(500).body("Batch job failed");
}
}
}
2. firstName,lastName
3. John,Doe
4. Jane,Doe
5.
6. Test the REST API:
o Use Postman or any REST client to send a GET request to
http://localhost:8080/batch/start.
o Check the console for job execution details.
1. Check the MySQL database to see if the data has been processed and stored
correctly.
2. Check the logs to confirm that the batch job has completed successfully.
This setup includes the restartability feature of Spring Batch inherently because it leverages
the job repository to store job execution details. If a job fails, you can restart it, and it will
pick up from where it left off based on the saved state in the database.
java
package com.example.springbatchrestart.config;
import com.example.springbatchrestart.model.Person;
import com.example.springbatchrestart.processor.PersonItemProcessor;
import
com.example.springbatchrestart.listener.JobCompletionNotificationListener;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecutionListener;
import
org.springframework.batch.core.configuration.annotation.EnableBatchProcessi
ng;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.core.step.builder.StepBuilderHelper;
import org.springframework.batch.core.Step;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.database.JpaPagingItemReader;
import
org.springframework.batch.item.database.builder.JpaPagingItemReaderBuilder;
import org.springframework.batch.item.file.FlatFileItemReader;
import
org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import
org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import
org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.transaction.PlatformTransactionManager;
import javax.persistence.EntityManagerFactory;
@Configuration
@EnableBatchProcessing
public class BatchConfig {
@Autowired
private EntityManagerFactory entityManagerFactory;
@Bean
public FlatFileItemReader<Person> reader() {
return new FlatFileItemReaderBuilder<Person>()
.name("personItemReader")
.resource(new ClassPathResource("sample-data.csv"))
.delimited()
.names("firstName", "lastName")
.fieldSetMapper(new BeanWrapperFieldSetMapper<>() {{
setTargetType(Person.class);
}})
.build();
}
@Bean
public PersonItemProcessor processor() {
return new PersonItemProcessor();
}
@Bean
public JpaPagingItemReader<Person> writer() {
return new JpaPagingItemReaderBuilder<Person>()
.name("personItemWriter")
.entityManagerFactory(entityManagerFactory)
.queryString("SELECT p FROM Person p")
.build();
}
@Bean
public Job importUserJob(JobRepository jobRepository,
JobCompletionNotificationListener listener, Step step1) {
return new JobBuilder("importUserJob", jobRepository)
.listener(listener)
.start(step1)
.build();
}
@Bean
public Step step1(JobRepository jobRepository,
PlatformTransactionManager transactionManager, ItemReader<Person> reader,
ItemProcessor<Person, Person> processor, ItemWriter<Person> writer) {
return new StepBuilder("step1", jobRepository)
.<Person, Person>chunk(10, transactionManager)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}
}
Additional Changes
1. Ensure your MySQL database is running and the schema is set up correctly.
2. Run your Spring Boot application from STS.
3. Test the REST API using a client like Postman:
o GET request to http://localhost:8080/batch/start.
Explanation:
JobBuilder and StepBuilder are now used directly with the JobRepository.
JobBuilderFactory and StepBuilderFactory are no longer needed as JobBuilder
and StepBuilder provide the necessary methods to build jobs and steps.
FlatFileItemReader and JpaPagingItemReader are used for reading data from CSV
and JPA, respectively.
This ensures that your application is up-to-date with the latest Spring Batch configurations
and avoids deprecated APIs.
java
package com.example.springbatchrestart.listener;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.BatchStatus;
import org.springframework.stereotype.Component;
@Component
public class JobCompletionNotificationListener implements
JobExecutionListener {
@Override
public void beforeJob(JobExecution jobExecution) {
// This method can be used to perform actions before the job starts
}
@Override
public void afterJob(JobExecution jobExecution) {
if (jobExecution.getStatus() == BatchStatus.COMPLETED) {
System.out.println("Job completed successfully");
} else if (jobExecution.getStatus() == BatchStatus.FAILED) {
System.out.println("Job failed with status: " +
jobExecution.getStatus());
}
}
}
Full Configuration
1. BatchConfig.java:
java
package com.example.springbatchrestart.config;
import com.example.springbatchrestart.model.Person;
import com.example.springbatchrestart.processor.PersonItemProcessor;
import
com.example.springbatchrestart.listener.JobCompletionNotificationListener;
import org.springframework.batch.core.Job;
import
org.springframework.batch.core.configuration.annotation.EnableBatchProcessi
ng;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.core.Step;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.database.JpaPagingItemReader;
import
org.springframework.batch.item.database.builder.JpaPagingItemReaderBuilder;
import org.springframework.batch.item.file.FlatFileItemReader;
import
org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import
org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import
org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.transaction.PlatformTransactionManager;
import javax.persistence.EntityManagerFactory;
@Configuration
@EnableBatchProcessing
public class BatchConfig {
@Autowired
private EntityManagerFactory entityManagerFactory;
@Bean
public FlatFileItemReader<Person> reader() {
return new FlatFileItemReaderBuilder<Person>()
.name("personItemReader")
.resource(new ClassPathResource("sample-data.csv"))
.delimited()
.names("firstName", "lastName")
.fieldSetMapper(new BeanWrapperFieldSetMapper<>() {{
setTargetType(Person.class);
}})
.build();
}
@Bean
public PersonItemProcessor processor() {
return new PersonItemProcessor();
}
@Bean
public JpaPagingItemReader<Person> writer() {
return new JpaPagingItemReaderBuilder<Person>()
.name("personItemWriter")
.entityManagerFactory(entityManagerFactory)
.queryString("SELECT p FROM Person p")
.build();
}
@Bean
public Job importUserJob(JobRepository jobRepository,
JobCompletionNotificationListener listener, Step step1) {
return new JobBuilder("importUserJob", jobRepository)
.listener(listener)
.start(step1)
.build();
}
@Bean
public Step step1(JobRepository jobRepository,
PlatformTransactionManager transactionManager, ItemReader<Person> reader,
ItemProcessor<Person, Person> processor, ItemWriter<Person> writer) {
return new StepBuilder("step1", jobRepository)
.<Person, Person>chunk(10, transactionManager)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}
}
2. JobCompletionNotificationListener.java:
java
package com.example.springbatchrestart.listener;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.BatchStatus;
import org.springframework.stereotype.Component;
@Component
public class JobCompletionNotificationListener implements
JobExecutionListener {
@Override
public void beforeJob(JobExecution jobExecution) {
// This method can be used to perform actions before the job starts
}
@Override
public void afterJob(JobExecution jobExecution) {
if (jobExecution.getStatus() == BatchStatus.COMPLETED) {
System.out.println("Job completed successfully");
} else if (jobExecution.getStatus() == BatchStatus.FAILED) {
System.out.println("Job failed with status: " +
jobExecution.getStatus());
}
}
}
3. PersonItemProcessor.java:
java
package com.example.springbatchrestart.processor;
import com.example.springbatchrestart.model.Person;
import org.springframework.batch.item.ItemProcessor;
@Override
public Person process(final Person person) throws Exception {
final String firstName = person.getFirstName().toUpperCase();
final String lastName = person.getLastName().toUpperCase();
return transformedPerson;
}
}
4. Person.java:
java
package com.example.springbatchrestart.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
public Person() {}
5. BatchJobController.java:
java
package com.example.springbatchrestart.controller;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/batch")
public class BatchJobController {
@Autowired
JobLauncher jobLauncher;
@Autowired
Job importUserJob;
@GetMapping("/start")
public ResponseEntity<String> startBatchJob() {
try {
jobLauncher.run(importUserJob, new JobParametersBuilder()
.addLong("startAt", System.currentTimeMillis())
.toJobParameters());
return ResponseEntity.ok("Batch job has been invoked");
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.status(500).body("Batch job failed");
}
}
}
1. Ensure your MySQL database is running and the schema is created correctly.
2. Run your Spring Boot application from STS.
3. Test the REST API using a client like Postman:
o Send a GET request to http://localhost:8080/batch/start.
This setup avoids deprecated APIs and aligns with the latest practices in Spring Batch.
error Error starting ApplicationContext. To display the condition evaluation report re-run
your application with 'debug' enabled. [2m2024-05-26T22:23:23.898+05:30[0;39m
[31mERROR[0;39m [35m6860[0;39m [2m---[0;39m [2m[RestartSpringBatch]
[ restartedMain][0;39m [2m[0;39m[36mo.s.b.d.LoggingFailureAnalysisReporter [0;39m [2m:
[0;39m *************************** APPLICATION FAILED TO START
*************************** Description: Parameter 4 of method step1 in
com.batch.config.BatchConfig required a bean of type
'org.springframework.batch.item.ItemWriter' that could not be found. The injection point has
the following annotations: -
@org.springframework.beans.factory.annotation.Autowired(required=true) Action: Consider
defining a bean of type 'org.springframework.batch.item.ItemWriter' in your configuration.
ChatGPT
The error you're encountering is because Spring could not find a bean of type ItemWriter. In
the provided BatchConfig.java, we mistakenly defined the JpaPagingItemReader as the
ItemWriter. Let's correct that by defining a proper ItemWriter.
Let's define a simple ItemWriter that uses JPA to write the data to the database.
Corrected BatchConfig.java
java
package com.example.springbatchrestart.config;
import com.example.springbatchrestart.model.Person;
import com.example.springbatchrestart.processor.PersonItemProcessor;
import
com.example.springbatchrestart.listener.JobCompletionNotificationListener;
import org.springframework.batch.core.Job;
import
org.springframework.batch.core.configuration.annotation.EnableBatchProcessi
ng;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.core.Step;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.database.JpaItemWriter;
import org.springframework.batch.item.database.JpaPagingItemReader;
import
org.springframework.batch.item.database.builder.JpaPagingItemReaderBuilder;
import org.springframework.batch.item.file.FlatFileItemReader;
import
org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import
org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import
org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.transaction.PlatformTransactionManager;
import javax.persistence.EntityManagerFactory;
@Configuration
@EnableBatchProcessing
public class BatchConfig {
@Autowired
private EntityManagerFactory entityManagerFactory;
@Bean
public FlatFileItemReader<Person> reader() {
return new FlatFileItemReaderBuilder<Person>()
.name("personItemReader")
.resource(new ClassPathResource("sample-data.csv"))
.delimited()
.names("firstName", "lastName")
.fieldSetMapper(new BeanWrapperFieldSetMapper<>() {{
setTargetType(Person.class);
}})
.build();
}
@Bean
public PersonItemProcessor processor() {
return new PersonItemProcessor();
}
@Bean
public JpaItemWriter<Person> writer() {
JpaItemWriter<Person> writer = new JpaItemWriter<>();
writer.setEntityManagerFactory(entityManagerFactory);
return writer;
}
@Bean
public Job importUserJob(JobRepository jobRepository,
JobCompletionNotificationListener listener, Step step1) {
return new JobBuilder("importUserJob", jobRepository)
.listener(listener)
.start(step1)
.build();
}
@Bean
public Step step1(JobRepository jobRepository,
PlatformTransactionManager transactionManager, ItemReader<Person> reader,
ItemProcessor<Person, Person> processor, ItemWriter<Person> writer) {
return new StepBuilder("step1", jobRepository)
.<Person, Person>chunk(10, transactionManager)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}
}
Explanation:
JobCompletionNotificationListener.java:
java
package com.example.springbatchrestart.listener;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.BatchStatus;
import org.springframework.stereotype.Component;
@Component
public class JobCompletionNotificationListener implements
JobExecutionListener {
@Override
public void beforeJob(JobExecution jobExecution) {
// This method can be used to perform actions before the job starts
}
@Override
public void afterJob(JobExecution jobExecution) {
if (jobExecution.getStatus() == BatchStatus.COMPLETED) {
System.out.println("Job completed successfully");
} else if (jobExecution.getStatus() == BatchStatus.FAILED) {
System.out.println("Job failed with status: " +
jobExecution.getStatus());
}
}
}
PersonItemProcessor.java:
java
package com.example.springbatchrestart.processor;
import com.example.springbatchrestart.model.Person;
import org.springframework.batch.item.ItemProcessor;
@Override
public Person process(final Person person) throws Exception {
final String firstName = person.getFirstName().toUpperCase();
final String lastName = person.getLastName().toUpperCase();
Person.java:
java
package com.example.springbatchrestart.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
public Person() {}
BatchJobController.java:
java
package com.example.springbatchrestart.controller;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/batch")
public class BatchJobController {
@Autowired
JobLauncher jobLauncher;
@Autowired
Job importUserJob;
@GetMapping("/start")
public ResponseEntity<String> startBatchJob() {
try {
jobLauncher.run(importUserJob, new JobParametersBuilder()
.addLong("startAt", System.currentTimeMillis())
.toJobParameters());
return ResponseEntity.ok("Batch job has been invoked");
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.status(500).body("Batch job
failed");
}
}
}
1. Ensure your MySQL database is running and the schema is created correctly.
2. Run your Spring Boot application from STS.
3. Test the REST API using a client like Postman:
o Send a GET request to http://localhost:8080/batch/start.
To diagnose the issue causing the "Batch job failed" message, let's add more detailed logging
and handle any potential exceptions. Here are the steps to debug and improve the error
handling in the BatchJobController.
Updated BatchJobController.java
java
package com.example.springbatchrestart.controller;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@RestController
@RequestMapping("/batch")
public class BatchJobController {
private static final Logger log =
LoggerFactory.getLogger(BatchJobController.class);
@Autowired
JobLauncher jobLauncher;
@Autowired
Job importUserJob;
@GetMapping("/start")
public ResponseEntity<String> startBatchJob() {
try {
jobLauncher.run(importUserJob, new JobParametersBuilder()
.addLong("startAt", System.currentTimeMillis())
.toJobParameters());
return ResponseEntity.ok("Batch job has been invoked");
} catch (Exception e) {
log.error("Batch job failed", e);
return ResponseEntity.status(500).body("Batch job failed: " +
e.getMessage());
}
}
}
Enhanced JobCompletionNotificationListener.java
To get more information about the job execution status, let's also update the listener to
provide more insights.
java
package com.example.springbatchrestart.listener;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.BatchStatus;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component
public class JobCompletionNotificationListener implements
JobExecutionListener {
@Override
public void beforeJob(JobExecution jobExecution) {
log.info("Job started: {}",
jobExecution.getJobInstance().getJobName());
}
@Override
public void afterJob(JobExecution jobExecution) {
if (jobExecution.getStatus() == BatchStatus.COMPLETED) {
log.info("Job completed successfully");
} else if (jobExecution.getStatus() == BatchStatus.FAILED) {
log.error("Job failed with status: {}",
jobExecution.getStatus());
jobExecution.getAllFailureExceptions().forEach(ex ->
log.error("Job failure exception: ", ex));
}
}
}
Let's ensure that the batch configuration is correct and no errors are hidden. Here is the
BatchConfig again for reference:
java
package com.example.springbatchrestart.config;
import com.example.springbatchrestart.model.Person;
import com.example.springbatchrestart.processor.PersonItemProcessor;
import
com.example.springbatchrestart.listener.JobCompletionNotificationListener;
import org.springframework.batch.core.Job;
import
org.springframework.batch.core.configuration.annotation.EnableBatchProcessi
ng;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.core.Step;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.database.JpaItemWriter;
import
org.springframework.batch.item.database.builder.JpaPagingItemReaderBuilder;
import org.springframework.batch.item.file.FlatFileItemReader;
import
org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import
org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import
org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.transaction.PlatformTransactionManager;
import javax.persistence.EntityManagerFactory;
@Configuration
@EnableBatchProcessing
public class BatchConfig {
@Autowired
private EntityManagerFactory entityManagerFactory;
@Bean
public FlatFileItemReader<Person> reader() {
return new FlatFileItemReaderBuilder<Person>()
.name("personItemReader")
.resource(new ClassPathResource("sample-data.csv"))
.delimited()
.names("firstName", "lastName")
.fieldSetMapper(new BeanWrapperFieldSetMapper<>() {{
setTargetType(Person.class);
}})
.build();
}
@Bean
public PersonItemProcessor processor() {
return new PersonItemProcessor();
}
@Bean
public JpaItemWriter<Person> writer() {
JpaItemWriter<Person> writer = new JpaItemWriter<>();
writer.setEntityManagerFactory(entityManagerFactory);
return writer;
}
@Bean
public Job importUserJob(JobRepository jobRepository,
JobCompletionNotificationListener listener, Step step1) {
return new JobBuilder("importUserJob", jobRepository)
.listener(listener)
.start(step1)
.build();
}
@Bean
public Step step1(JobRepository jobRepository,
PlatformTransactionManager transactionManager, ItemReader<Person> reader,
ItemProcessor<Person, Person> processor, ItemWriter<Person> writer) {
return new StepBuilder("step1", jobRepository)
.<Person, Person>chunk(10, transactionManager)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}
}
1. Ensure the database is running: Make sure your MySQL server is up and the
schema is correctly set up.
2. Run the application: Start your Spring Boot application from STS.
3. Check the logs: After invoking the batch job via the REST API, check the logs for
detailed error messages.
sh
curl -X GET http://localhost:8080/batch/start
Check the response and the application logs for detailed information about any issues. If there
are specific errors related to the reader, processor, or writer, the logs should provide details.
If further issues arise, please share the specific log output for more precise troubleshooting.