Introduction
Spring batch is a batch processing framework developed and maintained by the Spring framework team. It is one of the leading and popular batch processing framework in the market. Spring batch uses the chunk oriented processing for performing the batch processing which is more efficient for handling the large volume of data. Spring batch is not a scheduler for launching the job, it is a job for batch processing. This can be invoked / triggered by an external events like system timer, scheduler, etc. to start the job. This tutorial helps you to understand the basic concepts required for writing a simple spring batch program.
- The Spring Batch Infrastructure
- Configure Spring Batch Retry on Error
- Bulletproof Job Scheduling in Spring Batch
Spring Batch Terminologies
The below picture presents the important terminologies used across the spring batch programming.
Spring Batch Example
The following are the simple steps to write a Spring Batch job. Note that these steps are specific to this example and it may differ based on the batch process requirement.
This example takes the input data from a CSV file using FlatFileItemReader API and then without any further processing (Spring Batch provides ItemProcessor for processing the read data before writing to the database) it directly writes into the database table using OrderItemWriter which an implementation class for ItemWriter. Lets follow the below steps to understand this example.
- Step 1: Create a domain object with the required data structure.
- Step 2: Create a FieldSetMapper implementation class which is required for mapping the domain object properties to the CSV file used in this example.
- Step 3: Create a table in the database for storing the data
- Step 4: Create data source configuration file with database credentials.
- Step 5: Create Job context configuration file
- Step 6: Create ItemWriter implementation class which will be used for inserting the processed data to the database.
- Step 7: Create Job Launcher class for invoking the Job and running the batch process.
- Step 8: Run the Job launcher that is created in the step 7.
Spring Batch Project Structure
Employee.java
package javabeat.net; import java.io.Serializable; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name="employee") public class Employee implements Serializable { private String empId; private String city; private String country; @XmlElement(name="EMP_ID") public String getEmpId() { return empId; } public void setEmpId(String empId) { this.empId = empId; } @XmlElement(name="CITY") public String getCity() { return city; } public void setCity(String city) { this.city = city; } @XmlElement(name="COUNTRY") public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } }
EmployeeDataMapper.java
package javabeat.net; import org.springframework.batch.item.file.mapping.FieldSetMapper; import org.springframework.batch.item.file.transform.FieldSet; import org.springframework.validation.BindException; public class EmployeeDataMapper implements FieldSetMapper<Employee> { public Employee mapFieldSet(FieldSet fieldSet) throws BindException { Employee order = new Employee(); order.setEmpId(fieldSet.readString(0)); order.setCity(fieldSet.readString(1)); order.setCountry(fieldSet.readString(2)); return order; } }
OrderItemWriter.java
package javabeat.net; import java.util.List; import javax.sql.DataSource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.batch.item.ItemWriter; public class OrderItemWriter implements ItemWriter<Employee> { private static final String INSERT_EMPLOYEE = "insert into batch.employee " + "( empid, city, country ) values(?,?,?)"; private JdbcTemplate jdbcTemplate; public void write(List<? extends Employee> employees) throws Exception { for (Employee order : employees) { jdbcTemplate.update(INSERT_EMPLOYEE, order.getCity(), order.getCountry(), order.getCountry()); } } public OrderItemWriter(DataSource ds) { this.jdbcTemplate = new JdbcTemplate(ds); } }
datasource-config.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd"> <!-- connect to MySQL database --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/batch" /> <property name="username" value="root" /> <property name="password" value="admin" /> </bean> <bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" /> <!-- create job-meta tables automatically In production you don't need to create this every time. Just create once in production. --> <jdbc:initialize-database data-source="dataSource"> <jdbc:script location="org/springframework/batch/core/schema-drop-mysql.sql" /> <jdbc:script location="org/springframework/batch/core/schema-mysql.sql" /> </jdbc:initialize-database> </beans>
job-config.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:batch="http://www.springframework.org/schema/batch" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-3.0.xsd"> <import resource="datasource-config.xml" /> <import resource="job-context.xml" /> <job id="employeeJob" xmlns="http://www.springframework.org/schema/batch"> <step id="employeeprocessor"> <tasklet> <chunk reader="reader" writer="writer" commit-interval="3" skip-limit="2"> <skippable-exception-classes> <include class="org.springframework.batch.item.file.FlatFileParseException" /> </skippable-exception-classes> </chunk> </tasklet> </step> </job> <bean id="reader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step"> <property name="resource" value="classpath:input/employees.csv" /> <property name="linesToSkip" value="1" /> <property name="lineMapper"> <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper"> <property name="lineTokenizer"> <bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer"> <property name="names" value="EMP_ID,CITY,COUNTRY" /> <property name="delimiter" value="," /> </bean> </property> <property name="fieldSetMapper"> <bean class="javabeat.net.EmployeeDataMapper" /> </property> </bean> </property> </bean> <bean id="writer" class="javabeat.net.OrderItemWriter"> <constructor-arg ref="dataSource" /> </bean> </beans>
job-context.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <!-- stored job-meta in memory <bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"> <property name="transactionManager" ref="transactionManager" /> </bean>--> <!-- stored job-meta in database --> <bean id="jobRepository" class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="transactionManager" ref="transactionManager" /> <property name="databaseType" value="mysql" /> </bean> <bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" /> <bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher"> <property name="jobRepository" ref="jobRepository" /> </bean> </beans>
Main.java
package javabeat.net; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { /** * @param args */ public static void main(String[] args) { String[] config = { "job-config.xml" }; ApplicationContext context = new ClassPathXmlApplicationContext(config); JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher"); Job job = (Job) context.getBean("employeeJob"); try { JobExecution execution = jobLauncher.run(job, new JobParameters()); System.out.println("Exit Status : " + execution.getStatus()); } catch (Exception e) { e.printStackTrace(); } System.out.println("Finished Execution of Batch Job"); } }[wpdm_file id=117]