春バッチの例
Spring Batchの例へようこそ。Spring Batchは、バッチジョブの実行に使用されるSpringフレームワークのモジュールです。私たちは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 is basically read from a source like Database, CSV etc. then process the data and write it to a source like Database, CSV, XML etc.
- Tasklet means doing a single task or operation like cleaning of connections, freeing up resources after processing is done.
- Read-Process-Write and tasklets can be chained together to run a job.
スプリングバッチの例
スプリングバッチの実装に関する動作例を考えましょう。実装の目的のために以下のシナリオを考慮します。データを含むCSVファイルをXML形式に変換する必要があり、タグは列名に基づいて命名されます。以下に、スプリングバッチの具体例に使用される重要なツールとライブラリを示します。
-
- Apache Maven 3.5.0は、プロジェクトのビルドと依存関係の管理に使用されます。
-
- Eclipse Oxygen Release 4.7.0は、Spring Batch Mavenアプリケーションの作成のためのIDEです。
-
- 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のサンプルプロジェクトに必要なすべての依存関係を含んだ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.scdev.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
スプリングバッチのジョブ設定
私たちは、スプリングのビーンとスプリングバッチジョブを設定ファイルで定義する必要があります。以下は、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.scdev.spring.model.Report"
scope="prototype" />
<bean id="itemProcessor" class="com.scdev.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.scdev.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.scdev.spring.model.Report</value>
</list>
</property>
</bean>
</beans>
-
- CSVファイルを読み込むためにFlatFileItemReaderを使用し、データを処理するためにCustomItemProcessorを使用し、StaxEventItemWriterを使用してXMLファイルに書き込みます。
-
- batch:job – このタグは、作成したいジョブを定義します。IdプロパティはジョブのIDを指定します。1つのxmlファイルで複数のジョブを定義することができます。
-
- batch:step – このタグは、Spring Batchジョブの異なるステップを定義するために使用されます。
-
- Spring Batch Frameworkでは、2つの異なる処理スタイルが提供されており、「タスクレットステップ指向」と「チャンク指向」です。この例では、チャンク指向スタイルが使用されており、データを1つずつ読み込んで「チャンク」を作成し、トランザクションの境界内で書き込まれます。
-
- reader: データを読み込むために使用されるSpring Beanです。この例ではcsvFileItemReaderビーンを使用しています。
-
- processor: データを処理するために使用されるクラスです。この例ではCustomItemProcessorを使用しています。
-
- writer: データをXMLファイルに書き込むために使用されるビーンです。
-
- commit-interval: このプロパティは、処理が完了したときにコミットされるチャンクのサイズを定義します。基本的には、ItemReaderがデータを1つずつ読み込み、ItemProcessorも同じ方法で処理しますが、ItemWriterはcommit-intervalのサイズに等しい場合にのみデータを書き込みます。
- このプロジェクトの一部として使用される3つの重要なインターフェースは、org.springframework.batch.itemパッケージからのItemReader、ItemProcessor、およびItemWriterです。
スプリングバッチのモデルクラス
最初に、CSVファイルをJavaオブジェクトに読み込み、それからJAXBを使ってXMLファイルに書き込んでいます。以下は、必要なJAXB注釈が付いたモデルクラスです。
package com.scdev.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マッパーの設定で定義されたものと同じである必要があります。これは、プロパティ名=”names” value=”id,firstname,lastname,dob”という場合です。
スプリングバッチのFieldSetMapper
日本語で以下の文を自然な文に書き換えてください。オプションは1つだけ必要です。
日付を変換するために カスタムのFieldSetMapperが必要です。データ型の変換が不要な場合は、値を自動的に名前でマッピングするだけで良いので BeanWrapperFieldSetMapperのみを使用してください。FieldSetMapperを拡張したJavaクラスはReportFieldSetMapperです。
package com.scdev.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.scdev.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.scdev.spring;
import org.springframework.batch.item.ItemProcessor;
import com.scdev.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の実装でデータを操作することができます。名前(first name)と姓(last name)の値を大文字に変換していることがわかると思います。
春の設定ファイル
私たちの春のバッチ設定ファイルには、context.xmlとdatabase.xmlの2つの追加の設定ファイルをインポートしています。
<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 – The JobRepository is responsible for storing each Java object into its correct meta-data table for spring batch.
- transactionManager- this is responsible for committing the transaction once size of commit-interval and the processed data is equal.
- jobLauncher – This is the heart of spring batch. This interface contains the run method which is used to trigger the job.
<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の実行アクセスを与えない方が良いでしょう。
スプリングバッチのテーブル
-
- 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に関連するデータを保持しています。JobExecutionごとに正確に1つのJob ExecutionContextがあり、その特定のジョブ実行に必要なすべてのジョブレベルのデータが含まれています。このデータは通常、ジョブインスタンスが失敗した後に再開する必要のある状態を表します。
-
- Batch_step_execution_context: BATCH_STEP_EXECUTION_CONTEXTテーブルはステップのExecutionContextに関連するデータを保持しています。StepExecutionごとに正確に1つのExecutionContextがあり、特定のステップ実行のために永続化する必要のあるすべてのデータが含まれています。このデータは通常、ステップインスタンスが失敗した後に再開する必要のある状態を表します。
-
- Batch_job_execution_seq: このテーブルはジョブのデータ実行シーケンスを保持しています。
-
- Batch_step_execution_seq: このテーブルはステップ実行のシーケンスに関するデータを保持しています。
- Batch_job_seq: このテーブルはジョブのシーケンスデータを保持しています。複数のジョブがある場合、複数の行が返されます。
春バッチのテストプログラム
私たちのSpring Batchのサンプルプロジェクトは準備完了です。最終ステップは、Javaプログラムとして実行するためのテストクラスを作成することです。
package com.scdev.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のサンプルプロジェクトをダウンロードしてください。」
参照:公式ガイド