Весенний Пакет: Как Записать Заголовок Файла При Добавлении Файла И Отсутствии Элементов Для Записи

#spring-batch #spring-batch-tasklet

Вопрос:

Я пытаюсь записать элементы в файл, в комплекте с «идентификатором типа». В файле будет более одного такого набора элементов. Каждый набор элементов с одинаковым идентификатором типа должен иметь соответствующий верхний и нижний колонтитулы.

Я настроил Spring Batch для отдельного Step чтения записей базы данных по идентификатору типа (используя a RepositoryItemReader , передавая ему идентификатор типа в качестве аргумента метода) и записываю их в файл вместе с их верхним и нижним колонтитулами, используя 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 , когда append верно, поэтому заголовок записывается только для первого шага, в строке 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();
    }
}
 

Problem 2: Writing Zero Items

A further requirement is that even if the database has no items for a given type ID, the file must have a header and footer corresponding to the type ID, (and no item lines in between). It should look like this:

 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
 

But instead, only the footers are written. No sign of the headers:

 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
 

I tracked this down to the way SimpleChunkProcessor works. If it receives an empty list of items, it won’t call through to MyItemWriter.write() , so there’s no chance my header is written out when I have zero items.

From 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));
    }
}
 

And also from SimpleChunkProcessor :

 protected boolean isComplete(Chunk<I> inputs) {
    return inputs.isEmpty();
}
 

Possible Solution to Problem 2

However, SimpleChunkProcessor.isComplete() is protected, so I subclassed it, overrode isComplete() to 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;
    }
}
 

But how do I get my HeaderWritingChunkProcessor into my application? Where do I set it in the configuration of my step, or elsewhere?

Or have I missed a simpler solution to achieve my two core requirements?

  1. Write a header even in appending mode, not just on line 1 of the file.
  2. Write a header even if there are zero items to write.

I should add, the footers are always written as I need them in them in the file, so no issue with those.

Thank you.