web-dev-qa-db-ja.com

Spring Batch:1つのリーダー、複数のプロセッサーおよびライター

Springバッチでは、ItemReaderが読み取ったアイテムを2つの異なるプロセッサーとライターに渡す必要があります。私が達成しようとしているのは...

 + ---> ItemProcessor#1 ---> ItemWriter#1 
 | 
 ItemReader ---> item --- + 
 | 
 + ---> ItemProcessor#2 ---> ItemWriter#2 

これは、ItemWriter#1によって書き込まれたアイテムは、ItemWriter#2によって書き込まれたものとはまったく異なる方法で処理される必要があるために必要です。さらに、ItemReaderはデータベースからアイテムを読み取り、それが実行するクエリは非常に計算コストが高いため、同じクエリを2回実行すると破棄されます。

そのようなセットアップを達成する方法についてのヒントはありますか?または、少なくとも、論理的に同等の設定ですか?

21
danidemi

このソリューションは、アイテムがプロセッサー#1とプロセッサー#2で処理される場合に有効です。

このシグネチャでプロセッサ#0を作成する必要があります。

class Processor0<Item, CompositeResultBean>

ここで、CompositeResultBeanは、次のように定義されたBeanです。

class CompositeResultBean {
  Processor1ResultBean result1;
  Processor2ResultBean result2;
}

プロセッサー#0で、プロセッサー#1および#2に作業を委任し、結果をCompositeResultBeanに入れます。

CompositeResultBean Processor0.process(Item item) {
  final CompositeResultBean r = new CompositeResultBean();
  r.setResult1(processor1.process(item));
  r.setResult2(processor2.process(item));
  return r;
}

あなた自身のライターは、ライターに委任するCompositeItemWriterですCompositeResultBean.result1またはCompositeResultBean.result2PropertyExtractingDelegatingItemWriter を参照してください、おそらく助けることができます)

11

PropertyExtractingDelegatingItemWriterをライターとして使用するというLucaの提案に従い、1つのステップで2つの異なるエンティティを操作することができました。

まず最初に、プロセッサからの2つのエンティティ/結果を保存するDTOを定義しました

public class DatabaseEntry {
    private AccessLogEntry accessLogEntry;
    private BlockedIp blockedIp;

    public AccessLogEntry getAccessLogEntry() {
        return accessLogEntry;
    }

    public void setAccessLogEntry(AccessLogEntry accessLogEntry) {
        this.accessLogEntry = accessLogEntry;
    }

    public BlockedIp getBlockedIp() {
        return blockedIp;
    }

    public void setBlockedIp(BlockedIp blockedIp) {
        this.blockedIp = blockedIp;
    }
}

次に、このDTOをPropertyExtractingDelegatingItemWriterクラスのライターに渡しました。ここでは、エンティティをデータベースに書き込むための2つのカスタマイズされたメソッドを定義しています。以下のライターコードを参照してください。

@Configuration
public class LogWriter extends LogAbstract {
    @Autowired
    private DataSource dataSource;

    @Bean()
    public PropertyExtractingDelegatingItemWriter<DatabaseEntry> itemWriterAccessLogEntry() {
        PropertyExtractingDelegatingItemWriter<DatabaseEntry> propertyExtractingDelegatingItemWriter = new PropertyExtractingDelegatingItemWriter<DatabaseEntry>();
        propertyExtractingDelegatingItemWriter.setFieldsUsedAsTargetMethodArguments(new String[]{"accessLogEntry", "blockedIp"});
        propertyExtractingDelegatingItemWriter.setTargetObject(this);
        propertyExtractingDelegatingItemWriter.setTargetMethod("saveTransaction");
        return propertyExtractingDelegatingItemWriter;
    }

    public void saveTransaction(AccessLogEntry accessLogEntry, BlockedIp blockedIp) throws SQLException {
        writeAccessLogTable(accessLogEntry);
        if (blockedIp != null) {
            writeBlockedIp(blockedIp);
        }

    }

    private void writeBlockedIp(BlockedIp entry) throws SQLException {
        PreparedStatement statement = dataSource.getConnection().prepareStatement("INSERT INTO blocked_ips (ip,threshold,startDate,endDate,comment) VALUES (?,?,?,?,?)");
        statement.setString(1, entry.getIp());
        statement.setInt(2, threshold);
        statement.setTimestamp(3, Timestamp.valueOf(startDate));
        statement.setTimestamp(4, Timestamp.valueOf(endDate));
        statement.setString(5, entry.getComment());
        statement.execute();
    }

    private void writeAccessLogTable(AccessLogEntry entry) throws SQLException {
        PreparedStatement statement = dataSource.getConnection().prepareStatement("INSERT INTO log_entries (date,ip,request,status,userAgent) VALUES (?,?,?,?,?)");
        statement.setTimestamp(1, Timestamp.valueOf(entry.getDate()));
        statement.setString(2, entry.getIp());
        statement.setString(3, entry.getRequest());
        statement.setString(4, entry.getStatus());
        statement.setString(5, entry.getUserAgent());
        statement.execute();
    }
}

このアプローチを使用すると、複数のエンティティを処理するための単一のリーダーから必要な初期動作を取得し、1つのステップでそれらを保存できます。

3
Juan Pablo G

CompositeItemProcessorCompositeItemWriterを使用できます

それはあなたのスキーマと正確に同じようには見えません、それは順次的ですが、それは仕事をします。

2

妥当な量のアイテム(1 Go未満など)がある場合は、別の解決策があります。選択の結果をSpring Beanでラップされたコレクションにキャッシュできます。

その後、無料でコレクションを2回読み取ることができます。

0
Tristan