04.Spring Batchの基本概念
(ExecutionContext)
概要
Spring BatchのExecution Contextについてみていきます。
Spring Batchが提供するExecutionContext は2つあります。
・JobのExecutionContext (以下JobExecutionContext)
・StepのExecutionContext (以下StepExecutionContext)
上記の2つは同じ機能ですが、適用範囲が違います。
これらを以下で見ていくことにしましょう。
ExecutionContextとは
ExectionContextとはJobに使用するデータを保管しておく保管庫です。
Java上はExecutionContextというクラスで表現されています。
JobやStepの終了時や、チャンクのコミット時など、決められたタイミングでDBに保存されます。
ですので、リスタート時でも前回の保存データを読み取ることもできます。
さて、先にあげた2つのExecutionContextについて簡単に説明しておきます。
<JobExecutionContext>
Jobの処理を通して、いつでも参照できるデータを保存しています。
追加・更新もできます。
通常、ステップ処理の結果(値など)は次のステップに渡すことができません。
しかし、JobExecutionContextに保管しておけば、どのステップからも参照することができます。
<StepExectutionContext>
Step内の処理であれば、データを参照・追加・更新が自由にできます。
JobExectuionContextとの違いは、他のステップからは参照できない点になります。
主な利用ケースは、リスタートのための情報を保管することです。
チャンク処理などで、途中でエラー終了した場合、何個までデータを読み込んだかをStepExectuionContextに保管しておくことで
次回のリスタート処理で、失敗した箇所から処理を開始できます。
SpringBatchで用意しているチャンク関連のクラス(ItemReaderクラス)は、既にこの仕組みが入っているので
リスタートが可能になっています。
次に、具体的に使い方を見ていこうと思います。
使い方
ここでは、StepExecutionListenerのメソッドの1つを例に見てみましょう。
呼び出し元からStepExecution が渡ってきますので、このクラスからExectutionContextを取り出し、値を登録します。
【値の登録の仕方】
@Override
public void beforeStep(StepExecution se) {
this.stepExecution = se;
ExecutionContext context = se.getExecutionContext();
context.put("count", 12);
}
StepExecutionContextは、StepExecutionから取得します。
同様に、JobExecutionContextは、JobExectuionから取得します。
取得後の使い方は全く同じです。
put(キー名、値)で値を登録、更新します。
【値の取得の仕方】
//型を明示的に指定
int count = context.getInt("count", 0);
//型を指定しない
count = (Integer)context.get("count");
型を明示的に指定する方法は、1番目の引数がキー名、2番目の引数はキーが見つからなかったときのデフォルト値です。
getInt()以外にも、getDouble(), getString()など、よく使用するものは用意されています。
型を指定しない方法は、自分で型変換する必要があります。
通常は、明示的に指定する方法で行います。
型を指定しない方法は、自作のPOJOを保管した場合に使用します。
JobのExectuionContextもStepのExectuionContextもクラス型は同じですので、使い方も同じです。
ExecutionContextの仕組み
ExecutionContextには、POJOであればほぼ何でも保管できるかと思います。
Java上であれば、何でも保管できることはそれほど不思議ではありません。
しかし、それをDBに保存するとなると少し不思議に思いませんせんか?
そのあたりを軽く見て行きます。
仕組みは知っている人ならば難しくありません。
Xstreamフレームワークを使用して、JSON形式に変換(シリアライズ)し、JSON文字列をDBに保管しています。
もし、既にDBを使用してSpringBatchを起動している人は、batch_step_execution_contextテーブルを見てみるといいでしょう。
【変換後のJSON文字列のイメージ】
{"map":
{"entry":[
{"string":"FlatFileItemWriter.current.count","long":106},
{"string":"FlatFileItemWriter.written","long":5},
{"string":"FlatFileItemReader.read.count","int":6}
]}
}
DBから取り出すときには逆にJSONからJavaのオブジェクトに変換します。
注意点
上記の仕組みを読んでいただけたでしょうか?
そうすると大体の注意点が分かるかと思います。
軽く箇条書きしてみましょう!
・JSONを保管するカラムは2500バイト程なので、それを超えるようなデータは保管できない。
・XstreamでJSONに変換できないJavaオブジェクトは保管できない。
ですので、やたらめったらExecutionContextにデータを保管するのはやめましょう。
特にJobExecutionContextは、ステップ処理の結果などを他のステップと共有する唯一の手段です。
そのため、何も考えずにJobExecutionContextに莫大なデータを保管しがちです。
しかし上記の理由からそれは行ってはいけません。
そもそもまずは、ステップ内で処理が完結し、次のステップに結果を渡さなくても良いような構成にすべきです。
例えば、10個の処理があって、それを1つのステップで行うか?それとも10個のステップに分けるか?という問題を検討しなおすべきでしょう。
最後に
ExectuionContextは、StepExecutionがあれば取得できます。
StepExecutionはたいていの場合、リスナーから取得します。
JobExecutionは、stepExecution.getJobExecution()で取得できますので、結局StepExecutionがあればすべて手に入ります。
これらのメソッドを利用してうまく処理をしていきましょう。
ただし、使い方にはお気をつけください。
Created Date: 2010/09/05