Springバッチでは、ItemReaderが読み取ったアイテムを2つの異なるプロセッサーとライターに渡す必要があります。私が達成しようとしているのは...
+ ---> ItemProcessor#1 ---> ItemWriter#1 | ItemReader ---> item --- + | + ---> ItemProcessor#2 ---> ItemWriter#2
これは、ItemWriter#1によって書き込まれたアイテムは、ItemWriter#2によって書き込まれたものとはまったく異なる方法で処理される必要があるために必要です。さらに、ItemReaderはデータベースからアイテムを読み取り、それが実行するクエリは非常に計算コストが高いため、同じクエリを2回実行すると破棄されます。
そのようなセットアップを達成する方法についてのヒントはありますか?または、少なくとも、論理的に同等の設定ですか?
このソリューションは、アイテムがプロセッサー#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.result2
( PropertyExtractingDelegatingItemWriter を参照してください、おそらく助けることができます)
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つのステップでそれらを保存できます。
CompositeItemProcessor
とCompositeItemWriter
を使用できます
それはあなたのスキーマと正確に同じようには見えません、それは順次的ですが、それは仕事をします。
妥当な量のアイテム(1 Go未満など)がある場合は、別の解決策があります。選択の結果をSpring Beanでラップされたコレクションにキャッシュできます。
その後、無料でコレクションを2回読み取ることができます。