Поиск по сайту:

Пример весенней партии


Добро пожаловать в пример Spring Batch. Spring Batch — это модуль Spring Framework для выполнения пакетного задания. Мы можем использовать весенний пакет для обработки серии заданий.

Пример весенней партии

Прежде чем приступить к рассмотрению примера программы весенней партии, давайте разберемся с терминологией весенней партии.

  • Задание может состоять из n шагов. Каждый шаг содержит задачу Чтение-Процесс-Запись или может иметь одну операцию, которая называется тасклет.
  • Чтение-процесс-запись в основном читается из источника, такого как база данных, CSV и т. д., затем данные обрабатываются и записываются в такой источник, как база данных, CSV, XML и т. д.
  • Tasklet означает выполнение одной задачи или операции, например очистку соединений, освобождение ресурсов после завершения обработки.
  • Чтение-обработка-запись и тасклеты могут быть объединены в цепочку для запуска задания.

Пример весенней партии

Рассмотрим рабочий пример реализации весеннего пакета. Мы рассмотрим следующий сценарий для целей реализации. Файл CSV, содержащий данные, необходимо преобразовать в XML вместе с данными и тегами, которые будут названы в соответствии с именем столбца. Ниже приведены важные инструменты и библиотеки, используемые для примера весенней партии.

  1. Apache Maven 3.5.0 — для сборки проектов и управления зависимостями.
  2. Eclipse Oxygen Release 4.7.0 — IDE для создания весеннего пакетного приложения maven.
  3. Ява 1.8
  4. Spring Core 4.3.12.РЕЛИЗ
  5. Весна OXM 4.3.12.РЕЛИЗ
  6. Весна JDBC 4.3.12.РЕЛИЗ
  7. Весенняя партия 3.0.8.РЕЛИЗ
  8. Драйвер MySQL Java 5.1.25 — используйте в зависимости от вашей установки MySQL. Это необходимо для таблиц метаданных Spring Batch.

Пример структуры каталогов Spring Batch

Зависимости Spring Batch Maven

Ниже приведено содержимое файла pom.xml со всеми необходимыми зависимостями для нашего примера проекта весенней партии.

<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

Вот содержимое нашего образца CSV-файла для весенней пакетной обработки.

1001,Tom,Moody, 29/7/2013
1002,John,Parker, 30/7/2013
1003,Henry,Williams, 31/7/2013

Конфигурация пакетного задания Spring

Мы должны определить весенний компонент и весеннее пакетное задание в файле конфигурации. Ниже приведено содержимое файла job-batch-demo.xml, это самая важная часть проекта весенней партии.

<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>

  1. Мы используем FlatFileItemReader для чтения файла CSV, CustomItemProcessor для обработки данных и записи в файл XML с помощью StaxEventItemWriter.
  2. batch:job — этот тег определяет задание, которое мы хотим создать. Свойство Id указывает идентификатор задания. Мы можем определить несколько заданий в одном XML-файле.
  3. batch:step — этот тег используется для определения различных этапов пакетного задания Spring.
  4. В Spring Batch Framework предлагается два разных стиля обработки: «Ориентированный на шаг задачи» и «Ориентированный на фрагмент». Стиль, ориентированный на фрагменты, используемый в этом примере, относится к чтению данных по одному и созданию «фрагментов», которые будут записаны в пределах границ транзакции.
  5. reader: bean-компонент Spring, используемый для чтения данных. В этом примере мы использовали bean-компонент csvFileItemReader, который является экземпляром FlatFileItemReader.
  6. процессор: это класс, который используется для обработки данных. В этом примере мы использовали CustomItemProcessor.
  7. writer: bean-компонент, используемый для записи данных в XML-файл.
  8. commit-interval: это свойство определяет размер фрагмента, который будет зафиксирован после завершения обработки. В основном это означает, что ItemReader будет читать данные один за другим, и ItemProcessor также будет обрабатывать их таким же образом, но ItemWriter будет записывать данные только тогда, когда они равны размеру интервала фиксации.
  9. В рамках этого проекта используются три важных интерфейса: ItemReader, ItemProcessor и ItemWriter из org.springframework.batch. .item пакет.

Класс Spring Batch Model

Прежде всего, мы читаем файл CSV в объект Java, а затем используем аннотации.

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
				+ "]";
	}

}

Обратите внимание, что поля класса модели должны быть такими же, как определено в конфигурации весеннего пакетного сопоставления, т.е. property name=names value=id,firstname,lastname,dob в нашем случае.

Spring Batch FieldSetMapper

Пользовательский FieldSetMapper необходим для преобразования Date. Если преобразование типа данных не требуется, то для автоматического сопоставления значений по имени следует использовать только BeanWrapperFieldSetMapper. Класс Java, который расширяет FieldSetMapper, называется 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<Report> {

	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));

		// default format 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 Пакетный процессор элементов

Теперь, как определено в конфигурации задания, 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

В наш файл конфигурации весеннего пакета мы импортировали два дополнительных файла конфигурации — 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 — сердце весеннего пакета. Этот интерфейс содержит метод запуска, который используется для запуска задания.

<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.

Весенние пакетные таблицы

  1. Batch_job_instance: таблица BATCH_JOB_INSTANCE содержит всю информацию, относящуюся к JobInstance.
  2. Batch_job_execution_params: таблица BATCH_JOB_EXECUTION_PARAMS содержит всю информацию, относящуюся к объекту JobParameters.
  3. Batch_job_execution: таблица BATCH_JOB_EXECUTION содержит данные, относящиеся к объекту JobExecution. При каждом запуске задания добавляется новая строка.
  4. Batch_step_execution: таблица BATCH_STEP_EXECUTION содержит всю информацию, относящуюся к объекту StepExecution.
  5. Batch_job_execution_context: таблица BATCH_JOB_EXECUTION_CONTEXT содержит данные, относящиеся к ExecutionContext задания. Существует ровно один Job ExecutionContext для каждого JobExecution, и он содержит все данные уровня задания, необходимые для выполнения этого конкретного задания. Эти данные обычно представляют собой состояние, которое необходимо получить после сбоя, чтобы экземпляр JobInstance мог перезапуститься с того места, где произошел сбой.
  6. Batch_step_execution_context: таблица BATCH_STEP_EXECUTION_CONTEXT содержит данные, относящиеся к ExecutionContext шага. Существует ровно один ExecutionContext для каждого StepExecution, и он содержит все данные, которые необходимо сохранить для выполнения определенного шага. Эти данные обычно представляют собой состояние, которое необходимо получить после сбоя, чтобы экземпляр JobInstance мог перезапуститься с того места, где произошел сбой.
  7. Batch_job_execution_seq: эта таблица содержит последовательность выполнения данных задания.
  8. Batch_step_execution_seq: эта таблица содержит данные для последовательности выполнения шага.
  9. Batch_job_seq: эта таблица содержит данные для последовательности заданий. Если у нас есть несколько заданий, мы получим несколько строк.

Программа весеннего пакетного тестирования

Наш пример проекта 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

Ссылка: Официальное руководство