SpringバッチHello Worldの例
Spring Batchは一連のジョブの実行 – バッチ処理用のフレームワークです。 Spring Batchでは、ジョブは多くのステップで構成され、各ステップは
READ-PROCESS-WRITE`タスクまたは
single operation`タスク(タスクレット)で構成されます。
-
「READ-PROCESS-WRITE」プロセスの場合、それは、
リソース(csv、xml、データベース)、それを “処理”し、他のリソース(csv、xml、データベース)に “書き込む”ことができます。たとえば、あるステップは、CSVファイルからデータを読み取り、処理してデータベースに書き込むことができます。 Spring Batchは、CSV、XML、およびデータベースを読み書きするために作られた多くのクラスを提供します。
-
「シングル」操作タスク(タスクレット)の場合は、単一のタスクを実行することを意味します
ステップが開始または完了する前または完了する前にリソースをクリーンアップするような場合のみです。
-
そして、ステップは一緒に連鎖して仕事として走ることができます.
1 Job = Many Steps. 1 Step = 1 READ-PROCESS-WRITE or 1 Tasklet. Job = {Step 1 -> Step 2 -> Step 3} (Chained together)
春のバッチの例
次のバッチジョブを検討してください。
-
ステップ1 – フォルダAからCSVファイルを読み取り、処理して、フォルダBに書き込みます.
“READ-PROCESS-WRITE”
。ステップ2 – フォルダBからCSVファイルを読み込んで処理し、
データベース。 “READ-PROCESS-WRITE”
。ステップ3 – フォルダBからCSBファイルを削除します。 “タスクレット”
-
ステップ4 – データベースからデータを読み取り、統計を生成して生成する
XML形式でレポートし、フォルダCに書き込みます。 “READ-PROCESS-WRITE”
。ステップ5 – レポートを読んでマネージャのEメールに送信します。 “タスクレット”
Spring Batchでは、次のように宣言できます。
<job id="abcJob" xmlns="http://www.springframework.org/schema/batch"> <step id="step1" next="step2"> <tasklet> <chunk reader="cvsItemReader" writer="cvsItemWriter" processor="itemProcesser" commit-interval="1"/> </tasklet> </step> <step id="step2" next="step3"> <tasklet> <chunk reader="cvsItemReader" writer="databaseItemWriter" processor="itemProcesser" commit-interval="1"/> </tasklet> </step> <step id="step3" next="step4"> <tasklet ref="fileDeletingTasklet"/> </step> <step id="step4" next="step5"> <tasklet> <chunk reader="databaseItemReader" writer="xmlItemWriter" processor="itemProcesser" commit-interval="1"/> </tasklet> </step> <step id="step5"> <tasklet ref="sendingEmailTasklet"/> </step> </job>
ジョブとステップの実行全体がデータベースに格納され、失敗したステップが失敗した場所で再起動できるようになり、ジョブ全体を開始する必要はありません。
1.チュートリアル
このSpring Batchチュートリアルでは、ジョブの作成方法、CSVファイルの読み込み方法、処理方法、出力をXMLファイルに書き込む方法について説明します。
使用されるツールとライブラリ
-
Maven 3
-
Eclipse 4.2
-
JDK 1.6
-
Spring Core 3.2.2.RELEASE
-
Spring OXM 3.2.2.RELEASE
-
Spring JDBC 3.2.2.RELEASE
-
Spring Batch 2.2.0.RELEASE
2.プロジェクトディレクトリ
標準のMavenプロジェクトである最終プロジェクトディレクトリを確認します。
3.プロジェクトの依存関係
Spring Core、Spring Batch、JDK 1.5だけの依存関係が必要です。コメントは読んでください。
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4__0__0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.mkyong</groupId> <artifactId>SpringBatchExample</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>SpringBatchExample</name> <url>http://maven.apache.org</url> <properties> <jdk.version>1.6</jdk.version> <spring.version>3.2.2.RELEASE</spring.version> <spring.batch.version>2.2.0.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> </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>
4.春バッチジョブ
CSVファイル。
report.csv
1001,"213,100",980,"mkyong", 29/7/2013 1002,"320,200",1080,"staff 1", 30/7/2013 1003,"342,197",1200,"staff 2", 31/7/2013
FlatFileItemReader`でcsvファイルを読み込むSpringバッチジョブは
itemProcessor`でデータを処理し、 `StaxEventItemWriter`でXMLファイルに書き込みます。
job-hello-world.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:batch="http://www.springframework.org/schema/batch" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd "> <import resource="../config/context.xml"/> <import resource="../config/database.xml"/> <bean id="report" class="com.mkyong.model.Report" scope="prototype"/> <bean id="itemProcessor" class="com.mkyong.CustomItemProcessor"/> <batch:job id="helloWorldJob"> <batch:step id="step1"> <batch:tasklet> <batch:chunk reader="cvsFileItemReader" writer="xmlItemWriter" processor="itemProcessor" commit-interval="10"> </batch:chunk> </batch:tasklet> </batch:step> </batch:job> <bean id="cvsFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader"> <property name="resource" value="classpath:cvs/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,sales,qty,staffName,date"/> </bean> </property> <property name="fieldSetMapper"> <bean class="com.mkyong.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.mkyong.model.Report</value> </list> </property> </bean> </beans>
CSV値を `Report`オブジェクトにマップし、XMLファイルに書き出します(jaxbアノテーションを介して)。
Report.java
package com.mkyong.model; import java.math.BigDecimal; 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 BigDecimal sales; private int qty; private String staffName; private Date date; @XmlAttribute(name = "id") public int getId() { return id; } public void setId(int id) { this.id = id; } @XmlElement(name = "sales") public BigDecimal getSales() { return sales; } public void setSales(BigDecimal sales) { this.sales = sales; } @XmlElement(name = "qty") public int getQty() { return qty; } public void setQty(int qty) { this.qty = qty; } @XmlElement(name = "staffName") public String getStaffName() { return staffName; } public void setStaffName(String staffName) { this.staffName = staffName; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } @Override public String toString() { return "Report[id=" + id + ", sales=" + sales + ", qty=" + qty + ", staffName=" + staffName + "]"; } }
Date`を変換するには、カスタム
FieldSetMapper`が必要です。データ型変換がない場合は、BeanWrapperFieldSetMapperを使用して値を自動的に名前でマップします。
ReportFieldSetMapper.java
package com.mkyong; 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.mkyong.model.Report; public class ReportFieldSetMapper implements FieldSetMapper<Report> { private SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); @Override public Report mapFieldSet(FieldSet fieldSet) throws BindException { Report report = new Report(); report.setId(fieldSet.readInt(0)); report.setSales(fieldSet.readBigDecimal(1)); report.setQty(fieldSet.readInt(2)); report.setStaffName(fieldSet.readString(3)); //default format yyyy-MM-dd //fieldSet.readDate(4); String date = fieldSet.readString(4); try { report.setDate(dateFormat.parse(date)); } catch (ParseException e) { e.printStackTrace(); } return report; } }
itemProcessorはitemWriterの前に起動されます。
CustomItemProcessor.java
package com.mkyong; import org.springframework.batch.item.ItemProcessor; import com.mkyong.model.Report; public class CustomItemProcessor implements ItemProcessor<Report, Report> { @Override public Report process(Report item) throws Exception { System.out.println("Processing..." + item); return item; } }
Springのコンテキストとデータベースの設定。
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-3.2.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>
database.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-3.2.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.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/test"/> <property name="username" value="root"/> <property name="password" value=""/> </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>
5.それを実行する
バッチジョブを実行する最も簡単な方法。
package com.mkyong; 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 App { public static void main(String[]args) { String[]springConfig = { "spring/batch/jobs/job-hello-world.xml" }; ApplicationContext context = new ClassPathXmlApplicationContext(springConfig); JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher"); Job job = (Job) context.getBean("helloWorldJob"); try { JobExecution execution = jobLauncher.run(job, new JobParameters()); System.out.println("Exit Status : " + execution.getStatus()); } catch (Exception e) { e.printStackTrace(); } System.out.println("Done"); } }
出力
report.xml
<?xml version="1.0" encoding="UTF-8"?> <report> <record id="1001"> <date>2013-07-29T00:00:00+08:00</date> <qty>980</qty> <sales>213100</sales> <staffName>mkyong</staffName> </record> <record id="1002"> <date>2013-07-30T00:00:00+08:00</date> <qty>1080</qty> <sales>320200</sales> <staffName>staff 1</staffName> </record> <record id="1003"> <date>2013-07-31T00:00:00+08:00</date> <qty>1200</qty> <sales>342197</sales> <staffName>staff 2</staffName> </record> </report>
コンソール
Jul 30, 2013 11:52:00 PM org.springframework.batch.core.launch.support.SimpleJobLauncher$1 run INFO: Job:[FlowJob:[name=helloWorldJob]]launched with the following parameters:[{}]Jul 30, 2013 11:52:00 PM org.springframework.batch.core.job.SimpleStepHandler handleStep INFO: Executing step:[step1]Processing...Report[id=1001, sales=213100, qty=980, staffName=mkyong]Processing...Report[id=1002, sales=320200, qty=1080, staffName=staff 1]Processing...Report[id=1003, sales=342197, qty=1200, staffName=staff 2]Jul 30, 2013 11:52:00 PM org.springframework.batch.core.launch.support.SimpleJobLauncher$1 run INFO: Job:[FlowJob:[name=helloWorldJob]]completed with the following parameters:[{}]and the following status:[COMPLETED]Exit Status : COMPLETED Done
ソースコードをダウンロードする
ダウンロードする –
SpringBatch-Hello-World-Example.zip
(27 kb)