TL;DR : Spring Batch Job を使用して Spring Batch Job を作成するにはどうすればよいですか? トランザクション境界が問題のようです。これは古典的な質問のようですが、ここでもう一度言います。
次のユース ケースがあります。FTP サーバーをポーリングし、見つかった XML ファイルを blob としてデータベースに保存する必要があります。XML には、外部 Web サービスに送信して応答を保存する必要がある対象のエントリが 0...N 個あります。応答は再試行不可または再試行可能であり、監査目的で各要求とその応答を保存する必要があります。
ドメイン/JPA モデルは次のとおりです。バッチ (XML BLOB を含む) には、0 ~ N の BatchRow オブジェクトが含まれます。BatchRow には、Web サービスに送信されるデータが含まれており、Web サービス呼び出しに関するステータス情報を保持する 1...N 個の BatchRowHistory オブジェクトも含まれています。
Spring Batch を使用してこれを実装するように依頼されました (この統合のケース以降、Spring Integration は別の可能性がありました)。今、私はさまざまなアプローチに苦労してきましたが、このタスクははるかに複雑であり、したがって私見のように難しいと感じています。
タスクを次のジョブに分割しました。
ジョブ 1 :
Step11: ファイルを取得し、BLOB としてデータベースに保存します。
Step12: XML をエントリに分割し、それらのエントリを db に保存します。
Step13 : Job2 を作成し、Step12 で保存した各エントリに対して起動します。エントリのドメイン モデル データベースでジョブ 2 の作成済みフラグをマークします。
ジョブ 2 :
- Step21: エントリごとに Web サービスを呼び出し、結果を db に格納します。リトライとスキップのロジックはここにあります。Job2 タイプでは、手動での再起動などが必要になる可能性があります。
この構造の背後にあるロジックは、Job1 が定期的にスケジュールされて実行されることです (1 分に 1 回程度)。Job2 は、これらのジョブがあり、それらが成功したか、再試行制限に達して失敗した場合に実行されます。ドメインモデルは基本的に結果のみを保存し、Spring Batch はショーの実行を担当します。手動での再起動などは、Spring Batch Admin を介して処理できます (少なくともそう願っています)。また、Job2 には JobParameters マップに BatchRow の ID があるため、Spring Batch Admin で表示できます。
質問 1 :この職務構造は理にかなっていますか? つまり、db の各行に対して新しい Spring バッチ ジョブを作成すると、目的が無効になり、あるレベルで車輪を再発明するように見えますか?
質問 2 : Step13 で Job2 エントリを作成するにはどうすればよいですか?
トランザクションと JobRepository で最初の問題が発生しましたが、次のセットアップでいくつかのジョブを起動することに成功しました。
<batch:step id="Step13" parent="stepParent">
<batch:tasklet>
<batch:transaction-attributes propagation="NEVER"/>
<batch:chunk reader="rowsWithoutJobReader" processor="batchJobCreator" writer="itemWriter"
commit-interval="10" />
</batch:tasklet>
</batch:step>
<bean id="stepParent" class="org.springframework.batch.core.step.item.FaultTolerantStepFactoryBean" abstract="true"/>
commit-interval="10" は、これが現在最大 10 個のジョブを作成できることを意味することに注意してください。batchJobCreator は JobLauncher.run メソッドを呼び出し、問題なく動作しますが、itemWriter は更新された情報で BatchRows をデータベースに書き戻すことができないためです ( boolean jobCreated フラグがオンになっています)。その明白な理由は、トランザクション属性の伝播.NEVER ですが、それがなければ、jobLauncher でジョブを作成できません。
更新がデータベースに渡されないため、同じ BatchRows が再び取得され、ログが次のように乱雑になります。
org.springframework.batch.retry.RetryException: Non-skippable exception in recoverer while processing; nested exception is org.springframework.batch.core.repository.JobExecutionAlreadyRunningException: A job execution for this job is already running: JobInstance: id=1, version=0, JobParameters=[{batchRowId=71}], Job=[foo.bar]
at org.springframework.batch.core.step.item.FaultTolerantChunkProcessor$2.recover(FaultTolerantChunkProcessor.java:278)
at org.springframework.batch.retry.support.RetryTemplate.handleRetryExhausted(RetryTemplate.java:420)
at org.springframework.batch.retry.support.RetryTemplate.doExecute(RetryTemplate.java:289)
at org.springframework.batch.retry.support.RetryTemplate.execute(RetryTemplate.java:187)
at org.springframework.batch.core.step.item.BatchRetryTemplate.execute(BatchRetryTemplate.java:215)
at org.springframework.batch.core.step.item.FaultTolerantChunkProcessor.transform(FaultTolerantChunkProcessor.java:287)
at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:190)
at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:74)
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:386)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:130)
at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:264)
at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:76)
at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:367)
at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:214)
at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:143)
at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:250)
at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:195)
at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:135)
at org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:61)
at org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:60)
at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:144)
at org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:124)
at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:135)
at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:293)
at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:120)
at java.lang.Thread.run(Thread.java:680)
つまり、ジョブは Spring Batch で既に作成されており、Step13 の後の実行でそれらのファイルを再度作成しようとします。Job2/Step21 で jobCreated フラグを true に設定するこの設定を回避することはできますが、それはちょっとぎこちなく、私には間違っているように感じます。
質問 3 : ドメイン オブジェクト駆動型のアプローチを採用しました。かなり精巧な JPQL クエリと JPAItemReaders を使用して、ドメイン テーブルをスキャンする Spring Batch Jobs を用意しました。このアプローチの問題点は、Spring Batch の優れた機能を使用していないことです。履歴と再試行ロジックが問題です。再試行ロジックを JPQL クエリに直接コーディングする必要があります (たとえば、BatchRow に 3 つ以上の BatchRowHistory 要素がある場合、失敗しており、手動で再調査する必要があります)。各 Web サービス呼び出しに対して個別の Spring Batch Job を作成しようとするのではなく、弾丸をかじってこのアプローチを続行する必要がありますか?
必要に応じてソフトウェア情報: Spring Batch 2.1.9、Hibernate 4.1.2、Spring 3.1.2、Java 6。
前もってありがとう、そして長い話でごめんなさい、ティモ
編集 1: 新しいジョブを生成する必要があると思う理由は次のとおりです。
リーダーが null を返している間ループするか、例外がスローされます
取引開始
N 行全体のリーダー - プロセッサ - ライター ループ
バッチ サイズ N のトランザクション終了
失敗した各エントリが問題です。Spring Batch Admin を使用して失敗したジョブを表示できるように、バッチ内の各行に対して手動で再起動可能な実行 (Spring Batch Admin で再起動できるのはジョブだけですよね?) が必要です (行 ID を含むジョブパラメーターを使用)ドメイン db から) などを再起動します。ジョブを生成してドメイン db に履歴を保存せずに、この種の動作を実現するにはどうすればよいですか?