このガイドでは、基本的なバッチ駆動型ソリューションの作成プロセスについて説明します。
構築するもの
CSV スプレッドシートからデータをインポートし、それをカスタムコードで変換し、データベースに最終結果を保存するサービスを構築します。
必要なもの
約 15 分
お気に入りのテキストエディターまたは IDE
コードを直接 IDE にインポートすることもできます。
このガイドを完了する方法
ほとんどの Spring 入門ガイドと同様に、最初から始めて各ステップを完了するか、すでに慣れている場合は基本的なセットアップステップをバイパスできます。いずれにしても、最終的に動作するコードになります。
最初から始めるには、Spring Initializr から開始に進みます。
基本をスキップするには、次の手順を実行します。
このガイドのソースリポジトリをダウンロードして解凍するか、Git (英語) を使用してクローンを作成します。
git clone https://github.com/spring-guides/gs-batch-processing.git (英語)
gs-batch-processing/initial
に cdビジネスクラスを作成するにジャンプしてください。
完了したときは、gs-batch-processing/complete
のコードに対して結果を確認できます。
ビジネスデータ
通常、顧客またはビジネスアナリストがスプレッドシートを提供します。この簡単な例では、src/main/resources/sample-data.csv
で作成されたデータを見つけることができます。
Jill,Doe
Joe,Doe
Justin,Doe
Jane,Doe
John,Doe
このスプレッドシートには、各行にコンマで区切られた名と姓が含まれています。これは、Spring がカスタマイズなしで処理できるかなり一般的なパターンです。
次に、データを保存するテーブルを作成する SQL スクリプトを作成する必要があります。このようなスクリプトは src/main/resources/schema-all.sql
にあります。
DROP TABLE people IF EXISTS;
CREATE TABLE people (
person_id BIGINT IDENTITY NOT NULL PRIMARY KEY,
first_name VARCHAR(20),
last_name VARCHAR(20)
);
Spring Boot は、起動時に [email protected] (英語) @[email protected] (英語) @.sql を自動的に実行します。-all は、すべてのプラットフォームのデフォルトです。 |
Spring Initializr から開始
すべての Spring アプリケーションでは、Spring Initializr (英語) から始める必要があります。Initializr は、アプリケーションに必要なすべての依存関係をすばやく取り込む方法を提供し、多くの設定を行います。この例では、Spring Batch および HyperSQL データベースの依存関係が必要です。
次のリストは、Maven を選択したときに作成される pom.xml
ファイルを示しています。
<?xml version="1.0" encoding="UTF-8"?> <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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>batch-processing</artifactId> <version>0.0.1-SNAPSHOT</version> <name>batch-processing</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-batch</artifactId> </dependency> <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.batch</groupId> <artifactId>spring-batch-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
次のリストは、Gradle を選択したときに作成される build.gradle
ファイルを示しています。
plugins { id 'org.springframework.boot' version '2.2.2.RELEASE' id 'io.spring.dependency-management' version '1.0.8.RELEASE' id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '1.8' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-batch' runtimeOnly 'org.hsqldb:hsqldb' testImplementation('org.springframework.boot:spring-boot-starter-test') { exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' } testImplementation 'org.springframework.batch:spring-batch-test' } test { useJUnitPlatform() }
ビジネスクラスを作成する
データの入力および出力の形式を確認できるようになったため、次の例(src/main/java/com/example/batchprocessing/Person.java
から)が示すように、データの行を表すコードを作成できます。
package com.example.batchprocessing;
public class Person {
private String lastName;
private String firstName;
public Person() {
}
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public String toString() {
return "firstName: " + firstName + ", lastName: " + lastName;
}
}
Person
クラスをインスタンス化するには、コンストラクターを介して、またはプロパティを設定して、姓と名を使用します。
中間プロセッサーを作成する
バッチ処理の一般的なパラダイムは、データを取り込み、変換し、別の場所にパイプで送ることです。ここでは、名前を大文字に変換する簡単なトランスフォーマーを作成する必要があります。次のリスト(src/main/java/com/example/batchprocessing/PersonItemProcessor.java
から)は、その方法を示しています。
package com.example.batchprocessing;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.item.ItemProcessor;
public class PersonItemProcessor implements ItemProcessor<Person, Person> {
private static final Logger log = LoggerFactory.getLogger(PersonItemProcessor.class);
@Override
public Person process(final Person person) throws Exception {
final String firstName = person.getFirstName().toUpperCase();
final String lastName = person.getLastName().toUpperCase();
final Person transformedPerson = new Person(firstName, lastName);
log.info("Converting (" + person + ") into (" + transformedPerson + ")");
return transformedPerson;
}
}
PersonItemProcessor
は、Spring Batch の ItemProcessor
インターフェースを実装しています。これにより、このガイドの後半で定義するバッチジョブにコードを簡単に接続できます。インターフェースに従って、受信 Person
オブジェクトを受け取り、その後、それを大文字の Person
に変換します。
入力と出力のタイプは同じである必要はありません。実際、1 つのデータソースを読み取った後、アプリケーションのデータフローに異なるデータタイプが必要になる場合があります。 |
バッチジョブをまとめる
ここで、実際のバッチジョブをまとめる必要があります。Spring Batch は、カスタムコードを記述する必要性を減らす多くのユーティリティクラスを提供します。代わりに、ビジネスロジックに集中できます。
ジョブを構成するには、src/main/java/com/exampe/batchprocessing/BatchConfiguration.java
の次の例のように、最初に Spring @Configuration
クラスを作成する必要があります。
@Configuration
@EnableBatchProcessing
public class BatchConfiguration {
@Autowired
public JobBuilderFactory jobBuilderFactory;
@Autowired
public StepBuilderFactory stepBuilderFactory;
...
}
手始めに、@EnableBatchProcessing
アノテーションは、ジョブをサポートし、多くの脚の作業を節約する多くの重要な Bean を追加します。この例では、メモリベースのデータベース(@EnableBatchProcessing
が提供)を使用しています。つまり、完了するとデータが失われます。また、さらに必要ないくつかのファクトリをオートワイヤーします。次に、BatchConfiguration
クラスに次の Bean を追加して、リーダー、プロセッサー、ライターを定義します。
@Bean
public FlatFileItemReader<Person> reader() {
return new FlatFileItemReaderBuilder<Person>()
.name("personItemReader")
.resource(new ClassPathResource("sample-data.csv"))
.delimited()
.names(new String[]{"firstName", "lastName"})
.fieldSetMapper(new BeanWrapperFieldSetMapper<Person>() {{
setTargetType(Person.class);
}})
.build();
}
@Bean
public PersonItemProcessor processor() {
return new PersonItemProcessor();
}
@Bean
public JdbcBatchItemWriter<Person> writer(DataSource dataSource) {
return new JdbcBatchItemWriterBuilder<Person>()
.itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>())
.sql("INSERT INTO people (first_name, last_name) VALUES (:firstName, :lastName)")
.dataSource(dataSource)
.build();
}
コードの最初のチャンクは、入力、プロセッサー、出力を定義します。
reader()
はItemReader
を作成します。sample-data.csv
と呼ばれるファイルを探し、各項目を十分な情報で解析してPerson
に変換します。processor()
は、前に定義したPersonItemProcessor
のインスタンスを作成します。これは、データを大文字に変換するためのものです。writer(DataSource)
はItemWriter
を作成します。これは JDBC 宛先を対象としており、@EnableBatchProcessing
によって作成された dataSource のコピーを自動的に取得します。Java Bean プロパティによって駆動される単一のPerson
を挿入するために必要な SQL ステートメントが含まれています。
最後のチャンク(src/main/java/com/example/batchprocessing/BatchConfiguration.java
から)は、実際のジョブ構成を示しています。
@Bean
public Job importUserJob(JobCompletionNotificationListener listener, Step step1) {
return jobBuilderFactory.get("importUserJob")
.incrementer(new RunIdIncrementer())
.listener(listener)
.flow(step1)
.end()
.build();
}
@Bean
public Step step1(JdbcBatchItemWriter<Person> writer) {
return stepBuilderFactory.get("step1")
.<Person, Person> chunk(10)
.reader(reader())
.processor(processor())
.writer(writer)
.build();
}
最初のメソッドはジョブを定義し、2 番目のメソッドは単一のステップを定義します。ジョブはステップから構築されます。各ステップには、リーダー、プロセッサー、ライターが含まれます。
このジョブ定義では、ジョブがデータベースを使用して実行状態を維持するため、インクリメンターが必要です。次に、各ステップをリストします(ただし、このジョブには 1 つのステップしかありません)。ジョブは終了し、Java API は完全に構成されたジョブを生成します。
ステップ定義では、一度に書き込むデータ量を定義します。この場合、一度に最大 10 個のレコードが書き込まれます。次に、以前に注入した Bean を使用して、リーダー、プロセッサー、ライターを構成します。
chunk() は汎用メソッドであるため、<Person,Person> の接頭辞が付きます。これは、処理の各「チャンク」の入力および出力タイプを表し、ItemReader<Person> および ItemWriter<Person> と並んでいます。 |
バッチ構成の最後の部分は、ジョブが完了したときに通知を受ける方法です。次の例(src/main/java/com/example/batchprocessing/JobCompletionNotificationListener.java
から)は、そのようなクラスを示しています。
package com.example.batchprocessing;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.listener.JobExecutionListenerSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
@Component
public class JobCompletionNotificationListener extends JobExecutionListenerSupport {
private static final Logger log = LoggerFactory.getLogger(JobCompletionNotificationListener.class);
private final JdbcTemplate jdbcTemplate;
@Autowired
public JobCompletionNotificationListener(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public void afterJob(JobExecution jobExecution) {
if(jobExecution.getStatus() == BatchStatus.COMPLETED) {
log.info("!!! JOB FINISHED! Time to verify the results");
jdbcTemplate.query("SELECT first_name, last_name FROM people",
(rs, row) -> new Person(
rs.getString(1),
rs.getString(2))
).forEach(person -> log.info("Found <" + person + "> in the database."));
}
}
}
JobCompletionNotificationListener
は、ジョブが BatchStatus.COMPLETED
の場合にリッスンし、JdbcTemplate
を使用して結果をインスペクションします。
アプリケーションを実行可能にする
バッチ処理は Web アプリと WAR ファイルに埋め込むことができますが、以下に示すより単純なアプローチはスタンドアロンアプリケーションを作成します。古き良き Java main()
メソッドによって駆動される単一の実行可能な JAR ファイルにすべてをパッケージ化します。
Spring Initializr がアプリケーションクラスを作成しました。この単純な例の場合、それをさらに変更しなくても機能します。次のリスト(src/main/java/com/example/batchprocessing/BatchProcessingApplication.java
から)は、アプリケーションクラスを示しています。
package com.example.batchprocessing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BatchProcessingApplication {
public static void main(String[] args) throws Exception {
System.exit(SpringApplication.exit(SpringApplication.run(BatchProcessingApplication.class, args)));
}
}
@SpringBootApplication
は、次のすべてを追加する便利なアノテーションです。
@Configuration
: アプリケーションコンテキストの Bean 定義のソースとしてクラスにタグを付けます。@EnableAutoConfiguration
: クラスパス設定、他の Bean、さまざまなプロパティ設定に基づいて Bean の追加を開始するよう Spring Boot に指示します。例:spring-webmvc
がクラスパスにある場合、このアノテーションはアプリケーションに Web アプリケーションとしてフラグを立て、DispatcherServlet
のセットアップなどの主要な動作をアクティブにします。@ComponentScan
: Spring に、com/example
パッケージ内の他のコンポーネント、構成、サービスを探して、コントローラーを検出させるように指示します。
main()
メソッドは、Spring Boot の SpringApplication.run()
メソッドを使用してアプリケーションを起動します。XML が 1 行もないことに気付きましたか? web.xml
ファイルもありません。この Web アプリケーションは 100% 純粋な Java であり、接続機能やインフラストラクチャの構成に対処する必要はありませんでした。
SpringApplication.exit()
および System.exit()
は、ジョブの補完時に JVM が確実に終了することに注意してください。詳細については、Spring Boot リファレンスドキュメントのアプリケーション終了セクションを参照してください。
デモンストレーションのために、JdbcTemplate
を作成し、データベースを照会し、バッチジョブが挿入する人の名前を出力するコードがあります。
実行可能 JAR を構築する
コマンドラインから Gradle または Maven を使用してアプリケーションを実行できます。必要なすべての依存関係、クラス、リソースを含む単一の実行可能 JAR ファイルを構築して実行することもできます。実行可能な jar を構築すると、開発ライフサイクル全体、さまざまな環境などで、アプリケーションとしてサービスを簡単に提供、バージョン管理、デプロイできます。
Gradle を使用する場合、./gradlew bootRun
を使用してアプリケーションを実行できます。または、次のように、./gradlew build
を使用して JAR ファイルをビルドしてから、JAR ファイルを実行できます。
Maven を使用する場合、./mvnw spring-boot:run
を使用してアプリケーションを実行できます。または、次のように、./mvnw clean package
で JAR ファイルをビルドしてから、JAR ファイルを実行できます。
ここで説明する手順は、実行可能な JAR を作成します。クラシック WAR ファイルを作成することもできます。 |
ジョブは、変換された各人の行を出力します。ジョブの実行後、データベースのクエリからの出力も確認できます。次の出力のようになります。
Converting (firstName: Jill, lastName: Doe) into (firstName: JILL, lastName: DOE)
Converting (firstName: Joe, lastName: Doe) into (firstName: JOE, lastName: DOE)
Converting (firstName: Justin, lastName: Doe) into (firstName: JUSTIN, lastName: DOE)
Converting (firstName: Jane, lastName: Doe) into (firstName: JANE, lastName: DOE)
Converting (firstName: John, lastName: Doe) into (firstName: JOHN, lastName: DOE)
Found <firstName: JILL, lastName: DOE> in the database.
Found <firstName: JOE, lastName: DOE> in the database.
Found <firstName: JUSTIN, lastName: DOE> in the database.
Found <firstName: JANE, lastName: DOE> in the database.
Found <firstName: JOHN, lastName: DOE> in the database.
要約
おめでとう!スプレッドシートからデータを取り込んで処理し、データベースに書き込むバッチジョブを作成しました。
関連事項
次のガイドも役立ちます。
新しいガイドを作成したり、既存のガイドに貢献したいですか? 投稿ガイドラインを参照してください: GitHub (英語) 。
すべてのガイドは、コード用の ASLv2 ライセンス、およびドキュメント用の Attribution、NoDerivatives クリエイティブコモンズライセンス (英語) でリリースされています。 |