在Spring批处理中,我需要将ItemReader读取的项目传递给两个不同的处理器和编写器。我想要实现的是......
+---> ItemProcessor#1 ---> ItemWriter#1 | ItemReader ---> item ---+ | +---> ItemProcessor#2 ---> ItemWriter#2
这是必需的,因为与ItemWriter#2编写的项目相比,ItemWriter#1编写的项目应该以完全不同的方式处理。 此外,ItemReader从数据库中读取项目,并且它执行的查询计算成本非常高,因此应该丢弃两次执行相同的查询。
有关如何实现此类设置的任何提示?或者,至少,逻辑上等同的设置?
答案 0 :(得分:9)
如果您的项目应由处理器#1和处理器#2
处理,则此解决方案有效您必须使用此签名创建处理器#0:
class Processor0<Item, CompositeResultBean>
其中CompositeResultBean
是定义为
class CompositeResultBean {
Processor1ResultBean result1;
Processor2ResultBean result2;
}
在您的Processor#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,也许可以提供帮助)
答案 1 :(得分:3)
我遵循Luca的建议,使用PropertyExtractingDelegatingItemWriter
作为作家,我能够在一个步骤中与两个不同的实体合作。
首先,我所做的是定义一个存储来自处理器的两个实体/结果的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
类的编写器,在那里我定义了两个自定义方法来将实体写入数据库,请参阅下面的编写器代码:
@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();
}
}
通过这种方法,您可以从单个读取器获取所需的初始行为,以处理多个实体并将其保存在一个步骤中。
答案 2 :(得分:2)
您可以使用CompositeItemProcessor
和CompositeItemWriter
它看起来不像你的架构,它将是顺序的,但它会完成这项工作。
答案 3 :(得分:0)
如果您有合理数量的项目(例如少于1 Go),还有另一种解决方案:您可以将select的结果缓存到包含在Spring bean中的集合中。
然后你可以免费阅读该集合两次。
答案 4 :(得分:-1)
这是我提出的解决方案。
因此,我们的想法是编写一个“包含”ItemProcessor和ItemWriter的新Writer。为了给你一个想法,我们称它为PreprocessoWriter,这就是核心代码。
private ItemWriter<O> writer;
private ItemProcessor<I, O> processor;
@Override
public void write(List<? extends I> items) throws Exception {
List<O> toWrite = new ArrayList<O>();
for (I item : items) {
toWrite.add(processor.process(item));
}
writer.write(toWrite);
}
有很多事情被抛在一边。例如,管理ItemStream。但在我们的特殊情况下,这已经足够了。
因此,您可以将多个PreprocessorWriter与CompositeWriter结合使用。