自作クラスをrestartできるようにするには? | Java Springの逆引きメモ

Java Springの逆引きメモ

JavaのSpring frameworkのメモを書いていきます!
初心者の勉強ノートなので間違いがあるかもしれませんが、何かヒントになることがあれば幸いです。

SpringBatchで用意しているItemReader、ItemWriterについては、既にrestartできるように設計されています。

自作で作った場合は自分で設計しなければなりません。

restartできるように設計するときに、どのように考えればよいか?

また、実際にどのようにプログラムすればよいかを簡単に見ていきます。


 ※注意: Springが用意しているクラスの中にはテストや検証を簡単にするものがあり、

  これらはrestart設計されてません。


【restart可能にするための基本的考え方】

restartするということは、前回どこか途中で終了しています。

基本的にはどこまで読み込んだか?どこまで書き込んだか?を保存しておく必要があります。

そして、Stepを開始したときに保存した値を読み込んで、途中で終わっていればそこまでの処理を実行する必要があります。


例えば、前回ItemWriterが5回目で失敗した場合、ItemReaderで4回目までデータを読み飛ばしてStep処理を始めれば、後はJobに任せれば5個目のデータを読み込んでItemProcessor、ItemWriterにデータを渡すのでOKということにりますね!


そうすると知っておくべきポイントが以下のようになるかと思います。

 ・最後のデータの位置を保存する方法

 ・Stepが開始したときにデータを読み飛ばす方法


では上記をこれから見ていくことにしましょう!



【最後のデータの位置を保存する方法】

まず、ExcecutionContextについて知っておきましょう!

簡単に言うと、データを保存しておくMapです。

StepExecutionとJobExecutionそれぞれに別々のExcecutionContextが保持されています。

Stepについては、Stepごとに別々のインスタンスで保持されています。

詳しい説明は、・Executionとは? (SpringBatch用語) を参照ください。


まあ、ほぼこれが答えです(笑)


 例: executionContext.putInt("sendMailCnt", this.sendedCount);


こんな風にすれば値が保持されます。

そして、設定した値がDBに保存されることになります。


保存についてはもうひとつ重要なポイントがあります。

ItemWriterがコミットしたときに保存しなければなりません。

なぜならもし、ItemReaderがデータを読み込んだときに保存すれば、その後ロールバックしたら数がずれちゃいますよね?

例えば、コミット間隔が5個で、10個目のデータまで読み込み、ItemWriterで8個目のデータ書き込みでロールバックして終了した場合。

コミットされているのは5個目までなので、restart時は6個目からItemReaderが読み込み始めないといけません。

もし10個目のデータを読み込んだ段階で10という値を保存してしまえば11個目から読み込み始めてしまいますよね。


そこで、コミットがあったときに読み込んだ個数を保存するようにします


方法: 

まず、ItemReaderにItemStreamをimplementsします。 

ItemStreamはStepをopen/close/commitするときに呼び出されるメソッドを用意しています。

implementsしたItemReader、ItemWriterなどは自動でそれらのメソッドが呼ばれます。


例: commitしたときにread数を保存する


public void update(ExecutionContext executionContext)
throws ItemStreamException

{

  executionContext.putInt("sendMailCnt", this.sendedCount);

}


上記のような感じになります。




【Stepが開始したときにデータを読み飛ばす方法】

さてどこまで読み込んだかを保存できたところで次に、restartしたときにデータを読み飛ばす方法を見てみます。

実はこの答えの半分は、ItemStreamにあります。

ItemStreamのopenメソッドに読み飛ばしの処理を実装すればOKです。

例えば以下のような具合です。


例:

public void open(ExecutionContext executionContext) throws ItemStreamException

{

  int itemCount = executionContext.getInt("sendMailCnt", 0);

  this.sendedCount = itemCount;

  for (int i = 0; i < itemCount; ++i)

   readData(); //データ1個を読むだけのメソッドを作っておく

}




これでなんとなくやり方わかりましたでしょうか?

open/updateはSpringBatchの方で自動で呼んでくれるので、呼び出すための処理は記述する必要はありませんビックリマーク

ItemWriterにもそのときそのときの状態を持たなければrestartできない場合は、ItemReaderと同様にItemStreamをimplementsして同様のことをしてください。


それ程難しくないですよね。


せっかくなので自作のクラスについてもrestart可能にしましょう!


もしそれがかなわない場合は、設定ファイルのjobタグの属性にrestartable="false" を追加しておきましょう!





参照:

・トップ
・SpringBatch機能について

・WEB上で定期的に何か処理をするには?

・実際に作成するものとは(Stepの概念について)

・Spring Batchを使えるようにするには? (準備編)

・Spring Batchを起動するには? (基本編)

・Stepを使って処理を書くには?

・複数のStepをひとまとまりで扱うには?(flow)

・条件分岐をさせるには?

・既に用意されている機能(クラス)を探すには?

・DBにデータを読み書きするには?

・データの書き込み(コミット)タイミングを制御するには? (restart機能もついでに見る!)

・バッチ処理を非同期で起動するには?

・ItemWriterの出力先をメールにするには?

・CommandLineJobRunnerとは?

・Executionとは? (SpringBatch用語)