これは、なにをしたくて書いたもの?
Spring Batchについて、ちょっと見ておこうかな、ということで。
Spring Batch
Spring Batchのページはこちら。
https://spring.io/projects/spring-batch
ドキュメント。
Spring Batch - Reference Documentation
まずは、イントロダクションを読んでみましょう。
Spring Batchが登場したバックグラウンドについて。
Spring Batch Introduction / Background
OSSや関連するコミュニティは、WebベースやMicroservicesベースのアーキテクチャーに大きく注目していますが、バッチ処理に関しては
そうでもありません。バッチには標準的で再利用可能なアーキテクチャーがないため、エンタープライズITでは1回限りの社内ソリューションが
多く作り出されています。
While open source software projects and associated communities have focused greater attention on web-based and microservices-based architecture frameworks, there has been a notable lack of focus on reusable architecture frameworks to accommodate Java-based batch processing needs, despite continued needs to handle such processing within enterprise IT environments. The lack of a standard, reusable batch architecture has resulted in the proliferation of many one-off, in-house solutions developed within client enterprise IT functions.
この状況に対して、SpringSourceとAccentureが協力してSpring Batchができたようです。
どういう時に使うのか?というところですが。
Spring Batch Introduction / Usage Scenarios
一般に、バッチプログラムは次のようなことを行います。
- 多数のレコードをデータベース、ファイル、またはキューから読み取る
- データに対して、なにか処理をする
- フォーマットを変更し、データを書き戻す
Spring Batchは、この基本的なバッチの繰り返しを自動化したもので、以下のようなシーンで役に立ちます。
- バッチ処理の定期的なコミット
- 並行、並列バッチ処理
- メッセージ駆動処理
- 障害後の手動またはスケジュールされた再起動
- 依存するステップの順次処理
- レコードのスキップなど
- バッチサイズが小さい場合の一括トランザクション
Spring Batchのアーキテクチャーはこちら。
Spring Batch Introduction / Spring Batch Architecture
なのですが、Spring Batchのドメインにおける用語を見た方がよいでしょうね。
一般的なバッチの原則およびガイドラインとされるものが、こちらに書かれています。
Spring Batch Introduction / General Batch Principles and Guidelines
たとえば、以下のような内容です。
- 可能な限り単純化し、単一のバッチアプリケーションで複雑な構造を作り出すことは避ける
- データの保存場所と処理を近くに配置すること
- IOを最小限に抑え、メモリを活用すること
- バッチを2度実行しない
- データの整合性については常に最悪の想定を行い、適切なチェックとレコードのバリデーションを行い、データの整合性を維持すること
- 早い段階で、現実的な量のデータを用意してストレステストを実施すること
そして、バッチ処理戦略。
Spring Batch Introduction / Batch Processing Strategies
バッチの設計と実装は、以下の標準的な構成要素から行えるようにすべきです。
- 変換アプリケーション
- 外部システムによって提供または生成されたファイルの種類ごとに、処理に必要な標準フォーマットに変換する
- バリデーションアプリケーション
- 抽出アプリケーション
- データベースやファイルからレコードを読み取り、事前定義されたルールに基づいてレコードを選択、ファイル出力を行う
- 抽出/更新アプリケーション
- データベースやファイルからレコードを読み取り、データベースを変更したりファイル出力を行う
- 処理/更新アプリケーション
- 入力となるトランザクションから抽出、検証を行い、処理を実行する
- データベースから必要なデータを取得するとともに、データベースの更新や出力処理用のレコード作成を含む
- 出力/フォーマットアプリケーション
- ファイルを読み込み、データを再構成して、印刷や外部システム向けに出力ファイルを作成する
これらでビルディングブロックで構築できない場合は、シェルスクリプトなども作成する必要があります。
ユーティリティ的なステップとしては、ソート、分割、マージといったものがあります。
さらにオプションとして、以下についても書かれています。
- バッチウィンドウでの処理
- バッチとオンライン処理の並行性
- バッチとオンライン処理との競合を踏まえた、楽観的ロックや悲観的ロックなどの戦略
- 並列処理
- パーティショニング
- 並列化との組み合わせになる
Spring Batchに関する用語。
- Job … バッチプロセス全体のこと
- JobInstance … 論理的なJobの実行単位。状態を持ち、中断したところから再開することもできる
- JobParameters … Jobを開始するために与えるパラメーター
- JobExecution … Job実行する単一の技術的な概念。JobInstanceは成功または失敗で終了する可能性があるが、実行が正常に完了しない限りはそのJobInstanceは完了したとはみなされない
- Step … Jobのシーケンシャルなフェーズをカプセル化したもの。すべてのJobは、ひとつ以上のStepから構成される
- StepExecution … あるStepの実行を表現したもの。Stepが実行される度に、新しいStepExecutionが作成される。また前のStepが失敗したなどの理由で、Stepが起動しない限りは作成されない
- ExecutionContext … フレームワークによって永続化される、キーと値のペアのこと。Jobの再実行を容易にすることに使われたりする
- JobRepository … Job、JobExecution、Step、StepExecution、ExecutionContextなど、上記のステレオタイプの永続化メカニズム。JobLauncherに対するCRUD操作を提供する
- JobLauncher … JobParametersを与えてJobを起動するためのシンプルなインターフェース
- ItemReader … Stepへの入力となる、ひとつのItemを取得することを抽象化したもの。取得するItemがなくなった場合は、
nullを返すことで表現する - ItemWriter … Stepの出力となる、バッチまたはItemのチャンクを抽象化したもの。次に受け取るItemの情報は知らず、現在の呼び出して渡されたItemの情報のみを知っている
- ItemProcessor … Itemへのビジネス処理を抽象化したもの。ItemReaderがひとつItemを読み取りItemWriterが書き込むまでの間に、ItemProcessorは変換やその他のビジネス処理を行うアクセスポイントを提供する。Itemの処理中にそのItemが無効だと判定した場合は、
nullを返すことでそのItemは書き出す必要がないことを表す
ちなみに、用語集もあります。
Jobの構成や実行については、こちら。
Stepの構成については、こちら。
ここで、Stepの構成にはチャンク指向のStepとTaskletというStepの2種類があることがわかります。
Taskletは初出ですが、executeというメソッドを持つTaskletインターフェースを使って実装されます。
チャンク指向のStepは、先述のItemReader、ItemWriter、そしてItemProcessorから構成されます。
そして、Job内のStepはフローを構成することができます。
Configuring a Step / Controlling Step Flow
ここまでが、基本的な構成要素ですね。
あとは、ItemReader、ItemWriter、そしてItemProcessorに関するドキュメントや、
Spring Batchによる、実装済みのItemReaderとItemWriterの紹介もあります。
List of ItemReaders and ItemWriters
一般的なバッチのパターンや、トランザクションとバッチ処理の組み合わせなどを見るとよいかなと思います。
Batch Processing and Transactions
Spring Integrationと組み合わせることもできるようです。
Spring BootとSpring Batch
“How-to” Guides / Batch Applications
Spring BootでSpring Batchを使う際には、多くの人が質問をするようです。
A number of questions often arise when people use Spring Batch from within a Spring Boot application.
- “How-to” Guides / Batch Applications / Specifying a Batch Data Source
- デフォルトでバッチアプリケーションはJobの詳細を保存するのに、
DataSourceを必要とする DataSourceはデフォルトでシングルトンが想定されているが、DataSourceのBean定義と@BatchDataSourceアノテーションを使用することで、複数扱うことも可能
- デフォルトでバッチアプリケーションはJobの詳細を保存するのに、
- “How-to” Guides / Batch Applications / Running Spring Batch Jobs on Startup
- Spring BatchのAuto Configurationは、
@EnableBatchProcessingアノテーションを付与することで有効になる - デフォルトでは、すべてのJobを起動する
- 起動するJobを絞り込むには、
spring.batch.job.namesにひとつ、または複数のJobの名前(複数の場合はカンマ区切り)で指定する
- Spring BatchのAuto Configurationは、
- “How-to” Guides / Batch Applications / Running from the Command Line
- “How-to” Guides / Batch Applications / Storing the Job Repository
また、Spring Batch向けのSpring Bootのプロパティはこれくらいになります。
spring.batch.jdbc.initialize-schemaspring.batch.jdbc.platformspring.batch.jdbc.schemaspring.batch.jdbc.table-prefixspring.batch.job.enabledspring.batch.job.names
Application Properties / Integration Properties
長くなりましたね。ドキュメントを眺めるのはこれくらいにして、実際にSpring Batchを使っていってみましょう。
環境
今回の環境は、こちら。
$ java --version openjdk 17.0.2 2022-01-18 OpenJDK Runtime Environment (build 17.0.2+8-Ubuntu-120.04) OpenJDK 64-Bit Server VM (build 17.0.2+8-Ubuntu-120.04, mixed mode, sharing) $ mvn --version Apache Maven 3.8.5 (3599d3414f046de2324203b78ddcf9b5e4388aa0) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 17.0.2, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "5.4.0-109-generic", arch: "amd64", family: "unix"
また、データベースとしてはMySQLを使用し、172.17.0.2で動作しているものとします。
$ mysql --version mysql Ver 8.0.28 for Linux on x86_64 (MySQL Community Server - GPL)
お題
今回は、チャンクとTaskletでそれぞれひとつずつJobを作ってみることにします。
データベースアクセスにはJPAを使い、JobRepositoryの永続化先もMySQLとします。
プロジェクトを作成する
まずはSpring Bootプロジェクトを作成します。
$ curl -s https://start.spring.io/starter.tgz \ -d bootVersion=2.6.7 \ -d javaVersion=17 \ -d name=batch-example \ -d groupId=org.littlewings \ -d artifactId=batch-example \ -d version=0.0.1-SNAPSHOT \ -d packageName=org.littlewings.spring.batch \ -d dependencies=batch,data-jpa,mysql \ -d baseDir=batch-example | tar zxvf -
依存関係には、Spring Batch、Spring Data JPA、MySQL JDBC Driverとしています。
プロジェクト内に移動。
$ cd batch-example
<properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-batch</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </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>
デフォルトで作成されているソースコードは、削除しておきます。
$ rm src/main/java/org/littlewings/spring/batch/BatchExampleApplication.java src/test/java/org/littlewings/spring/batch/BatchExampleApplicationTests.java
mainクラスの作成と下準備
最初に、mainクラスとどのジョブでも使いそうなものを作成、設定していきます。
src/main/java/org/littlewings/spring/batch/App.java
package org.littlewings.spring.batch; import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableBatchProcessing public class App { public static void main(String... args) { SpringApplication.run(App.class, args); } }
Spring Bootのドキュメントに習い、@EnableBatchProcessingアノテーションを付与してSpring Batchを有効にします。
“How-to” Guides / Batch Applications / Running Spring Batch Jobs on Startup
テーブル定義は、以下とします。
src/main/resources/schema.sql
create table if not exists book ( isbn varchar(14), title varchar(100), price int, publish_date date, primary key(isbn) );
JPAのエンティティクラス。
src/main/java/org/littlewings/spring/batch/entity/Book.java
package org.littlewings.spring.batch.entity; import java.time.LocalDate; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "book") public class Book { @Id @Column(name = "isbn") String isbn; @Column(name = "title") String title; @Column(name = "price") Integer price; @Column(name = "publish_date") LocalDate publishDate; // getter/setterは省略 }
リポジトリー。Spring Data JPAに従い、JpaRepositoryインターフェースを拡張して用意。
src/main/java/org/littlewings/spring/batch/repository/BookRepository.java
package org.littlewings.spring.batch.repository; import org.littlewings.spring.batch.entity.Book; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface BookRepository extends JpaRepository<Book, String> { }
Spring Bootの設定。
src/main/resources/application.properties
spring.datasource.url=jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8&characterSetResults=utf-8&connectionCollation=utf8mb4_0900_bin spring.datasource.username=kazuhira spring.datasource.password=password spring.sql.init.mode=always spring.batch.jdbc.initialize-schema=always
データベース接続と、schema.sqlの実行(spring.sql.init.mode)および
Spring Batchのメタデータ用のテーブルも作成(spring.batch.jdbc.initialize-schema)するようにします。
取り込むCSVファイルは、ヘッダーなしのものと有りのものの2つを作成。
ヘッダーなし。
src/main/resources/book.csv
978-4798142470,Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発,4400,2016-07-21 978-4774182179,[改訂新版]Spring入門 ――Javaフレームワーク・より良い設計とアーキテクチャ,4180,2016-06-14 978-1492076988,Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications,6265,2021-03-23 978-1484237236,The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud,7361,2019-07-09 978-4798161488,MySQL徹底入門 第4版 MySQL 8.0対応,4180,2020-07-06 978-4797393118,基礎からのMySQL 第3版 (基礎からシリーズ),6038,2017-09-22 978-4873116389,実践ハイパフォーマンスMySQL 第3版,5280,2013-11-25 978-4295000198,やさしく学べるMySQL運用・管理入門【5.7対応】,2860,2016-12-15 978-4798147406,詳解MySQL 5.7 止まらぬ進化に乗り遅れないためのテクニカルガイド (NEXT ONE),3960,2016-08-26 978-4774170206,MariaDB&MySQL全機能バイブル,3850,2014-12-18
ヘッダーあり。
src/main/resources/book_with_header.csv
isbn,title,price,publishDate 978-4798142470,Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発,4400,2016-07-21 978-4774182179,[改訂新版]Spring入門 ――Javaフレームワーク・より良い設計とアーキテクチャ,4180,2016-06-14 978-1492076988,Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications,6265,2021-03-23 978-1484237236,The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud,7361,2019-07-09 978-4798161488,MySQL徹底入門 第4版 MySQL 8.0対応,4180,2020-07-06 978-4797393118,基礎からのMySQL 第3版 (基礎からシリーズ),6038,2017-09-22 978-4873116389,実践ハイパフォーマンスMySQL 第3版,5280,2013-11-25 978-4295000198,やさしく学べるMySQL運用・管理入門【5.7対応】,2860,2016-12-15 978-4798147406,詳解MySQL 5.7 止まらぬ進化に乗り遅れないためのテクニカルガイド (NEXT ONE),3960,2016-08-26 978-4774170206,MariaDB&MySQL全機能バイブル,3850,2014-12-18
チャンクStepを作成する
では、チャンク用のStepを持つJobから作成していきます。
今回のお題だと自分でItemReaderやItemWriterを作成しなくてもよいのですが、まずは自分でも書いてみることにします。
ItemReader。
src/main/java/org/littlewings/spring/batch/chunk/CsvFileItemReader.java
package org.littlewings.spring.batch.chunk; import java.io.BufferedReader; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import org.littlewings.spring.batch.entity.Book; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.batch.item.file.DefaultBufferedReaderFactory; import org.springframework.batch.item.file.ResourceAwareItemReaderItemStream; import org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader; import org.springframework.core.io.Resource; public class CsvFileItemReader extends AbstractItemCountingItemStreamItemReader<Book> implements ResourceAwareItemReaderItemStream<Book> { Logger logger = LoggerFactory.getLogger(CsvFileItemReader.class); Resource resource; BufferedReader reader; DateTimeFormatter publishDateFormatter; @Override protected void doOpen() throws Exception { DefaultBufferedReaderFactory bufferedReaderFactory = new DefaultBufferedReaderFactory(); reader = bufferedReaderFactory.create(resource, "UTF-8"); publishDateFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd"); } @Override protected Book doRead() throws Exception { String line = reader.readLine(); if (line != null) { String[] tokens = line.split(","); Book book = new Book(); book.setIsbn(tokens[0]); book.setTitle(tokens[1]); book.setPrice(Integer.parseInt(tokens[2])); book.setPublishDate(LocalDate.parse(tokens[3], publishDateFormatter)); logger.info("[reader] read book: isbn = {}, title = {}", book.getIsbn(), book.getTitle()); return book; } logger.info("[reader] readed books"); return null; } @Override protected void doClose() throws Exception { reader.close(); } @Override public void setResource(Resource resource) { this.resource = resource; } }
1行のCSV要素をBookクラスにマッピングする箇所は、愚直に作成しました。ヘッダーの読み飛ばしには対応していません。
ItemReaderの作成には、AbstractItemCountingItemStreamItemReaderクラスを継承すると良さそうです。
Abstract base class that provides basic restart capabilities by counting the number of items returned from an ItemReader.
List of ItemReaders and ItemWriters
また、ResourceAwareItemReaderItemStreamインターフェースを実装することで、Spring Batchの他のItemReaderと同じように
読み込み対象のファイルをResourceで表現するようにしておきます。
ちなみに、このItemReaderはクラスの定義時にはSpringのBeanとしては定義せず、Java ConfigでSpringのBeanとして登録します。
ItemProcessor。こちらは、ログ出力するだけにしました。
LoggingBookProcessor.java
package org.littlewings.spring.batch.chunk; import org.littlewings.spring.batch.entity.Book; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.batch.item.ItemProcessor; public class LoggingBookProcessor implements ItemProcessor<Book, Book> { Logger logger = LoggerFactory.getLogger(LoggingBookProcessor.class); @Override public Book process(Book item) throws Exception { logger.info("[processor] process book: isbn = {}, title = {}", item.getIsbn(), item.getTitle()); return item; } }
ItemWriter。
src/main/java/org/littlewings/spring/batch/chunk/BookJpaItemWriter.java
package org.littlewings.spring.batch.chunk; import java.util.List; import org.littlewings.spring.batch.entity.Book; import org.littlewings.spring.batch.repository.BookRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.batch.item.support.AbstractItemStreamItemWriter; import org.springframework.beans.factory.annotation.Autowired; public class BookJpaItemWriter extends AbstractItemStreamItemWriter<Book> { Logger logger = LoggerFactory.getLogger(BookJpaItemWriter.class); @Autowired BookRepository bookRepository; @Override public void write(List<? extends Book> items) throws Exception { logger.info("[writer] write items: size = {}", items.size()); items.forEach(book -> { logger.info("[writer] write item: isbn = {}, title = {}", book.getIsbn(), book.getTitle()); bookRepository.save(book); }); } }
先ほど作成した、Book用のリポジトリーを使ってデータをテーブルに保存します。
継承しているAbstractItemStreamItemWriterクラスは、ItemWriterおよびItemStreamを実装した抽象クラスで、今回は使用していませんが
openメソッドやcloseメソッドをオーバーライドして初期化、終了処理を実装することができます。
Abstract base class that combines the ItemStream and ItemWriter interfaces.
List of ItemReaders and ItemWriters
では、JobおよびStepの定義を行っていきます。
参照するのは、以下あたりですね。
Configuring and Running a Job / Configuring a Job
Configuring a Step / Chunk-oriented Processing / Configuring a Step
今回は、こんな感じで作成。
src/main/java/org/littlewings/spring/batch/config/FileLoadToDatabaseJobConfig.java
package org.littlewings.spring.batch.config; import org.littlewings.spring.batch.chunk.BookJpaItemWriter; import org.littlewings.spring.batch.chunk.CsvFileItemReader; import org.littlewings.spring.batch.chunk.LoggingBookProcessor; import org.littlewings.spring.batch.entity.Book; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepScope; import org.springframework.batch.core.launch.support.RunIdIncrementer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; @Configuration public class FileLoadToDatabaseJobConfig { @Autowired JobBuilderFactory jobBuilderFactory; @Autowired StepBuilderFactory stepBuilderFactory; @Bean public Job fileLoadToDatabaseJob() { return jobBuilderFactory .get("fileLoadToDatabaseJob") .incrementer(new RunIdIncrementer()) .start(fileLoadToDatabaseStep()) .build(); } @Bean public Step fileLoadToDatabaseStep() { return stepBuilderFactory .get("fileLoadToDatabaseStep") .<Book, Book>chunk(3) .reader(csvFileItemReader(null)) .processor(loggingBookProcessor()) .writer(bookJpaItemWriter()) .build(); } @StepScope @Bean public CsvFileItemReader csvFileItemReader(@Value("#{jobParameters['filePath']}") String filePath) { Resource fileResource = new FileSystemResource(filePath); CsvFileItemReader itemReader = new CsvFileItemReader(); itemReader.setResource(fileResource); itemReader.setName("csvFileItemReader"); itemReader.setSaveState(false); return itemReader; } @StepScope @Bean public LoggingBookProcessor loggingBookProcessor() { return new LoggingBookProcessor(); } @StepScope @Bean public BookJpaItemWriter bookJpaItemWriter() { return new BookJpaItemWriter(); } }
ここまでに作成した、ItemReader、ItemProcessor、ItemWriterのBean定義。いずれも@StepScopeとしています。
@StepScope @Bean public CsvFileItemReader csvFileItemReader(@Value("#{jobParameters['filePath']}") String filePath) { Resource fileResource = new FileSystemResource(filePath); CsvFileItemReader itemReader = new CsvFileItemReader(); itemReader.setResource(fileResource); itemReader.setName("csvFileItemReader"); itemReader.setSaveState(false); return itemReader; } @StepScope @Bean public LoggingBookProcessor loggingBookProcessor() { return new LoggingBookProcessor(); } @StepScope @Bean public BookJpaItemWriter bookJpaItemWriter() { return new BookJpaItemWriter(); }
読み込むファイルは、JobParameterとして指定することにしました。JobParameter(またはJobParameters)を使うには、
Beanが@StepScopdeでスコープ定義されている必要があります。
public CsvFileItemReader csvFileItemReader(@Value("#{jobParameters['filePath']}") String filePath) { Resource fileResource = new FileSystemResource(filePath);
Stateというのは、コミットされる前までにどこまで読み取ったか(処理したか)を保存するもので、再起動時に続きから始められる
仕掛けになるものですが。今回はこういった考慮はしないことにするので、falseにしておきます。
itemReader.setSaveState(false);
ItemReaders and ItemWriters / Preventing State Persistence
あとはJobとStepの定義。
@Bean public Job fileLoadToDatabaseJob() { return jobBuilderFactory .get("fileLoadToDatabaseJob") .incrementer(new RunIdIncrementer()) .start(fileLoadToDatabaseStep()) .build(); } @Bean public Step fileLoadToDatabaseStep() { return stepBuilderFactory .get("fileLoadToDatabaseStep") .<Book, Book>chunk(3) .reader(csvFileItemReader(null)) .processor(loggingBookProcessor()) .writer(bookJpaItemWriter()) .build(); }
読み込むファイル内のデータが10件しかないので、チャンクサイズは3にしています。
JobBuilderFactory#getやStepBuilderFactory#getで指定している名前がJob名やStep名になるようなのですが、Bean名は別になるので
他と衝突しないようにBean定義のメソッド名を合わせるなり@Beanアノテーションで指定するなりした方がよいでしょう。
また、incrementerで指定しているRunIdIncrementerですが、これはJobParameterとしてrun.idを追加するものです。
RunIdIncrementer (Spring Batch 4.3.5 API)
run.idは自動でインクリメントされていくのですが、Spring BatchのJobは同じパラメーター指定では起動できなくなるので、
こちらを追加しておきます。
では、ここで1度動作確認しておきます。
パッケージング。
$ mvn package
起動。
$ java -Dspring.batch.job.names=fileLoadToDatabaseJob -jar target/batch-example-0.0.1-SNAPSHOT.jar filePath=src/main/resources/book.csv
Spring Batchでは、デフォルトですべてのJobを起動しようとします。これは、spring.batch.job.namesプロパティで起動するJobを
指摘できます(複数の場合はカンマ区切り)。
この時点ではひとつしかJobがないのですが、最初から指定しておきましょう。
ログは、こんな感じに出力されます。
2022-04-24 20:32:03.650 INFO 36238 --- [ main] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: [filePath=src/main/resources/book.csv]
2022-04-24 20:32:03.794 INFO 36238 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=fileLoadToDatabaseJob]] launched with the following parameters: [{run.id=1, filePath=src/main/resources/book.csv}]
2022-04-24 20:32:03.885 INFO 36238 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [fileLoadToDatabaseStep]
2022-04-24 20:32:04.006 INFO 36238 --- [ main] o.l.s.batch.chunk.CsvFileItemReader : [reader] read book: isbn = 978-4798142470, title = Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発
2022-04-24 20:32:04.009 INFO 36238 --- [ main] o.l.s.batch.chunk.CsvFileItemReader : [reader] read book: isbn = 978-4774182179, title = [改訂新版]Spring入門 ――Java フレームワーク・より良い設計とアーキテクチャ
2022-04-24 20:32:04.009 INFO 36238 --- [ main] o.l.s.batch.chunk.CsvFileItemReader : [reader] read book: isbn = 978-1492076988, title = Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications
2022-04-24 20:32:04.014 INFO 36238 --- [ main] o.l.s.batch.chunk.LoggingBookProcessor : [processor] process book: isbn = 978-4798142470, title = Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発
2022-04-24 20:32:04.014 INFO 36238 --- [ main] o.l.s.batch.chunk.LoggingBookProcessor : [processor] process book: isbn = 978-4774182179, title = [改訂新版]Spring入門 ――Javaフレームワーク・より良い設計とアーキテクチャ
2022-04-24 20:32:04.014 INFO 36238 --- [ main] o.l.s.batch.chunk.LoggingBookProcessor : [processor] process book: isbn = 978-1492076988, title = Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications
2022-04-24 20:32:04.015 INFO 36238 --- [ main] o.l.s.batch.chunk.BookJpaItemWriter : [writer] write items: size = 3
2022-04-24 20:32:04.016 INFO 36238 --- [ main] o.l.s.batch.chunk.BookJpaItemWriter : [writer] write item: isbn = 978-4798142470, title = Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発
2022-04-24 20:32:04.064 INFO 36238 --- [ main] o.l.s.batch.chunk.BookJpaItemWriter : [writer] write item: isbn = 978-4774182179, title = [改訂新版]Spring入門 ――Java フレームワーク・より良い設計とアーキテクチャ
2022-04-24 20:32:04.066 INFO 36238 --- [ main] o.l.s.batch.chunk.BookJpaItemWriter : [writer] write item: isbn = 978-1492076988, title = Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications
2022-04-24 20:32:04.111 INFO 36238 --- [ main] o.l.s.batch.chunk.CsvFileItemReader : [reader] read book: isbn = 978-1484237236, title = The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud
2022-04-24 20:32:04.111 INFO 36238 --- [ main] o.l.s.batch.chunk.CsvFileItemReader : [reader] read book: isbn = 978-4798161488, title = MySQL徹底入門 第4版 MySQL 8.0対応
2022-04-24 20:32:04.111 INFO 36238 --- [ main] o.l.s.batch.chunk.CsvFileItemReader : [reader] read book: isbn = 978-4797393118, title = 基礎からのMySQL 第3版 (基礎 からシリーズ)
2022-04-24 20:32:04.112 INFO 36238 --- [ main] o.l.s.batch.chunk.LoggingBookProcessor : [processor] process book: isbn = 978-1484237236, title = The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud
2022-04-24 20:32:04.112 INFO 36238 --- [ main] o.l.s.batch.chunk.LoggingBookProcessor : [processor] process book: isbn = 978-4798161488, title = MySQL徹底入門 第4版 MySQL 8.0対応
2022-04-24 20:32:04.112 INFO 36238 --- [ main] o.l.s.batch.chunk.LoggingBookProcessor : [processor] process book: isbn = 978-4797393118, title = 基礎からのMySQL 第3版 (基礎からシリーズ)
2022-04-24 20:32:04.112 INFO 36238 --- [ main] o.l.s.batch.chunk.BookJpaItemWriter : [writer] write items: size = 3
2022-04-24 20:32:04.112 INFO 36238 --- [ main] o.l.s.batch.chunk.BookJpaItemWriter : [writer] write item: isbn = 978-1484237236, title = The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud
2022-04-24 20:32:04.114 INFO 36238 --- [ main] o.l.s.batch.chunk.BookJpaItemWriter : [writer] write item: isbn = 978-4798161488, title = MySQL徹底入門 第4版 MySQL 8.0対応
2022-04-24 20:32:04.116 INFO 36238 --- [ main] o.l.s.batch.chunk.BookJpaItemWriter : [writer] write item: isbn = 978-4797393118, title = 基礎からのMySQL 第3版 (基礎 からシリーズ)
2022-04-24 20:32:04.145 INFO 36238 --- [ main] o.l.s.batch.chunk.CsvFileItemReader : [reader] read book: isbn = 978-4873116389, title = 実践ハイパフォーマンスMySQL 第3版
2022-04-24 20:32:04.145 INFO 36238 --- [ main] o.l.s.batch.chunk.CsvFileItemReader : [reader] read book: isbn = 978-4295000198, title = やさしく学べるMySQL運用・管 理入門【5.7対応】
2022-04-24 20:32:04.146 INFO 36238 --- [ main] o.l.s.batch.chunk.CsvFileItemReader : [reader] read book: isbn = 978-4798147406, title = 詳解MySQL 5.7 止まらぬ進化に乗り遅れないためのテクニカルガイド (NEXT ONE)
2022-04-24 20:32:04.146 INFO 36238 --- [ main] o.l.s.batch.chunk.LoggingBookProcessor : [processor] process book: isbn = 978-4873116389, title = 実践ハイパフォーマンスMySQL 第3版
2022-04-24 20:32:04.146 INFO 36238 --- [ main] o.l.s.batch.chunk.LoggingBookProcessor : [processor] process book: isbn = 978-4295000198, title = やさしく学べるMySQL運用・管理入門【5.7対応】
2022-04-24 20:32:04.146 INFO 36238 --- [ main] o.l.s.batch.chunk.LoggingBookProcessor : [processor] process book: isbn = 978-4798147406, title = 詳解MySQL 5.7 止まらぬ 進化に乗り遅れないためのテクニカルガイド (NEXT ONE)
2022-04-24 20:32:04.147 INFO 36238 --- [ main] o.l.s.batch.chunk.BookJpaItemWriter : [writer] write items: size = 3
2022-04-24 20:32:04.147 INFO 36238 --- [ main] o.l.s.batch.chunk.BookJpaItemWriter : [writer] write item: isbn = 978-4873116389, title = 実践ハイパフォーマンスMySQL 第3版
2022-04-24 20:32:04.149 INFO 36238 --- [ main] o.l.s.batch.chunk.BookJpaItemWriter : [writer] write item: isbn = 978-4295000198, title = やさしく学べるMySQL運用・管 理入門【5.7対応】
2022-04-24 20:32:04.150 INFO 36238 --- [ main] o.l.s.batch.chunk.BookJpaItemWriter : [writer] write item: isbn = 978-4798147406, title = 詳解MySQL 5.7 止まらぬ進化に乗り遅れないためのテクニカルガイド (NEXT ONE)
2022-04-24 20:32:04.189 INFO 36238 --- [ main] o.l.s.batch.chunk.CsvFileItemReader : [reader] read book: isbn = 978-4774170206, title = MariaDB&MySQL全機能バイブル
2022-04-24 20:32:04.189 INFO 36238 --- [ main] o.l.s.batch.chunk.CsvFileItemReader : [reader] readed books
2022-04-24 20:32:04.189 INFO 36238 --- [ main] o.l.s.batch.chunk.LoggingBookProcessor : [processor] process book: isbn = 978-4774170206, title = MariaDB&MySQL全機能バイブル
2022-04-24 20:32:04.189 INFO 36238 --- [ main] o.l.s.batch.chunk.BookJpaItemWriter : [writer] write items: size = 1
2022-04-24 20:32:04.190 INFO 36238 --- [ main] o.l.s.batch.chunk.BookJpaItemWriter : [writer] write item: isbn = 978-4774170206, title = MariaDB&MySQL全機能バイブル
2022-04-24 20:32:04.251 INFO 36238 --- [ main] o.s.batch.core.step.AbstractStep : Step: [fileLoadToDatabaseStep] executed in 365ms
2022-04-24 20:32:04.311 INFO 36238 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=fileLoadToDatabaseJob]] completed with the following parameters: [{run.id=1, filePath=src/main/resources/book.csv}] and the following status: [COMPLETED] in 464ms
ItemReader(チャンクサイズ分繰り返し) → ItemProcessor(チャンクサイズ分繰り返し) → ItemWriter(チャンクサイズのデータを一気に
引き渡し)を取得したチャンクの数だけ繰り返すという挙動になっています。
このあたりの呼び出しは、以下を見ると良さそうです。
run.idも指定されていますね。
2022-04-24 20:32:03.794 INFO 36238 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=fileLoadToDatabaseJob]] launched with the following parameters: [{run.id=1, filePath=src/main/resources/book.csv}]
データベースの方は、Jobを起動するまではなにもテーブルがありませんが
mysql> use practice; Database changed mysql> show tables; Empty set (0.00 sec)
Job実行後は以下のようになっています。
mysql> show tables; +------------------------------+ | Tables_in_practice | +------------------------------+ | BATCH_JOB_EXECUTION | | BATCH_JOB_EXECUTION_CONTEXT | | BATCH_JOB_EXECUTION_PARAMS | | BATCH_JOB_EXECUTION_SEQ | | BATCH_JOB_INSTANCE | | BATCH_JOB_SEQ | | BATCH_STEP_EXECUTION | | BATCH_STEP_EXECUTION_CONTEXT | | BATCH_STEP_EXECUTION_SEQ | | book | +------------------------------+ 10 rows in set (0.00 sec)
データが入っていることの確認。
mysql> select * from book; +----------------+---------------------------------------------------------------------------------------------------------+-------+--------------+ | isbn | title | price | publish_date | +----------------+---------------------------------------------------------------------------------------------------------+-------+--------------+ | 978-1484237236 | The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud | 7361 | 2019-07-09 | | 978-1492076988 | Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications | 6265 | 2021-03-23 | | 978-4295000198 | やさしく学べるMySQL運用・管理入門【5.7対応】 | 2860 | 2016-12-15 | | 978-4774170206 | MariaDB&MySQL全機能バイブル | 3850 | 2014-12-18 | | 978-4774182179 | [改訂新版]Spring入門 ――Javaフレームワーク・より良い設計とアーキテクチャ | 4180 | 2016-06-14 | | 978-4797393118 | 基礎からのMySQL 第3版 (基礎からシリーズ) | 6038 | 2017-09-22 | | 978-4798142470 | Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発 | 4400 | 2016-07-21 | | 978-4798147406 | 詳解MySQL 5.7 止まらぬ進化に乗り遅れないためのテクニカルガイド (NEXT ONE) | 3960 | 2016-08-26 | | 978-4798161488 | MySQL徹底入門 第4版 MySQL 8.0対応 | 4180 | 2020-07-06 | | 978-4873116389 | 実践ハイパフォーマンスMySQL 第3版 | 5280 | 2013-11-25 | +----------------+---------------------------------------------------------------------------------------------------------+-------+--------------+ 10 rows in set (0.01 sec)
他のテーブルの内容は、こんな感じになっています。
mysql> select * from BATCH_JOB_EXECUTION; +------------------+---------+-----------------+----------------------------+----------------------------+----------------------------+-----------+-----------+--------------+----------------------------+----------------------------+ | JOB_EXECUTION_ID | VERSION | JOB_INSTANCE_ID | CREATE_TIME | START_TIME | END_TIME | STATUS | EXIT_CODE | EXIT_MESSAGE | LAST_UPDATED | JOB_CONFIGURATION_LOCATION | +------------------+---------+-----------------+----------------------------+----------------------------+----------------------------+-----------+-----------+--------------+----------------------------+----------------------------+ | 1 | 2 | 1 | 2022-04-24 20:32:03.720000 | 2022-04-24 20:32:03.814000 | 2022-04-24 20:32:04.278000 | COMPLETED | COMPLETED | | 2022-04-24 20:32:04.279000 | NULL | +------------------+---------+-----------------+----------------------------+----------------------------+----------------------------+-----------+-----------+--------------+----------------------------+----------------------------+ 1 row in set (0.00 sec) mysql> select * from BATCH_JOB_EXECUTION_CONTEXT; +------------------+--------------------------------+--------------------+ | JOB_EXECUTION_ID | SHORT_CONTEXT | SERIALIZED_CONTEXT | +------------------+--------------------------------+--------------------+ | 1 | {"@class":"java.util.HashMap"} | NULL | +------------------+--------------------------------+--------------------+ 1 row in set (0.00 sec) mysql> select * from BATCH_JOB_EXECUTION_PARAMS; +------------------+---------+----------+-----------------------------+----------------------------+----------+------------+-------------+ | JOB_EXECUTION_ID | TYPE_CD | KEY_NAME | STRING_VAL | DATE_VAL | LONG_VAL | DOUBLE_VAL | IDENTIFYING | +------------------+---------+----------+-----------------------------+----------------------------+----------+------------+-------------+ | 1 | LONG | run.id | | 1970-01-01 09:00:00.000000 | 1 | 0 | Y | | 1 | STRING | filePath | src/main/resources/book.csv | 1970-01-01 09:00:00.000000 | 0 | 0 | Y | +------------------+---------+----------+-----------------------------+----------------------------+----------+------------+-------------+ 2 rows in set (0.00 sec) mysql> select * from BATCH_JOB_EXECUTION_SEQ; +----+------------+ | ID | UNIQUE_KEY | +----+------------+ | 1 | 0 | +----+------------+ 1 row in set (0.00 sec) mysql> select * from BATCH_JOB_INSTANCE; +-----------------+---------+-----------------------+----------------------------------+ | JOB_INSTANCE_ID | VERSION | JOB_NAME | JOB_KEY | +-----------------+---------+-----------------------+----------------------------------+ | 1 | 0 | fileLoadToDatabaseJob | 2247a3263300ca04598c73ac33b0fb7e | +-----------------+---------+-----------------------+----------------------------------+ 1 row in set (0.00 sec) mysql> select * from BATCH_JOB_SEQ; +----+------------+ | ID | UNIQUE_KEY | +----+------------+ | 1 | 0 | +----+------------+ 1 row in set (0.00 sec) mysql> select * from BATCH_STEP_EXECUTION; +-------------------+---------+------------------------+------------------+----------------------------+----------------------------+-----------+--------------+------------+--------------+-------------+-----------------+------------------+--------------------+----------------+-----------+--------------+----------------------------+ | STEP_EXECUTION_ID | VERSION | STEP_NAME | JOB_EXECUTION_ID | START_TIME | END_TIME | STATUS | COMMIT_COUNT | READ_COUNT | FILTER_COUNT | WRITE_COUNT | READ_SKIP_COUNT | WRITE_SKIP_COUNT | PROCESS_SKIP_COUNT | ROLLBACK_COUNT | EXIT_CODE | EXIT_MESSAGE | LAST_UPDATED | +-------------------+---------+------------------------+------------------+----------------------------+----------------------------+-----------+--------------+------------+--------------+-------------+-----------------+------------------+--------------------+----------------+-----------+--------------+----------------------------+ | 1 | 6 | fileLoadToDatabaseStep | 1 | 2022-04-24 20:32:03.885000 | 2022-04-24 20:32:04.250000 | COMPLETED | 4 | 10 | 0 | 10 | 0 | 0 | 0 | 0 | COMPLETED | | 2022-04-24 20:32:04.252000 | +-------------------+---------+------------------------+------------------+----------------------------+----------------------------+-----------+--------------+------------+--------------+-------------+-----------------+------------------+--------------------+----------------+-----------+--------------+----------------------------+ 1 row in set (0.00 sec) mysql> select * from BATCH_STEP_EXECUTION_CONTEXT; +-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------+ | STEP_EXECUTION_ID | SHORT_CONTEXT | SERIALIZED_CONTEXT | +-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------+ | 1 | {"@class":"java.util.HashMap","batch.taskletType":"org.springframework.batch.core.step.item.ChunkOrientedTasklet","batch.stepType":"org.springframework.batch.core.step.tasklet.TaskletStep"} | NULL | +-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------+ 1 row in set (0.00 sec) mysql> select * from BATCH_STEP_EXECUTION_SEQ; +----+------------+ | ID | UNIQUE_KEY | +----+------------+ | 1 | 0 | +----+------------+ 1 row in set (0.01 sec)
RunIdIncrementerを使用しているので、同じ起動パラメーターでもう1度Jobを起動できます。
$ java -Dspring.batch.job.names=fileLoadToDatabaseJob -jar target/batch-example-0.0.1-SNAPSHOT.jar filePath=src/main/resources/book.csv
この時、run.idは2になっています。
2022-04-24 20:33:56.256 INFO 36352 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=fileLoadToDatabaseJob]] launched with the following parameters: [{run.id=2, filePath=src/main/resources/book.csv}]
もし、RunIdIncrementerを削除して
@Bean public Job fileLoadToDatabaseJob() { return jobBuilderFactory .get("fileLoadToDatabaseJob") // .incrementer(new RunIdIncrementer()) .start(fileLoadToDatabaseStep()) .build(); }
同じJobParameter定義で起動しようとすると、すでに完了済みということでエラーになります。
Caused by: org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException: A job instance already exists and is complete for parameters={filePath=src/main/resources/book.csv}. If you want to run this job again, change the parameters.
今回はこちらの定義ではないですが、JobParameterをそもそも使っていない場合は、完了したJobは以下の様に空振りします。
2022-04-24 21:09:56.925 INFO 38225 --- [ main] o.s.batch.core.job.SimpleStepHandler : Step already complete or not restartable, so no action to execute: StepExecution: id=8, version=6, name=fileLoadToDatabaseStep, status=COMPLETED, exitStatus=COMPLETED, readCount=10, filterCount=0, writeCount=10 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=4, rollbackCount=0, exitDescription=
Spring Batchが提供するItemReader、ItemWriterを使う
先ほどは、ItemReaderおよびItemWriterを自分で作成しました。
ですが、Spring BatchがItemReaderとItemWriterの実装をいくつか提供しているので、こちらを使用すれば自分でItemReaderやItemWriterを
実装せずにJobを構築できたりもします。
List of ItemReaders and ItemWriters
今回であれば、FlatFileItemReaderとJpaItemWriterが使えますね。
ItemReaders and ItemWriters / Flat Files
FlatFileItemReader (Spring Batch 4.3.5 API)
JpaItemWriter (Spring Batch 4.3.5 API)
JobおよびStepの定義はこちら。
src/main/java/org/littlewings/spring/batch/config/FileLoadToDatabaseSimplyJobConfig.java
package org.littlewings.spring.batch.config; import java.time.format.DateTimeFormatter; import javax.persistence.EntityManagerFactory; import org.littlewings.spring.batch.chunk.LoggingBookProcessor; import org.littlewings.spring.batch.entity.Book; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepScope; import org.springframework.batch.core.launch.support.RunIdIncrementer; import org.springframework.batch.item.database.JpaItemWriter; import org.springframework.batch.item.database.builder.JpaItemWriterBuilder; import org.springframework.batch.item.file.FlatFileItemReader; import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder; import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar; import org.springframework.format.support.DefaultFormattingConversionService; @Configuration public class FileLoadToDatabaseSimplyJobConfig { @Autowired JobBuilderFactory jobBuilderFactory; @Autowired StepBuilderFactory stepBuilderFactory; @Autowired EntityManagerFactory entityManagerFactory; @Bean public Job fileLoadToDatabaseSimplyJob() { return jobBuilderFactory .get("fileLoadToDatabaseSimplyJob") .incrementer(new RunIdIncrementer()) .start(fileLoadToDatabaseSimplyStep()) .build(); } @Bean public Step fileLoadToDatabaseSimplyStep() { return stepBuilderFactory .get("fileLoadToDatabaseSimplyStep") .<Book, Book>chunk(3) .reader(flatFileBookItemReader(null, false)) .processor(simpleLoggingBookProcessor()) .writer(jpaBookItemWriter()) .build(); } @StepScope @Bean public FlatFileItemReader<Book> flatFileBookItemReader( @Value("#{jobParameters['filePath']}") String filePath, @Value("#{jobParameters['hasHeader'] ?: true}") boolean hasHeader ) { Resource fileResource = new FileSystemResource(filePath); DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false); DateTimeFormatterRegistrar dateTimeFormatterRegistrar = new DateTimeFormatterRegistrar(); dateTimeFormatterRegistrar.setDateFormatter(DateTimeFormatter.ofPattern("uuuu-MM-dd")); dateTimeFormatterRegistrar.registerFormatters(conversionService); BeanWrapperFieldSetMapper<Book> fieldSetMapper = new BeanWrapperFieldSetMapper<>(); fieldSetMapper.setConversionService(conversionService); fieldSetMapper.setTargetType(Book.class); try { fieldSetMapper.afterPropertiesSet(); } catch (Exception e) { throw new RuntimeException(e); } return new FlatFileItemReaderBuilder<Book>() .name("flatFileBookItemReader") .resource(fileResource) .encoding("UTF-8") .delimited() .names(new String[]{"isbn", "title", "price", "publishDate"}) .linesToSkip(hasHeader ? 1 : 0) .fieldSetMapper(fieldSetMapper) .saveState(false) .build(); } @StepScope @Bean public LoggingBookProcessor simpleLoggingBookProcessor() { return new LoggingBookProcessor(); } @StepScope @Bean public JpaItemWriter<Book> jpaBookItemWriter() { return new JpaItemWriterBuilder<Book>() .entityManagerFactory(entityManagerFactory) .build(); } }
ItemProcessorの方は、先ほどのものをそのまま使っています。
FlatFileItemReaderの構築はこちら。
@StepScope @Bean public FlatFileItemReader<Book> flatFileBookItemReader( @Value("#{jobParameters['filePath']}") String filePath, @Value("#{jobParameters['hasHeader'] ?: true}") boolean hasHeader ) { Resource fileResource = new FileSystemResource(filePath); DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false); DateTimeFormatterRegistrar dateTimeFormatterRegistrar = new DateTimeFormatterRegistrar(); dateTimeFormatterRegistrar.setDateFormatter(DateTimeFormatter.ofPattern("uuuu-MM-dd")); dateTimeFormatterRegistrar.registerFormatters(conversionService); BeanWrapperFieldSetMapper<Book> fieldSetMapper = new BeanWrapperFieldSetMapper<>(); fieldSetMapper.setConversionService(conversionService); fieldSetMapper.setTargetType(Book.class); try { fieldSetMapper.afterPropertiesSet(); } catch (Exception e) { throw new RuntimeException(e); } return new FlatFileItemReaderBuilder<Book>() .name("flatFileBookItemReader") .resource(fileResource) .encoding("UTF-8") .delimited() .names(new String[]{"isbn", "title", "price", "publishDate"}) .linesToSkip(hasHeader ? 1 : 0) .fieldSetMapper(fieldSetMapper) .saveState(false) .build(); }
FlatFileItemReaderは、FlatFileItemReaderBuilderで構築します。
return new FlatFileItemReaderBuilder<Book>() .name("flatFileBookItemReader") .resource(fileResource) .encoding("UTF-8") .delimited() .names(new String[]{"isbn", "title", "price", "publishDate"}) .linesToSkip(hasHeader ? 1 : 0) .fieldSetMapper(fieldSetMapper) .saveState(false) .build();
namesで各項目の名前を定義しておき、BeanWrapperFieldSetMapperでBookにマッピングします。BookのプロパティにLocalDateを
使用したものもあるので、DefaultFormattingConversionServiceとDateTimeFormatterRegistrarを使って日付をパースできるように
しています。
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false); DateTimeFormatterRegistrar dateTimeFormatterRegistrar = new DateTimeFormatterRegistrar(); dateTimeFormatterRegistrar.setDateFormatter(DateTimeFormatter.ofPattern("uuuu-MM-dd")); dateTimeFormatterRegistrar.registerFormatters(conversionService); BeanWrapperFieldSetMapper<Book> fieldSetMapper = new BeanWrapperFieldSetMapper<>(); fieldSetMapper.setConversionService(conversionService); fieldSetMapper.setTargetType(Book.class);
読み込むCSVファイルのヘッダーの有無は、JobParameterで指定できるようにしました。
public FlatFileItemReader<Book> flatFileBookItemReader( @Value("#{jobParameters['filePath']}") String filePath, @Value("#{jobParameters['hasHeader'] ?: true}") boolean hasHeader ) {
デフォルトでヘッダー有りにしています。
JpaItemWriterは、JpaItemWriterBuilderで構築します。この時、EntityManagerFactoryが必要になります。
@StepScope @Bean public JpaItemWriter<Book> jpaBookItemWriter() { return new JpaItemWriterBuilder<Book>() .entityManagerFactory(entityManagerFactory) .build(); }
アプリケーションができたので、実行してみましょう。
最初に、データを削除しておきます。
mysql> truncate table book; Query OK, 0 rows affected (0.34 sec) mysql> select * from book; Empty set (0.00 sec)
パッケージングして
$ mvn package
実行してみます。先ほどとはspring.batch.job.namesに指定するJob名を変更します。
$ java -Dspring.batch.job.names=fileLoadToDatabaseSimplyJob -jar target/batch-example-0.0.1-SNAPSHOT.jar filePath=src/main/resources/book_with_header.csv
ログ。
2022-04-24 20:48:35.470 INFO 37031 --- [ main] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: [filePath=src/main/resources/book_with_header.csv]
2022-04-24 20:48:35.682 INFO 37031 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=fileLoadToDatabaseSimplyJob]] launched with the following parameters: [{run.id=1, filePath=src/main/resources/book_with_header.csv}]
2022-04-24 20:48:35.817 INFO 37031 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [fileLoadToDatabaseSimplyStep]
2022-04-24 20:48:35.981 INFO 37031 --- [ main] o.l.s.batch.chunk.LoggingBookProcessor : [processor] process book: isbn = 978-4798142470, title = Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発
2022-04-24 20:48:35.982 INFO 37031 --- [ main] o.l.s.batch.chunk.LoggingBookProcessor : [processor] process book: isbn = 978-4774182179, title = [改訂新版]Spring入門 ――Javaフレームワーク・より良い設計とアーキテクチャ
2022-04-24 20:48:35.982 INFO 37031 --- [ main] o.l.s.batch.chunk.LoggingBookProcessor : [processor] process book: isbn = 978-1492076988, title = Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications
2022-04-24 20:48:36.078 INFO 37031 --- [ main] o.l.s.batch.chunk.LoggingBookProcessor : [processor] process book: isbn = 978-1484237236, title = The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud
2022-04-24 20:48:36.078 INFO 37031 --- [ main] o.l.s.batch.chunk.LoggingBookProcessor : [processor] process book: isbn = 978-4798161488, title = MySQL徹底入門 第4版 MySQL 8.0対応
2022-04-24 20:48:36.078 INFO 37031 --- [ main] o.l.s.batch.chunk.LoggingBookProcessor : [processor] process book: isbn = 978-4797393118, title = 基礎からのMySQL 第3版 (基礎からシリーズ)
2022-04-24 20:48:36.118 INFO 37031 --- [ main] o.l.s.batch.chunk.LoggingBookProcessor : [processor] process book: isbn = 978-4873116389, title = 実践ハイパフォーマンスMySQL 第3版
2022-04-24 20:48:36.118 INFO 37031 --- [ main] o.l.s.batch.chunk.LoggingBookProcessor : [processor] process book: isbn = 978-4295000198, title = やさしく学べるMySQL運用・管理入門【5.7対応】
2022-04-24 20:48:36.118 INFO 37031 --- [ main] o.l.s.batch.chunk.LoggingBookProcessor : [processor] process book: isbn = 978-4798147406, title = 詳解MySQL 5.7 止まらぬ 進化に乗り遅れないためのテクニカルガイド (NEXT ONE)
2022-04-24 20:48:36.160 INFO 37031 --- [ main] o.l.s.batch.chunk.LoggingBookProcessor : [processor] process book: isbn = 978-4774170206, title = MariaDB&MySQL全機能バイブル
2022-04-24 20:48:36.208 INFO 37031 --- [ main] o.s.batch.core.step.AbstractStep : Step: [fileLoadToDatabaseSimplyStep] executed in 391ms
2022-04-24 20:48:36.298 INFO 37031 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=fileLoadToDatabaseSimplyJob]] completed with the following parameters: [{run.id=1, filePath=src/main/resources/book_with_header.csv}] and the following status: [COMPLETED] in 514ms
データが入りました。
mysql> select * from book; +----------------+---------------------------------------------------------------------------------------------------------+-------+--------------+ | isbn | title | price | publish_date | +----------------+---------------------------------------------------------------------------------------------------------+-------+--------------+ | 978-1484237236 | The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud | 7361 | 2019-07-09 | | 978-1492076988 | Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications | 6265 | 2021-03-23 | | 978-4295000198 | やさしく学べるMySQL運用・管理入門【5.7対応】 | 2860 | 2016-12-15 | | 978-4774170206 | MariaDB&MySQL全機能バイブル | 3850 | 2014-12-18 | | 978-4774182179 | [改訂新版]Spring入門 ――Javaフレームワーク・より良い設計とアーキテクチャ | 4180 | 2016-06-14 | | 978-4797393118 | 基礎からのMySQL 第3版 (基礎からシリーズ) | 6038 | 2017-09-22 | | 978-4798142470 | Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発 | 4400 | 2016-07-21 | | 978-4798147406 | 詳解MySQL 5.7 止まらぬ進化に乗り遅れないためのテクニカルガイド (NEXT ONE) | 3960 | 2016-08-26 | | 978-4798161488 | MySQL徹底入門 第4版 MySQL 8.0対応 | 4180 | 2020-07-06 | | 978-4873116389 | 実践ハイパフォーマンスMySQL 第3版 | 5280 | 2013-11-25 | +----------------+---------------------------------------------------------------------------------------------------------+-------+--------------+ 10 rows in set (0.00 sec)
今回のJobでヘッダーなしのファイルを読む時は、JobParameterであるhasHeaderをfalseにします。
$ java -Dspring.batch.job.names=fileLoadToDatabaseSimplyJob -jar target/batch-example-0.0.1-SNAPSHOT.jar filePath=src/main/resources/book.csv hasHeader=false
Taskletを作成する
最後に、Taskletを作成してみます。
今回は、ログ出力のみなのでこんな感じで作成。
src/main/java/org/littlewings/spring/batch/tasklet/LoggingBookTasklet.java
package org.littlewings.spring.batch.tasklet; import java.time.format.DateTimeFormatter; import org.littlewings.spring.batch.repository.BookRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.repeat.RepeatStatus; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Sort; public class LoggingBookTasklet implements Tasklet { Logger logger = LoggerFactory.getLogger(LoggingBookTasklet.class); @Autowired BookRepository bookRepository; @Override public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { DateTimeFormatter publishDateFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd"); bookRepository .findAll(Sort.by(Sort.Direction.DESC, "price")) .forEach(book -> logger.info( "[tasklet] isbn = {}, title = {}, price = {}, publishDate = {}", book.getIsbn(), book.getTitle(), book.getPrice(), book.getPublishDate().format(publishDateFormatter) )); return RepeatStatus.FINISHED; } }
JobおよびStep定義。
src/main/java/org/littlewings/spring/batch/config/LoggingDatabaseJobConfig.java
package org.littlewings.spring.batch.config; import org.littlewings.spring.batch.tasklet.LoggingBookTasklet; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepScope; import org.springframework.batch.core.launch.support.RunIdIncrementer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class LoggingDatabaseJobConfig { @Autowired JobBuilderFactory jobBuilderFactory; @Autowired StepBuilderFactory stepBuilderFactory; @Bean public Job loggingDatabaseJog() { return jobBuilderFactory .get("loggingDatabaseJob") .incrementer(new RunIdIncrementer()) .start(loggingDatabaseStep()) .build(); } @Bean public Step loggingDatabaseStep() { return stepBuilderFactory .get("loggingDatabaseStep") .tasklet(loggingBookTasklet()) .build(); } @StepScope @Bean public LoggingBookTasklet loggingBookTasklet() { return new LoggingBookTasklet(); } }
特徴的なところは、Stepを構築する際にtaskletを指定していることですね。
@Bean public Step loggingDatabaseStep() { return stepBuilderFactory .get("loggingDatabaseStep") .tasklet(loggingBookTasklet()) .build(); }
パッケージングして
$ mvn package
実行。
$ java -Dspring.batch.job.names=loggingDatabaseJob -jar target/batch-example-0.0.1-SNAPSHOT.jar
ログ。
2022-04-24 21:00:08.593 INFO 37435 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=loggingDatabaseJob]] launched with the following parameters: [{run.id=1}]
2022-04-24 21:00:08.697 INFO 37435 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [loggingDatabaseStep]
2022-04-24 21:00:08.990 INFO 37435 --- [ main] o.l.s.batch.tasklet.LoggingBookTasklet : [tasklet] isbn = 978-1484237236, title = The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud, price = 7361, publishDate = 2019-07-09
2022-04-24 21:00:08.990 INFO 37435 --- [ main] o.l.s.batch.tasklet.LoggingBookTasklet : [tasklet] isbn = 978-1492076988, title = Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications, price = 6265, publishDate = 2021-03-23
2022-04-24 21:00:08.990 INFO 37435 --- [ main] o.l.s.batch.tasklet.LoggingBookTasklet : [tasklet] isbn = 978-4797393118, title = 基礎からのMySQL 第3版 (基礎からシリーズ), price = 6038, publishDate = 2017-09-22
2022-04-24 21:00:08.990 INFO 37435 --- [ main] o.l.s.batch.tasklet.LoggingBookTasklet : [tasklet] isbn = 978-4873116389, title = 実践ハイパフォーマンスMySQL 第3版, price = 5280, publishDate = 2013-11-25
2022-04-24 21:00:08.990 INFO 37435 --- [ main] o.l.s.batch.tasklet.LoggingBookTasklet : [tasklet] isbn = 978-4798142470, title = Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発, price = 4400, publishDate = 2016-07-21
2022-04-24 21:00:08.991 INFO 37435 --- [ main] o.l.s.batch.tasklet.LoggingBookTasklet : [tasklet] isbn = 978-4774182179, title = [改訂新版]Spring入門 ――Javaフレームワーク・より良い設計とアーキテクチャ, price = 4180, publishDate = 2016-06-14
2022-04-24 21:00:08.991 INFO 37435 --- [ main] o.l.s.batch.tasklet.LoggingBookTasklet : [tasklet] isbn = 978-4798161488, title = MySQL徹底入門 第4版 MySQL 8.0対応, price = 4180, publishDate = 2020-07-06
2022-04-24 21:00:08.991 INFO 37435 --- [ main] o.l.s.batch.tasklet.LoggingBookTasklet : [tasklet] isbn = 978-4798147406, title = 詳解MySQL 5.7 止まらぬ進化に乗り遅れな いためのテクニカルガイド (NEXT ONE), price = 3960, publishDate = 2016-08-26
2022-04-24 21:00:08.991 INFO 37435 --- [ main] o.l.s.batch.tasklet.LoggingBookTasklet : [tasklet] isbn = 978-4774170206, title = MariaDB&MySQL全機能バイブル, price = 3850, publishDate = 2014-12-18
2022-04-24 21:00:08.991 INFO 37435 --- [ main] o.l.s.batch.tasklet.LoggingBookTasklet : [tasklet] isbn = 978-4295000198, title = やさしく学べるMySQL運用・管理入門【5.7 対応】, price = 2860, publishDate = 2016-12-15
2022-04-24 21:00:09.041 INFO 37435 --- [ main] o.s.batch.core.step.AbstractStep : Step: [loggingDatabaseStep] executed in 343ms
2022-04-24 21:00:09.101 INFO 37435 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=loggingDatabaseJob]] completed with the following parameters: [{run.id=1}] and the following status: [COMPLETED] in 454ms
これで、Taskletの方もOKですね。
JobRepositoryを作成するSQLについて
今回、spring.batch.jdbc.initialize-schemaをalwaysにしていたのでSpring BatchのJobRepositoryで使うテーブルを自動で作成して
くれていましたが、そのSQLは以下のディレクトリにあるようです。
ちなみに、ここまで実行した後の各テーブルの状態は、こんな感じになりました。
mysql> select * from BATCH_JOB_EXECUTION; +------------------+---------+-----------------+----------------------------+----------------------------+----------------------------+-----------+-----------+--------------+----------------------------+----------------------------+ | JOB_EXECUTION_ID | VERSION | JOB_INSTANCE_ID | CREATE_TIME | START_TIME | END_TIME | STATUS | EXIT_CODE | EXIT_MESSAGE | LAST_UPDATED | JOB_CONFIGURATION_LOCATION | +------------------+---------+-----------------+----------------------------+----------------------------+----------------------------+-----------+-----------+--------------+----------------------------+----------------------------+ | 1 | 2 | 1 | 2022-04-24 20:32:03.720000 | 2022-04-24 20:32:03.814000 | 2022-04-24 20:32:04.278000 | COMPLETED | COMPLETED | | 2022-04-24 20:32:04.279000 | NULL | | 2 | 2 | 2 | 2022-04-24 20:33:56.195000 | 2022-04-24 20:33:56.274000 | 2022-04-24 20:33:56.720000 | COMPLETED | COMPLETED | | 2022-04-24 20:33:56.720000 | NULL | | 3 | 2 | 3 | 2022-04-24 20:48:35.586000 | 2022-04-24 20:48:35.731000 | 2022-04-24 20:48:36.245000 | COMPLETED | COMPLETED | | 2022-04-24 20:48:36.246000 | NULL | | 4 | 2 | 4 | 2022-04-24 20:49:52.658000 | 2022-04-24 20:49:52.761000 | 2022-04-24 20:49:53.241000 | COMPLETED | COMPLETED | | 2022-04-24 20:49:53.242000 | NULL | | 5 | 2 | 5 | 2022-04-24 21:00:08.514000 | 2022-04-24 21:00:08.618000 | 2022-04-24 21:00:09.072000 | COMPLETED | COMPLETED | | 2022-04-24 21:00:09.073000 | NULL | +------------------+---------+-----------------+----------------------------+----------------------------+----------------------------+-----------+-----------+--------------+----------------------------+----------------------------+ 5 rows in set (0.00 sec) mysql> select * from BATCH_JOB_EXECUTION_CONTEXT; +------------------+--------------------------------+--------------------+ | JOB_EXECUTION_ID | SHORT_CONTEXT | SERIALIZED_CONTEXT | +------------------+--------------------------------+--------------------+ | 1 | {"@class":"java.util.HashMap"} | NULL | | 2 | {"@class":"java.util.HashMap"} | NULL | | 3 | {"@class":"java.util.HashMap"} | NULL | | 4 | {"@class":"java.util.HashMap"} | NULL | | 5 | {"@class":"java.util.HashMap"} | NULL | +------------------+--------------------------------+--------------------+ 5 rows in set (0.01 sec) mysql> select * from BATCH_JOB_EXECUTION_PARAMS; +------------------+---------+-----------+-----------------------------------------+----------------------------+----------+------------+-------------+ | JOB_EXECUTION_ID | TYPE_CD | KEY_NAME | STRING_VAL | DATE_VAL | LONG_VAL | DOUBLE_VAL | IDENTIFYING | +------------------+---------+-----------+-----------------------------------------+----------------------------+----------+------------+-------------+ | 1 | LONG | run.id | | 1970-01-01 09:00:00.000000 | 1 | 0 | Y | | 1 | STRING | filePath | src/main/resources/book.csv | 1970-01-01 09:00:00.000000 | 0 | 0 | Y | | 2 | LONG | run.id | | 1970-01-01 09:00:00.000000 | 2 | 0 | Y | | 2 | STRING | filePath | src/main/resources/book.csv | 1970-01-01 09:00:00.000000 | 0 | 0 | Y | | 3 | LONG | run.id | | 1970-01-01 09:00:00.000000 | 1 | 0 | Y | | 3 | STRING | filePath | src/main/resources/book_with_header.csv | 1970-01-01 09:00:00.000000 | 0 | 0 | Y | | 4 | LONG | run.id | | 1970-01-01 09:00:00.000000 | 2 | 0 | Y | | 4 | STRING | hasHeader | false | 1970-01-01 09:00:00.000000 | 0 | 0 | Y | | 4 | STRING | filePath | src/main/resources/book.csv | 1970-01-01 09:00:00.000000 | 0 | 0 | Y | | 5 | LONG | run.id | | 1970-01-01 09:00:00.000000 | 1 | 0 | Y | +------------------+---------+-----------+-----------------------------------------+----------------------------+----------+------------+-------------+ 10 rows in set (0.00 sec) mysql> select * from BATCH_JOB_EXECUTION_SEQ; +----+------------+ | ID | UNIQUE_KEY | +----+------------+ | 5 | 0 | +----+------------+ 1 row in set (0.00 sec) mysql> select * from BATCH_JOB_INSTANCE; +-----------------+---------+-----------------------------+----------------------------------+ | JOB_INSTANCE_ID | VERSION | JOB_NAME | JOB_KEY | +-----------------+---------+-----------------------------+----------------------------------+ | 1 | 0 | fileLoadToDatabaseJob | 2247a3263300ca04598c73ac33b0fb7e | | 2 | 0 | fileLoadToDatabaseJob | 1062f6824a3419e049c6d95f6e5e99c2 | | 3 | 0 | fileLoadToDatabaseSimplyJob | 22f79c44e978cc2d028116a60ed9fdd5 | | 4 | 0 | fileLoadToDatabaseSimplyJob | 27f07cdb7c5117f846c1f6eec03d26de | | 5 | 0 | loggingDatabaseJob | 853d3449e311f40366811cbefb3d93d7 | +-----------------+---------+-----------------------------+----------------------------------+ 5 rows in set (0.00 sec) mysql> select * from BATCH_JOB_SEQ; +----+------------+ | ID | UNIQUE_KEY | +----+------------+ | 5 | 0 | +----+------------+ 1 row in set (0.00 sec) mysql> select * from BATCH_STEP_EXECUTION; +-------------------+---------+------------------------------+------------------+----------------------------+----------------------------+-----------+--------------+------------+--------------+-------------+-----------------+------------------+--------------------+----------------+-----------+--------------+----------------------------+ | STEP_EXECUTION_ID | VERSION | STEP_NAME | JOB_EXECUTION_ID | START_TIME | END_TIME | STATUS | COMMIT_COUNT | READ_COUNT | FILTER_COUNT | WRITE_COUNT | READ_SKIP_COUNT | WRITE_SKIP_COUNT | PROCESS_SKIP_COUNT | ROLLBACK_COUNT | EXIT_CODE | EXIT_MESSAGE | LAST_UPDATED | +-------------------+---------+------------------------------+------------------+----------------------------+----------------------------+-----------+--------------+------------+--------------+-------------+-----------------+------------------+--------------------+----------------+-----------+--------------+----------------------------+ | 1 | 6 | fileLoadToDatabaseStep | 1 | 2022-04-24 20:32:03.885000 | 2022-04-24 20:32:04.250000 | COMPLETED | 4 | 10 | 0 | 10 | 0 | 0 | 0 | 0 | COMPLETED | | 2022-04-24 20:32:04.252000 | | 2 | 6 | fileLoadToDatabaseStep | 2 | 2022-04-24 20:33:56.364000 | 2022-04-24 20:33:56.690000 | COMPLETED | 4 | 10 | 0 | 10 | 0 | 0 | 0 | 0 | COMPLETED | | 2022-04-24 20:33:56.692000 | | 3 | 6 | fileLoadToDatabaseSimplyStep | 3 | 2022-04-24 20:48:35.817000 | 2022-04-24 20:48:36.208000 | COMPLETED | 4 | 10 | 0 | 10 | 0 | 0 | 0 | 0 | COMPLETED | | 2022-04-24 20:48:36.209000 | | 4 | 6 | fileLoadToDatabaseSimplyStep | 4 | 2022-04-24 20:49:52.851000 | 2022-04-24 20:49:53.207000 | COMPLETED | 4 | 10 | 0 | 10 | 0 | 0 | 0 | 0 | COMPLETED | | 2022-04-24 20:49:53.208000 | | 5 | 3 | loggingDatabaseStep | 5 | 2022-04-24 21:00:08.698000 | 2022-04-24 21:00:09.041000 | COMPLETED | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | COMPLETED | | 2022-04-24 21:00:09.042000 | +-------------------+---------+------------------------------+------------------+----------------------------+----------------------------+-----------+--------------+------------+--------------+-------------+-----------------+------------------+--------------------+----------------+-----------+--------------+----------------------------+ 5 rows in set (0.00 sec) mysql> select * from BATCH_STEP_EXECUTION_CONTEXT; +-------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------+ | STEP_EXECUTION_ID | SHORT_CONTEXT | SERIALIZED_CONTEXT | +-------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------+ | 1 | {"@class":"java.util.HashMap","batch.taskletType":"org.springframework.batch.core.step.item.ChunkOrientedTasklet","batch.stepType":"org.springframework.batch.core.step.tasklet.TaskletStep"} | NULL | | 2 | {"@class":"java.util.HashMap","batch.taskletType":"org.springframework.batch.core.step.item.ChunkOrientedTasklet","batch.stepType":"org.springframework.batch.core.step.tasklet.TaskletStep"} | NULL | | 3 | {"@class":"java.util.HashMap","batch.taskletType":"org.springframework.batch.core.step.item.ChunkOrientedTasklet","batch.stepType":"org.springframework.batch.core.step.tasklet.TaskletStep"} | NULL | | 4 | {"@class":"java.util.HashMap","batch.taskletType":"org.springframework.batch.core.step.item.ChunkOrientedTasklet","batch.stepType":"org.springframework.batch.core.step.tasklet.TaskletStep"} | NULL | | 5 | {"@class":"java.util.HashMap","batch.taskletType":"org.littlewings.spring.batch.tasklet.LoggingBookTasklet$$EnhancerBySpringCGLIB$$7acedf7a","batch.stepType":"org.springframework.batch.core.step.tasklet.TaskletStep"} | NULL | +-------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------+ 5 rows in set (0.00 sec) mysql> select * from BATCH_STEP_EXECUTION_SEQ; +----+------------+ | ID | UNIQUE_KEY | +----+------------+ | 5 | 0 | +----+------------+ 1 row in set (0.00 sec)
まとめ
Spring Batchを初めて使ってみました。
jBatchを使ったり、情報は見たことがあったのでざっくりと知っているつもりでしたが、実際にソースコードを書いて動かそうとすると
いろいろハマりまして…。
今回試してみて、少しは感覚がわかったかなと思います。