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" を追加しておきましょう!
参照:
・Spring Batchを使えるようにするには? (準備編)
・データの書き込み(コミット)タイミングを制御するには? (restart機能もついでに見る!)