Jill,Doe
Joe,Doe
Justin,Doe
Jane,Doe
John,Doe
バッチサービスの作成
このガイドでは、基本的なバッチ駆動型ソリューションの作成プロセスについて説明します。
構築するもの
CSV スプレッドシートからデータをインポートし、それをカスタムコードで変換し、データベースに最終結果を保存するサービスを構築します。
必要なもの
約 15 分
Eclipse STS や IntelliJ IDEA のような任意の IDE または VSCode のようなテキストエディター
Java 17 以降
コードを直接 IDE にインポートすることもできます。
本ガイドの完成までの流れ
ほとんどの Spring 入門ガイドと同様に、最初から始めて各ステップを完了するか、すでに慣れている場合は基本的なセットアップステップをバイパスできます。いずれにしても、最終的に動作するコードになります。
最初から始めるには、Spring Initializr から開始に進みます。
基本をスキップするには、次の手順を実行します。
このガイドを Eclipse で「Spring 入門コンテンツのインポート」するか、ソースリポジトリをダウンロードして解凍、または、Git (英語) を使用してクローンを作成します。
git clone https://github.com/spring-guides/gs-batch-processing.git
gs-batch-processing/initial
に cdビジネスデータにジャンプしてください。
完了したときは、gs-batch-processing/complete
のコードに対して結果を確認できます。
Spring Initializr から開始
IDE を使用する場合はプロジェクト作成ウィザードを使用します。IDE を使用せずにコマンドラインなどで開発する場合は、この事前に初期化されたプロジェクトからプロジェクトを ZIP ファイルとしてダウンロードできます。このプロジェクトは、このチュートリアルの例に合うように構成されています。
プロジェクトを手動で初期化するには:
IDE のメニューまたはブラウザーから Spring Initializr を開きます。アプリケーションに必要なすべての依存関係を取り込み、ほとんどのセットアップを行います。
Gradle または Maven のいずれかと、使用する言語を選択します。このガイドは、Java を選択したことを前提としています。
依存関係をクリックし、Spring Batch と HyperSQL データベースを選択します。
生成をクリックします。
結果の ZIP ファイルをダウンロードします。これは、選択して構成された Web アプリケーションのアーカイブです。
Eclipse や IntelliJ のような IDE は新規プロジェクト作成ウィザードから Spring Initializr の機能が使用できるため、手動での ZIP ファイルのダウンロードやインポートは不要です。 |
プロジェクトを Github からフォークして、IDE または他のエディターで開くこともできます。 |
ビジネスデータ
通常、顧客またはビジネスアナリストがスプレッドシートを提供します。この簡単な例では、src/main/resources/sample-data.csv
で作成されたデータを見つけることができます。
このスプレッドシートには、各行にコンマで区切られた名と姓が含まれています。これは、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 は、起動時に schema-@@platform@@.sql を自動的に実行します。-all は、すべてのプラットフォームのデフォルトです。 |
ビジネスクラスを作成する
データの入力および出力の形式を確認できるようになったため、次の例(src/main/java/com/example/batchprocessing/Person.java
から)が示すように、データの行を表すコードを作成できます。
package com.example.batchprocessing;
public record Person(String firstName, String 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) {
final String firstName = person.firstName().toUpperCase();
final String lastName = person.lastName().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/example/batchprocessing/BatchConfiguration.java
の次の例のように Spring @Configuration
クラスを作成する必要があります。この例では、メモリベースのデータベースを使用しています。つまり、データベースが完了すると、データは失われます。次の Bean を BatchConfiguration
クラスに追加して、リーダー、プロセッサー、ライターを定義します。
@Bean
public FlatFileItemReader<Person> reader() {
return new FlatFileItemReaderBuilder<Person>()
.name("personItemReader")
.resource(new ClassPathResource("sample-data.csv"))
.delimited()
.names("firstName", "lastName")
.targetType(Person.class)
.build();
}
@Bean
public PersonItemProcessor processor() {
return new PersonItemProcessor();
}
@Bean
public JdbcBatchItemWriter<Person> writer(DataSource dataSource) {
return new JdbcBatchItemWriterBuilder<Person>()
.sql("INSERT INTO people (first_name, last_name) VALUES (:firstName, :lastName)")
.dataSource(dataSource)
.beanMapped()
.build();
}
コードの最初のチャンクは、入力、プロセッサー、出力を定義します。
reader()
はItemReader
を作成します。sample-data.csv
と呼ばれるファイルを探し、各項目を十分な情報で解析してPerson
に変換します。processor()
は、前に定義したPersonItemProcessor
のインスタンスを作成します。これは、データを大文字に変換するためのものです。writer(DataSource)
はItemWriter
を作成します。これは JDBC 宛先を対象としており、Spring Boot によって作成された dataSource のコピーを自動的に取得します。これには、Java レコードコンポーネントによって駆動される単一のPerson
を挿入するために必要な SQL ステートメントが含まれています。
最後のチャンク(src/main/java/com/example/batchprocessing/BatchConfiguration.java
から)は、実際のジョブ構成を示しています。
@Bean
public Job importUserJob(JobRepository jobRepository,Step step1, JobCompletionNotificationListener listener) {
return new JobBuilder("importUserJob", jobRepository)
.listener(listener)
.start(step1)
.build();
}
@Bean
public Step step1(JobRepository jobRepository, DataSourceTransactionManager transactionManager,
FlatFileItemReader<Person> reader, PersonItemProcessor processor, JdbcBatchItemWriter<Person> writer) {
return new StepBuilder("step1", jobRepository)
.<Person, Person> chunk(3, transactionManager)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}
最初のメソッドはジョブを定義し、2 番目のメソッドは単一のステップを定義します。ジョブはステップから構築されます。各ステップには、リーダー、プロセッサー、ライターが含まれます。
次に、各ステップをリストします (ただし、このジョブにはステップが 1 つしかありません)。ジョブが終了し、Java API は完全に構成されたジョブを生成します。
ステップ定義では、一度に書き込むデータの量を定義します。この場合、一度に最大 3 つのレコードが書き込まれます。次に、前に注入した 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.JobExecutionListener;
import org.springframework.jdbc.core.DataClassRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
@Component
public class JobCompletionNotificationListener implements JobExecutionListener {
private static final Logger log = LoggerFactory.getLogger(JobCompletionNotificationListener.class);
private final JdbcTemplate jdbcTemplate;
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", new DataClassRowMapper<>(Person.class))
.forEach(person -> log.info("Found <{{}}> in the database.", person));
}
}
}
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) {
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 (Person[firstName=Jill, lastName=Doe]) into (Person[firstName=JILL, lastName=DOE])
Converting (Person[firstName=Joe, lastName=Doe]) into (Person[firstName=JOE, lastName=DOE])
Converting (Person[firstName=Justin, lastName=Doe]) into (Person[firstName=JUSTIN, lastName=DOE])
Converting (Person[firstName=Jane, lastName=Doe]) into (Person[firstName=JANE, lastName=DOE])
Converting (Person[firstName=John, lastName=Doe]) into (Person[firstName=JOHN, lastName=DOE])
Found <{Person[firstName=JILL, lastName=DOE]}> in the database.
Found <{Person[firstName=JOE, lastName=DOE]}> in the database.
Found <{Person[firstName=JUSTIN, lastName=DOE]}> in the database.
Found <{Person[firstName=JANE, lastName=DOE]}> in the database.
Found <{Person[firstName=JOHN, lastName=DOE]}> in the database.
要約
おめでとう! スプレッドシートからデータを取り込んで処理し、データベースに書き込むバッチジョブを作成しました。
関連事項
次のガイドも役立ちます。
新しいガイドを作成したり、既存のガイドに貢献したいですか? 投稿ガイドラインを参照してください [GitHub] (英語) 。
すべてのガイドは、コード用の ASLv2 ライセンス、およびドキュメント用の Attribution、NoDerivatives creative commons ライセンス (英語) でリリースされています。 |