Spring Boot + Spring Batch Scheduler(2022) Hello World Example
In this tutorial we will be implementing Spring Boot Batch Scheduler with the help of example.Spring Batch is one of the open-source framework available for batch processing. Sometimes, while working on enterprise applications, we need to perform spring batch jobs from time to time or periodically on fixed time. This can be done by using some cron expression which is passed to Spring TaskScheduler.
In this example, with the help of spring scheduling capability, we will configure a simple Job with a Tasklet that brings the data from the database and prints it in the csv file. This tasklet will be executed after certain amount of time with the help of specifying fixed rate , fixed delay or using cron expression. It is assumed that the reader is known with the basic terms like job, step etc. that are present in spring batch scheduling.
Table Of Contents :
-
This will be the standard directory layout for maven project structure-
We need to start by creating a Maven pom.xml(Project Object Model) file. The pom.xml file contains the project configuration details.<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.codeusingjava.batch</groupId> <artifactId>SpringBootBatchScheduler</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>SpringBootBatchScheduler</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.6.RELEASE</version> <relativePath /> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-batch</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <!-- It helps in serialise and deserialize objects --> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.9</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Creating the Database configuration in application.properties as follows-spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false spring.datasource.password=**** spring.datasource.username=root spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.initialize=false spring.datasource.schema=classpath:schema.sql
Inside schema.sql, the query to create a table in database is present.Paste this content in the mysql workbench.
Now insert some dummy data into the table.CREATE TABLE IF NOT EXISTS person ( person_id BIGINT auto_increment NOT NULL PRIMARY KEY, first_name VARCHAR(40), last_name VARCHAR(40), email VARCHAR(100), age INT );
Creating the User model class as follows-package com.codeusingjava.model; public class User { private Integer personId; private String firstName; private String lastName; private String email; private Integer age; public Integer getPersonId() { return userId; } public void setPersonId(Integer personId) { this.personId = personId; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
Creating the class for Spring Batch configuration
Here, RowMapper interface is used to map one by one rows of the ResultSet. The method writer from the FlatFileItemWriter takes a single object and then write that object in a flat file.
package com.codeusingjava.config; import java.sql.ResultSet; import java.sql.SQLException; import javax.sql.DataSource; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; import org.springframework.batch.core.launch.support.RunIdIncrementer; import org.springframework.batch.item.database.JdbcCursorItemReader; import org.springframework.batch.item.file.FlatFileItemWriter; import org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor; import org.springframework.batch.item.file.transform.DelimitedLineAggregator; 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.jdbc.core.RowMapper; import com.codeusingjava.model.User; import com.codeusingjava.processor.UserItemProcessor; @Configuration @EnableBatchProcessing public class BatchConfig { @Autowired private JobBuilderFactory jobBuilderFactory; @Autowired private StepBuilderFactory stepBuilderFactory; @Autowired private DataSource dataSource; @Bean public JdbcCursorItemReader<User> reader(){ JdbcCursorItemReader<User> cursorItemReader = new JdbcCursorItemReader<>(); cursorItemReader.setDataSource(dataSource); cursorItemReader.setSql("SELECT person_id,first_name,last_name,email,age FROM person"); cursorItemReader.setRowMapper(new UserRowMapper()); return cursorItemReader; } @Bean public UserItemProcessor processor(){ return new UserItemProcessor(); } @Bean public FlatFileItemWriter<User> writer(){ FlatFileItemWriter<User> writer = new FlatFileItemWriter<User>(); writer.setResource(new ClassPathResource("persons.csv")); DelimitedLineAggregator<User> lineAggregator = new DelimitedLineAggregator<User>(); lineAggregator.setDelimiter(","); BeanWrapperFieldExtractor<User> fieldExtractor = new BeanWrapperFieldExtractor<User>(); fieldExtractor.setNames(new String[]{"personId","firstName","lastName","email","age"}); lineAggregator.setFieldExtractor(fieldExtractor); writer.setLineAggregator(lineAggregator); return writer; } @Bean public Step step1(){ return stepBuilderFactory.get("step1").<User,User>chunk(10).reader(reader()).processor(processor()).writer(writer()).build(); } @Bean public Job exportPerosnJob(){ return jobBuilderFactory.get("exportPersonJob").incrementer(new RunIdIncrementer()).flow(step1()).end().build(); } } class UserRowMapper implements RowMapper<User> { @Override public User mapRow(ResultSet rs, int rowNum) throws SQLException { User person = new User(); person.setPersonId(rs.getInt("person_id")); person.setFirstName(rs.getString("first_name")); person.setLastName(rs.getString("last_name")); person.setEmail(rs.getString("email")); person.setAge(rs.getInt("age")); return person; } }
The @EnableScheduling annotation can enable the Scheduler in our application.
@Scheduled annotation is used to task scheduling.
We can schedule a task by using fixed rate,fixed delay attribute or using cron.
The cron expression signifies that the task will be scheduled after every 10 milliseconds.package com.codeusingjava.config; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.JobParametersBuilder; import org.springframework.batch.core.JobParametersInvalidException; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; import org.springframework.batch.core.repository.JobRestartException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component @EnableScheduling public class MyScheduler { @Autowired private JobLauncher jobLauncher; @Autowired private Job job; @Scheduled(cron="*/10 * * * * *") public void myScheduler(){ JobParameters jobParameters = new JobParametersBuilder().addLong("time", System.currentTimeMillis()).toJobParameters(); try { JobExecution jobExecution = jobLauncher.run(job, jobParameters); System.out.println("Job's Status:::"+jobExecution.getStatus()); } catch (JobExecutionAlreadyRunningException | JobRestartException | JobInstanceAlreadyCompleteException | JobParametersInvalidException e) { e.printStackTrace(); } } }
Creating the processor class
The processor class reads each and every data element, then processes the data and finally writes the processed record in the writer.package com.codeusingjava.processor; import org.springframework.batch.item.ItemProcessor; import com.codeusingjava.model.User; public class UserItemProcessor implements ItemProcessor<User, User>{ @Override public User process(User person) throws Exception { return person; } }
package com.codeusingjava; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringBootBatchSchedulerApplication { public static void main(String[] args) { SpringApplication.run(SpringBootBatchSchedulerApplication.class, args); } }
To see the database records into a csv file , go to the following locations inside the project:-