Добро пожаловать в Пример Spring Batch. Spring Batch – это модуль фреймворка Spring для выполнения пакетных задач. Мы можем использовать Spring Batch для обработки серии задач.
Пример Spring Batch
Прежде чем приступить к программе примера Spring Batch, давайте ознакомимся с некоторыми терминами Spring Batch.
- A job can consist of ‘n’ number of steps. Each step contains Read-Process-Write task or it can have single operation, which is called tasklet.
- Read-Process-Write в основном означает чтение из источника, такого как база данных, CSV и т. д., затем обработку данных и запись их в источник, такой как база данных, CSV, XML и т. д.
- Задача (Tasklet) означает выполнение одной задачи или операции, например, очистку соединений, освобождение ресурсов после завершения обработки.
- Read-Process-Write и задачи (Tasklets) могут быть объединены в цепочку для запуска задачи (job).
Пример Spring Batch
Давайте рассмотрим рабочий пример реализации Spring Batch. Мы рассмотрим следующий сценарий для целей реализации. Файл CSV, содержащий данные, должен быть преобразован в формат XML вместе с данными, и теги будут названы по имени столбца. Ниже приведены важные инструменты и библиотеки, используемые в примере Spring Batch.
- Апачи Мейвен 3.5.0 – для построения проекта и управления зависимостями.
- Эклипс Оксиген Релиз 4.7.0 – среда разработки для создания приложения на основе Spring Batch Maven.
- Java 1.8
- Spring Core 4.3.12.RELEASE
- Spring OXM 4.3.12.RELEASE
- Spring JDBC 4.3.12.RELEASE
- Spring Batch 3.0.8.RELEASE
- MySQL Java Driver 5.1.25 – использовать в соответствии с вашей установкой MySQL. Это необходимо для таблиц метаданных Spring Batch.
Структура каталогов примера Spring Batch
На изображении ниже показаны все компоненты в нашем примере проекта Spring Batch.
Зависимости Maven для Spring Batch
Ниже приведено содержимое файла pom.xml со всеми необходимыми зависимостями для нашего примера проекта Spring Batch.
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.journaldev.spring</groupId>
<artifactId>SpringBatchExample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SpringBatchDemo</name>
<url>https://maven.apache.org</url>
<properties>
<jdk.version>1.8</jdk.version>
<spring.version>4.3.12.RELEASE</spring.version>
<spring.batch.version>3.0.8.RELEASE</spring.batch.version>
<mysql.driver.version>5.1.25</mysql.driver.version>
<junit.version>4.11</junit.version>
</properties>
<dependencies>
<!-- Spring Core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring jdbc, for database -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring XML to/back object -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- MySQL database driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.driver.version}</version>
</dependency>
<!-- Spring Batch dependencies -->
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-core</artifactId>
<version>${spring.batch.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-infrastructure</artifactId>
<version>${spring.batch.version}</version>
</dependency>
<!-- Spring Batch unit test -->
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-test</artifactId>
<version>${spring.batch.version}</version>
</dependency>
<!-- Junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.10</version>
</dependency>
</dependencies>
<build>
<finalName>spring-batch</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<downloadSources>true</downloadSources>
<downloadJavadocs>false</downloadJavadocs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Обработка CSV-файла в Spring Batch
Вот содержимое нашего образцового CSV-файла для обработки в Spring Batch.
1001,Tom,Moody, 29/7/2013
1002,John,Parker, 30/7/2013
1003,Henry,Williams, 31/7/2013
Конфигурация задачи Spring Batch
Нам нужно определить spring bean и задачу Spring Batch в конфигурационном файле. Ниже приведено содержимое файла job-batch-demo.xml
, это самая важная часть проекта Spring Batch.
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:batch="https://www.springframework.org/schema/batch" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/batch
https://www.springframework.org/schema/batch/spring-batch-3.0.xsd
https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-4.3.xsd
">
<import resource="../config/context.xml" />
<import resource="../config/database.xml" />
<bean id="report" class="com.journaldev.spring.model.Report"
scope="prototype" />
<bean id="itemProcessor" class="com.journaldev.spring.CustomItemProcessor" />
<batch:job id="DemoJobXMLWriter">
<batch:step id="step1">
<batch:tasklet>
<batch:chunk reader="csvFileItemReader" writer="xmlItemWriter"
processor="itemProcessor" commit-interval="10">
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
<bean id="csvFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="resource" value="classpath:csv/input/report.csv" />
<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="id,firstname,lastname,dob" />
</bean>
</property>
<property name="fieldSetMapper">
<bean class="com.journaldev.spring.ReportFieldSetMapper" />
<!-- if no data type conversion, use BeanWrapperFieldSetMapper to map
by name <bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
<property name="prototypeBeanName" value="report" /> </bean> -->
</property>
</bean>
</property>
</bean>
<bean id="xmlItemWriter" class="org.springframework.batch.item.xml.StaxEventItemWriter">
<property name="resource" value="file:xml/outputs/report.xml" />
<property name="marshaller" ref="reportMarshaller" />
<property name="rootTagName" value="report" />
</bean>
<bean id="reportMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>com.journaldev.spring.model.Report</value>
</list>
</property>
</bean>
</beans>
- Мы используем
FlatFileItemReader
для чтения CSV-файла,CustomItemProcessor
для обработки данных и записи их в XML-файл с использованиемStaxEventItemWriter
. batch:job
– Этот тег определяет создаваемую задачу. Свойство Id указывает идентификатор задачи. Мы можем определить несколько задач в одном XML-файле.- batch:step – Этот тег используется для определения различных шагов задачи Spring Batch.
- Две различных стиля обработки предлагает фреймворк Spring Batch, которые называются “Ориентированный на задачи” и “Ориентированный на чанки”. Стиль “Ориентированный на чанки” используется в этом примере для пошагового чтения данных и создания “чанков”, которые будут записаны в рамках транзакции.
- reader: бин Spring, используемый для чтения данных. В этом примере мы использовали бин
csvFileItemReader
, который является экземпляромFlatFileItemReader
. - processor: это класс, используемый для обработки данных. В этом примере мы использовали
CustomItemProcessor
. - writer: бин, используемый для записи данных в файл XML.
- commit-interval: Это свойство определяет размер чанка, который будет подтвержден после завершения обработки. В основном это означает, что ItemReader будет читать данные по одному, а ItemProcessor также будет обрабатывать их таким же образом, но ItemWriter будет записывать данные только тогда, когда их размер будет равен значению commit-interval.
- Три важных интерфейса, используемых в этом проекте, это
ItemReader
,ItemProcessor
иItemWriter
из пакетаorg.springframework.batch.item
.
Модельный класс Spring Batch
Сначала мы считываем файл CSV в объект Java, а затем используем JAXB для записи его в файл XML. Ниже приведен наш класс модели с необходимыми аннотациями JAXB.
package com.journaldev.spring.model;
import java.util.Date;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "record")
public class Report {
private int id;
private String firstName;
private String lastName;
private Date dob;
@XmlAttribute(name = "id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@XmlElement(name = "firstname")
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
@XmlElement(name = "lastname")
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@XmlElement(name = "dob")
public Date getDob() {
return dob;
}
public void setDob(Date dob) {
this.dob = dob;
}
@Override
public String toString() {
return "Report [id=" + id + ", firstname=" + firstName + ", lastName=" + lastName + ", DateOfBirth=" + dob
+ "]";
}
}
Обратите внимание, что поля класса модели должны совпадать с определенными в конфигурации маппера Spring Batch, т.е. property name="names" value="id,firstname,lastname,dob"
в нашем случае.
Spring Batch FieldSetMapper
A custom FieldSetMapper
is needed to convert a Date. If no data type conversion is required, then only BeanWrapperFieldSetMapper
should be used to map the values by name automatically. The java class which extends FieldSetMapper
is ReportFieldSetMapper
.
package com.journaldev.spring;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.validation.BindException;
import com.journaldev.spring.model.Report;
public class ReportFieldSetMapper implements FieldSetMapper {
private SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
public Report mapFieldSet(FieldSet fieldSet) throws BindException {
Report report = new Report();
report.setId(fieldSet.readInt(0));
report.setFirstName(fieldSet.readString(1));
report.setLastName(fieldSet.readString(2));
// формат по умолчанию yyyy-MM-dd
// fieldSet.readDate(4);
String date = fieldSet.readString(3);
try {
report.setDob(dateFormat.parse(date));
} catch (ParseException e) {
e.printStackTrace();
}
return report;
}
}
Spring Batch Item Processor
Теперь, как определено в конфигурации задания, ItemProcessor будет выполнен перед ItemWriter. Мы создали класс CustomItemProcessor.java
для этого.
package com.journaldev.spring;
import org.springframework.batch.item.ItemProcessor;
import com.journaldev.spring.model.Report;
public class CustomItemProcessor implements ItemProcessor<Report, Report> {
public Report process(Report item) throws Exception {
System.out.println("Processing..." + item);
String fname = item.getFirstName();
String lname = item.getLastName();
item.setFirstName(fname.toUpperCase());
item.setLastName(lname.toUpperCase());
return item;
}
}
Мы можем манипулировать данными в реализации ItemProcessor. Как видите, я преобразую значения имени и фамилии в верхний регистр.
Spring Configuration Files
В нашем файле конфигурации весенней партии мы импортировали два дополнительных файла конфигурации – context.xml
и database.xml
.
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-4.3.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>
- jobRepository – JobRepository отвечает за сохранение каждого объекта Java в соответствующую таблицу метаданных для весенней партии.
- transactionManager – это отвечает за фиксацию транзакции, когда размер интервала фиксации и обработанные данные равны.
- jobLauncher – Это ядро весенней партии. Этот интерфейс содержит метод run, который используется для запуска задания.
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:jdbc="https://www.springframework.org/schema/jdbc" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-4.3.xsd
https://www.springframework.org/schema/jdbc
https://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd">
<!-- connect to 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/Test" />
<property name="username" value="test" />
<property name="password" value="test123" />
</bean>
<bean id="transactionManager"
class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
<!-- create job-meta tables automatically -->
<!-- <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>
Spring Batch использует некоторые таблицы метаданных для хранения информации о пакетных заданиях. Мы можем их создать из конфигураций весенней партии, но рекомендуется делать это вручную, выполняя SQL-файлы, как показано в закомментированном коде выше. С точки зрения безопасности лучше не предоставлять пользователю базы данных весенней партии доступ к выполнению DDL-запросов.
Таблицы весенней партии
Spring Batch таблицы очень тесно соответствуют объектам домена, представляющим их в Java. Например – JobInstance, JobExecution, JobParameters и StepExecution соответствуют BATCH_JOB_INSTANCE, BATCH_JOB_EXECUTION, BATCH_JOB_EXECUTION_PARAMS и BATCH_STEP_EXECUTION соответственно. ExecutionContext соответствует как BATCH_JOB_EXECUTION_CONTEXT, так и BATCH_STEP_EXECUTION_CONTEXT. JobRepository отвечает за сохранение и хранение каждого объекта Java в соответствующей таблице. Ниже приведены подробности каждой метаданные таблицы.
- Batch_job_instance: Таблица BATCH_JOB_INSTANCE содержит всю информацию, относящуюся к JobInstance.
- Batch_job_execution_params: Таблица BATCH_JOB_EXECUTION_PARAMS содержит всю информацию, относящуюся к объекту JobParameters.
- Batch_job_execution: Таблица BATCH_JOB_EXECUTION содержит данные, относящиеся к объекту JobExecution. Новая запись добавляется при каждом запуске задачи.
- Batch_step_execution: Таблица BATCH_STEP_EXECUTION содержит всю информацию, относящуюся к объекту StepExecution.
- Batch_job_execution_context: Таблица BATCH_JOB_EXECUTION_CONTEXT содержит данные, относящиеся к ExecutionContext работы. Существует ровно один ExecutionContext для каждого JobExecution, и он содержит все данные уровня задачи, необходимые для выполнения этой конкретной задачи. Эти данные обычно представляют собой состояние, которое необходимо восстановить после сбоя, чтобы JobInstance мог возобновить выполнение с того места, где произошел сбой.
- Batch_step_execution_context: Таблица BATCH_STEP_EXECUTION_CONTEXT содержит данные, относящиеся к контексту ExecutionContext шага. Для каждого StepExecution существует ровно один ExecutionContext, который содержит все данные, необходимые для сохранения состояния выполнения конкретного шага. Эти данные обычно представляют состояние, которое необходимо восстановить после сбоя, чтобы JobInstance мог продолжить выполнение с того момента, где произошел сбой.
- Batch_job_execution_seq: Эта таблица содержит последовательность выполнения данных задачи.
- Batch_step_execution_seq: Эта таблица содержит данные о последовательности выполнения шагов.
- Batch_job_seq: Эта таблица содержит данные о последовательности выполнения задачи. В случае наличия нескольких задач мы получим несколько строк.
Тестовая программа Spring Batch
Наш пример проекта Spring Batch готов, последний шаг – написать тестовый класс для его выполнения как java-программы.
package com.journaldev.spring;
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.launch.JobLauncher;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
String[] springConfig = { "spring/batch/jobs/job-batch-demo.xml" };
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(springConfig);
JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
Job job = (Job) context.getBean("DemoJobXMLWriter");
JobParameters jobParameters = new JobParametersBuilder().addLong("time", System.currentTimeMillis())
.toJobParameters();
try {
JobExecution execution = jobLauncher.run(job, jobParameters);
System.out.println("Exit Status : " + execution.getStatus());
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Done");
context.close();
}
}
Просто запустите вышеуказанную программу, и вы получите выходной XML, подобный приведенному ниже.
<?xml version="1.0" encoding="UTF-8"?><report><record id="1001"><dob>2013-07-29T00:00:00+05:30</dob><firstname>TOM</firstname><lastname>MOODY</lastname></record><record id="1002"><dob>2013-07-30T00:00:00+05:30</dob><firstname>JOHN</firstname><lastname>PARKER</lastname></record><record id="1003"><dob>2013-07-31T00:00:00+05:30</dob><firstname>HENRY</firstname><lastname>WILLIAMS</lastname></record></report>
Вот и все, что касается примера Spring Batch. Вы можете скачать окончательный проект по следующей ссылке.
Скачать пример проекта Spring Batch
Ссылка: Официальное руководство
Source:
https://www.digitalocean.com/community/tutorials/spring-batch-example