web-dev-qa-db-ja.com

SpringBatchのItemProcessorから複数のアイテムを返す

私はSpringBatchジョブを書いていますが、私のステップの1つに、プロセッサ用の次のコードがあります。

@Component
public class SubscriberProcessor implements ItemProcessor<NewsletterSubscriber, Account>, InitializingBean {

    @Autowired
    private AccountService service;

    @Override public Account process(NewsletterSubscriber item) throws Exception {
        if (!Strings.isNullOrEmpty(item.getId())) {
            return service.getAccount(item.getId());
        }
        // search with email address
        List<Account> accounts = service.findByEmail(item.getEmail());
        checkState(accounts.size() <= 1, "Found more than one account with email %s", item.getEmail());
        return accounts.isEmpty() ? null : accounts.get(0);
    }

    @Override public void afterPropertiesSet() throws Exception {
        Assert.notNull(service, "account service must be set");
    }
}

上記のコードは機能しますが、Accountごとに複数のNewsletterSubscriberを持つことが許可されるEdgeのケースがいくつかあることがわかりました。したがって、状態チェックを削除し、複数のAccountをアイテムライターに渡す必要があります。

私が見つけた解決策の1つは、ItemProcessorItemWriterの両方を変更してAccountではなくList<Account>タイプを処理することですが、これには2つの欠点があります。

  • ライターにネストされたリストがあるため、コードとテストは醜く、作成と保守が困難です。
  • 最も重要なのは、ライターに与えられたリストに複数のアカウントが含まれている可能性があるため、同じトランザクションで複数のAccountオブジェクトが書き込まれる可能性があるためです。これは避けたいと思います。

リスナーを使用する方法や、プロセッサのリストを回避するためにSpring Batchで使用される内部コンポーネントを置き換える方法はありますか?

更新

この問題のために 春のJiraの問題 を開きました。

isComplete および getAdjustedOutputsFaultTolerantChunkProcessorのメソッドを調べています。これらは、 SimpleChunkProcessor で拡張ポイントとしてマークされています。目標を達成するために何らかの方法でそれらを使用できるかどうかを確認します。

ヒントは大歓迎です。

10
Fabio

アイテムプロセッサは1つのものを取り込んで、リストを返します

MyItemProcessor implements ItemProcessor<SingleThing,List<ExtractedThingFromSingleThing>> {
    public List<ExtractedThingFromSingleThing> process(SingleThing thing) {
    //parse and convert to list
    }
}

ダウンストリームライターをラップして問題を解決します。このようにすると、このライターのダウンストリームのものはリストを操作する必要がありません。

@StepScope
public class ItemListWriter<T> implements ItemWriter<List<T>> {
    private ItemWriter<T> wrapped;

    public ItemListWriter(ItemWriter<T> wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public void write(List<? extends List<T>> items) throws Exception {
        for (List<T> subList : items) {
            wrapped.write(subList);
        }
    }
}
9
Matt Broekhuis

雑草にかなり深く入り込むことなく、SpringBatchのItemProcessorへの呼び出しごとに複数のアイテムを返す方法はありません。 ItemProcessorItemWriterの関係がどこにあるのかを本当に知りたい場合は(非推奨)、ChunkProcessorインターフェースの実装を見てください。単純なケース(SimpleChunkProcessor)はそれほど悪くはありませんが、フォールトトレラントロジック(FaultTolerantChunkProcessorを介したスキップ/再試行)のいずれかを使用すると、非常に手に負えないほど速くなります。

はるかに簡単なオプションは、このロジックをItemReaderに移動して、アイテムを返す前にこのエンリッチメントを実行することです。アイテムを返す前にサービスルックアップを実行するカスタムItemReader実装で使用しているItemReaderをラップします。この場合、リーダーからNewsletterSubscriberを返す代わりに、前の情報に基づいてAccountを返します。

3
Michael Minella

アカウントを返す代わりに、AccountWrapperまたはCollectionを作成します。ライターは明らかにこれを考慮に入れる必要があります:)

1
Esben