我正在尝试将项目写入由“类型 ID”捆绑的文件。该文件将包含多个这样的项目包。共享相同类型 ID 的每个项目包都必须具有相应的页眉和页脚。
我将 Spring Batch 配置为有一个单独Step
的按类型 ID 读取数据库记录(使用 a ,将类型 ID 作为方法参数传递给它)并使用andRepositoryItemReader
将它们连同它们的页眉和页脚一起写入文件。FlatFileHeaderCallback
FlatFileFooterCallback
第一步的FlatFileItemWriter
设置append
为false
,而后续步骤的编写者append
设置为 ,true
因此他们不会覆盖之前写入文件的内容。
我的文件应该是这样的:
Header TYPE ID 001 ::Other Header Info:: <---Written by Step 1, append false
Item TYPE ID 001 ::Other Item Info:: <---Written by Step 1, append false
Item TYPE ID 001 ::Other Item Info:: <---Written by Step 1, append false
Item TYPE ID 001 ::Other Item Info:: <---Written by Step 1, append false
Item TYPE ID 001 ::Other Item Info:: <---Written by Step 1, append false
Item TYPE ID 001 ::Other Item Info:: <---Written by Step 1, append false
Item TYPE ID 001 ::Other Item Info:: <---Written by Step 1, append false
Footer TYPE ID 001 ::Other Footer Info:: <---Written by Step 1, append false
Header TYPE ID 002 ::Other Header Info:: <---Written by Step 2, append true
Item TYPE ID 002 ::Other Item Info:: <---Written by Step 2, append true
Item TYPE ID 002 ::Other Item Info:: <---Written by Step 2, append true
Footer TYPE ID 002 ::Other Footer Info:: <---Written by Step 2, append true
Header TYPE ID 003 ::Other Header Info:: <---Written by Step 3, append true
Item TYPE ID 003 ::Other Item Info:: <---Written by Step 3, append true
Item TYPE ID 003 ::Other Item Info:: <---Written by Step 3, append true
Item TYPE ID 003 ::Other Item Info:: <---Written by Step 3, append true
Item TYPE ID 003 ::Other Item Info:: <---Written by Step 3, append true
Footer TYPE ID 003 ::Other Footer Info:: <---Written by Step 3, append true
问题 1:追加
但是,AbstractFileItemWriter.doOpen()
不会调用我的FlatFileHeaderCallback
whenappend
为真,因此 Header 仅在文件的第 1 行写入第一步,并且文件缺少“中间”标题:
Header TYPE ID 001 ::Other Header Info:: <---Written by Step 1, append false
Item TYPE ID 001 ::Other Item Info:: <---Written by Step 1, append false
Item TYPE ID 001 ::Other Item Info:: <---Written by Step 1, append false
Item TYPE ID 001 ::Other Item Info:: <---Written by Step 1, append false
Item TYPE ID 001 ::Other Item Info:: <---Written by Step 1, append false
Item TYPE ID 001 ::Other Item Info:: <---Written by Step 1, append false
Item TYPE ID 001 ::Other Item Info:: <---Written by Step 1, append false
Footer TYPE ID 001 ::Other Footer Info:: <---Written by Step 1, append false
Item TYPE ID 002 ::Other Item Info:: <---Written by Step 2, append true
Item TYPE ID 002 ::Other Item Info:: <---Written by Step 2, append true
Footer TYPE ID 002 ::Other Footer Info:: <---Written by Step 2, append true
Item TYPE ID 003 ::Other Item Info:: <---Written by Step 3, append true
Item TYPE ID 003 ::Other Item Info:: <---Written by Step 3, append true
Item TYPE ID 003 ::Other Item Info:: <---Written by Step 3, append true
Item TYPE ID 003 ::Other Item Info:: <---Written by Step 3, append true
Footer TYPE ID 003 ::Other Footer Info:: <---Written by Step 3, append true
问题 1 的解决方案
为了解决这个问题,我放弃了FlatFileFooterCallback
并编写了我自己FlatFileItemWriter
的写标题,无论append
但仅当写入的行数为零时。到目前为止,一切都很好。它以我需要的格式生成文件,以及“中间”标题。
public class MyFlatFileItemWriter extends FlatFileItemWriter<MyItem> {
@Override
public String doWrite(List<? extends MyItem> items) {
StringBuilder lines = new StringBuilder();
if (getOutputState().getLinesWritten() == 0) {
lines.append( <<MY HEADER CONTENT>> ))
.append(lineSeparator);
}
Iterator<? extends MyItem> iterator = items.iterator();
while (iterator.hasNext()) {
MyItem item = iterator.next();
lines.append(this.lineAggregator.aggregate(item)).append(this.lineSeparator);
}
return lines.toString();
}
}
问题 2:编写零项
进一步的要求是,即使数据库没有给定类型 ID 的项目,文件也必须具有对应于类型 ID 的页眉和页脚(并且两者之间没有项目行)。它应该如下所示:
Header TYPE ID 001 ::Other Header Info:: <---Written by Step 1, append false
Footer TYPE ID 001 ::Other Footer Info:: <---Written by Step 1, append false
Header TYPE ID 002 ::Other Header Info:: <---Written by Step 2, append true
Footer TYPE ID 002 ::Other Footer Info:: <---Written by Step 2, append true
Header TYPE ID 003 ::Other Header Info:: <---Written by Step 3, append true
Footer TYPE ID 003 ::Other Footer Info:: <---Written by Step 3, append true
但相反,只写入页脚。没有标题的迹象:
Footer TYPE ID 001 ::Other Footer Info:: <---Written by Step 1, append false
Footer TYPE ID 002 ::Other Footer Info:: <---Written by Step 2, append true
Footer TYPE ID 003 ::Other Footer Info:: <---Written by Step 3, append true
我追踪到它的工作方式SimpleChunkProcessor
。如果它收到一个空的项目列表,它不会调用 to MyItemWriter.write()
,所以当我有零个项目时,我的标题不可能被写出。
来自SimpleChunkProcessor
:
public final void process(StepContribution contribution, Chunk<I> inputs) throws Exception {
this.initializeUserData(inputs);
if (!this.isComplete(inputs)) {
Chunk<O> outputs = this.transform(contribution, inputs);
contribution.incrementFilterCount(this.getFilterCount(inputs, outputs));
this.write(contribution, inputs, this.getAdjustedOutputs(inputs, outputs));
}
}
还有来自SimpleChunkProcessor
:
protected boolean isComplete(Chunk<I> inputs) {
return inputs.isEmpty();
}
问题 2 的可能解决方案
但是,SimpleChunkProcessor.isComplete()
是受保护的,所以我将其子类化,覆盖isComplete()
为 return false
:
public class HeaderWritingChunkProcessor extends SimpleChunkProcessor<MyItem, MyItem> {
public HeaderWritingChunkProcessor(ItemWriter<MyItem> itemWriter) {
super(itemWriter);
}
@Override
protected boolean isComplete(Chunk<MyItem> inputs) {
return false;
}
}
但是如何让我HeaderWritingChunkProcessor
进入我的应用程序?在我的步骤配置中或其他地方设置它在哪里?
还是我错过了一个更简单的解决方案来实现我的两个核心要求?
- 即使在附加模式下也要写一个标题,而不仅仅是在文件的第 1 行。
- 即使有零个项目要写入,也要写一个标题。
我应该补充一点,页脚总是写在我需要它们的文件中,所以这些没有问题。
谢谢你。