ウェブサイト検索

Spring Batchの例


スプリングバッチの例へようこそ。スプリングバッチは、バッチジョブの実行のためのスプリングフレームワークモジュールです。私たちはスプリングバッチを使用して、一連のジョブを処理できます。

Spring Batchの例

春バッチの例プログラムに入る前に、春バッチの用語についてのアイデアを得ましょう。

  • 仕事は'n'の数のステップで構成される場合があります。各ステップには読み取り-処理-書き込みタスクが含まれているか、単一の操作を持つことができ、これをタスクレットと呼びます。
  • 読み取り-処理-書き込みは、基本的にデータベース、CSVなどのソースから読み取り、そのデータを処理して、データベース、CSV、XMLなどのソースに書き込むことです。
  • タスクレットとは、接続のクリーンアップや処理が完了した後のリソースの解放など、一つのタスクや操作を行うことを意味します。
  • 読み取り-処理-書き込みとタスクレットは、ジョブを実行するために連結することができます。

Spring Batchの例

スプリングバッチの実装のための具体的な例を考えましょう。実装の目的で以下のシナリオを考慮します。データを含むCSVファイルをXMLに変換し、データおよびタグは列名にちなんで名付けられます。以下は、スプリングバッチの例に使用される重要なツールとライブラリです。

  1. Apache Maven 3.5.0 - プロジェクトのビルドと依存関係管理のために。
  2. Eclipse Oxygen リリース 4.7.0 - Spring Batch Maven アプリケーションを作成するための IDE。
  3. Java 1.8
  4. Spring Core 4.3.12.RELEASE
  5. 春 OXM 4.3.12.RELEASE
  6. Spring JDBC 4.3.12.RELEASE
  7. Spring Batch 3.0.8.RELEASE
  8. MySQL Java Driver 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入力ファイルの処理

これは、春のバッチ処理のためのサンプルCSVファイルの内容です。

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

Spring Batch ジョブ設定

スプリングビーンとスプリングバッチジョブを設定ファイルで定義する必要があります。以下は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. 私たちは、CSVファイルを読むためにFlatFileItemReaderを使用し、データを処理するためにCustomItemProcessorを使用し、StaxEventItemWriterを使用してXMLファイルに書き込みます。
  2. batch:job – このタグは、作成したいジョブを定義します。Idプロパティは、ジョブのIDを指定します。単一のxmlファイル内で複数のジョブを定義することができます。
  3. バッチ:ステップ – このタグはスプリングバッチジョブの異なるステップを定義するために使用されます。
  4. Spring Batch Frameworkでは、"TaskletStep Oriented"と"Chunk Oriented"の2種類の処理スタイルが提供されています。この例で使用されるChunk Orientedスタイルは、データを1つずつ読み込み、トランザクション境界内で書き出される‘チャンク’を作成することを指します。
  5. リーダー: データを読み取るために使用されるスプリングBean。この例では、FlatFileItemReaderのインスタンスであるcsvFileItemReaderBeanを使用しています。
  6. プロセッサー:これはデータを処理するために使用されるクラスです。この例ではCustomItemProcessorを使用しました。
  7. ライター: データをXMLファイルに書き込むために使用されるビーンズ。
  8. コミット間隔:このプロパティは、処理が完了したときにコミットされるチャンクのサイズを定義します。基本的には、ItemReaderがデータを1つずつ読み込み、ItemProcessorも同様に処理しますが、ItemWriterはコミット間隔のサイズと等しいときのみデータを書き込みます。
  9. このプロジェクトの一部として使用される3つの重要なインターフェースは、org.springframework.batch.itemパッケージからのItemReader、ItemProcessor、およびItemWriterです。

スプリングバッチモデルクラス

まず最初に、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
				+ "]";
	}

}

モデルクラスのフィールドは、スプリングバッチマッパーの設定で定義されているものと同じである必要があります。つまり、私たちの場合はproperty name="names" value="id,firstname,lastname,dob"です。

スプリングバッチ フィールドセットマッパー

日付を変換するためには、カスタムFieldSetMapperが必要です。データ型の変換が必要ない場合は、値を名前で自動的にマップするためにBeanWrapperFieldSetMapperのみを使用すべきです。FieldSetMapperを拡張するjavaクラスは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;

	}

}

スプリングバッチアイテムプロセッサ

現在、ジョブ設定に定義されているように、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構成ファイル

私たちの春のバッチ設定ファイルでは、2つの追加設定ファイル、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>
  • ジョブリポジトリ - ジョブリポジトリは、各JavaオブジェクトをSpring Batchの正しいメタデータテーブルに保存する責任があります。
  • トランザクションマネージャー - コミット間隔のサイズと処理されたデータが等しいときにトランザクションをコミットする責任があります。
  • 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は、バッチジョブ情報を保存するためにいくつかのメタデータテーブルを使用します。これらはSpring Batchの構成から作成することもできますが、上記のコメントコードにあるように、SQLファイルを実行して手動で作成することをお勧めします。セキュリティの観点からは、Spring Batchのデータベースユーザーに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オブジェクトを正しいテーブルに保存および格納する責任を担っています。

  1. バッチジョブインスタンス: BATCH_JOB_INSTANCEテーブルは、ジョブインスタンスに関連するすべての情報を保持します。
  2. Batch_job_execution_params: BATCH_JOB_EXECUTION_PARAMS テーブルは JobParameters オブジェクトに関連するすべての情報を保持します。
  3. バッチジョブ実行: BATCH_JOB_EXECUTIONテーブルは、JobExecutionオブジェクトに関連するデータを保持します。ジョブが実行されるたびに新しい行が追加されます。
  4. バッチステップ実行: BATCH_STEP_EXECUTIONテーブルは、StepExecutionオブジェクトに関連するすべての情報を保持します。
  5. バッチジョブ実行コンテキスト: BATCH_JOB_EXECUTION_CONTEXTテーブルは、ジョブのExecutionContextに関連するデータを保持します。ジョブ実行ごとにちょうど1つのジョブ実行コンテキストがあり、それは特定のジョブ実行に必要なすべてのジョブレベルのデータを含んでいます。このデータは通常、失敗後に取得する必要のある状態を表し、ジョブインスタンスが失敗した場所から再起動できるようにします。
  6. Batch_step_execution_context: BATCH_STEP_EXECUTION_CONTEXTテーブルは、StepのExecutionContextに関連するデータを保持します。各StepExecutionには正確に1つのExecutionContextがあり、特定のステップの実行に必要なすべてのデータが含まれています。このデータは通常、障害が発生した後に取得する必要がある状態を示しており、JobInstanceが障害が発生したところから再起動できるようにします。
  7. Batch_job_execution_seq: このテーブルはジョブのデータ実行シーケンスを保持しています。
  8. Batch_step_execution_seq: このテーブルはステップ実行のためのシーケンスデータを保持しています。
  9. 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のサンプルプロジェクトをダウンロードする

参考:公式ガイド

関連記事: