Я читаю многоформатный файл, используя FlatFileItemReader
, сопоставляю каждую строку с соответствующим типом компонента в ItemProcessor
и выполняю обогащение данных. Но когда я пытаюсь записать эти записи в файл с помощью FlatFileItemWriter
, я не могу назначить отдельные BeanWrapperFieldExtractor
для разных типов записей. Как я могу разобраться с этим?
Формат входного файла
1#9999999#00001#2#RecordType1
2#00002#June#Statement#2020#9#RecordType2
3#7777777#RecordType3
Ожидаемый формат выходного файла
1#9999999#00001#2#RecordType1#mobilenumber1
2#00002#June#Statement#2020#9#RecordType2#mobilenumber2
3#7777777#RecordType3#mobilenumber3
ItemProcessor
public class RecordTypeItemProcessor implements ItemProcessor<RecordType, RecordType> {
@Override
public RecordType process(RecordType recordType) throws Exception {
if (recordType instanceof RecordType1) {
RecordType1 recordType1 = (RecordType1) recordType;
//enrichment logic
return recordType1;
} else if (recordType instanceof RecordType2) {
RecordType2 recordType2 = (RecordType2) recordType;
//enrichment logic
return recordType2;
}
else
return null;
}
}```
Вы можете использовать ClassifierCompositeItemProcessor
с ClassifierCompositeItemWriter
для этого. Идея состоит в том, чтобы классифицировать элементы в соответствии с их типом с помощью Classifier
и вызывать соответствующий обработчик/записывающий элемент. Этот подход чище, чем использование нескольких проверок instance of
в процессоре/писателе.
Есть несколько встроенных реализаций Classifier
(думаю, SubclassClassifier
вам подойдет), но при необходимости вы можете создать свои собственные.
Вы можете найти примеры в ClassifierCompositeItemProcessorTests и ClassifierCompositeItemWriterTests.
Решение, предложенное Махмудом, наконец-то сработало :) Однако есть несколько предостережений. ClassifierCompositeItemWriter
предназначен для записи в разные файлы. т. е. если есть 3 разных типа записи, будет 3 разных выходных файла. Но в моем случае использования я ожидаю вывод в одном файле в том же порядке. Итак, я упомянул одно и то же имя файла в каждом бине Writer и добавил `writer.setAppendAllowed(true); Но после этого разные типы записей были объединены вместе, и порядок сортировки изменился. Итак, я уменьшил размер фрагмента с 50 до 3. 3 — это общее количество типов записей. Однако это ударит по производительности. С некоторыми подобными настройками, наконец, я получаю желаемый результат. Вот моя реализация (просто для справки, нужно больше очистки)
Configuration.java (StepBuilderFactory)
...chunk(3).reader(reader).processor(processor()).writer(writer).stream(recordType1FlatFileItemWriter()).stream(recordType2FlatFileItemWriter()).build();
Шаг процессора
@Bean
@StepScope
public ItemProcessor processor() {
ClassifierCompositeItemProcessor<? extends RecordType, ? extends RecordType> processor = new ClassifierCompositeItemProcessor<>();
ItemProcessor<RecordType1, RecordType1> recordType1Processor = new ItemProcessor<RecordType1, RecordType1>() {
@Nullable
@Override
public RecordType1 process(RecordType1 recordType1) throws Exception {
//Processing logic
return recordType1;
}
};
ItemProcessor<RecordType2, RecordType2> recordType2Processor = new ItemProcessor<RecordType2, RecordType2>() {
@Nullable
@Override
public RecordType2 process(RecordType2 recordType2) throws Exception {
//Processing logic
return recordType2;
}
};
SubclassClassifier classifier = new SubclassClassifier();
Map typeMap = new HashMap();
typeMap.put(RecordType1.class, recordType1Processor);
typeMap.put(RecordType2.class, recordType2Processor);
classifier.setTypeMap(typeMap);
processor.setClassifier(classifier);
return processor;
}
Шаг писателя
@Bean
@StepScope
public ClassifierCompositeItemWriter writer(@Value("#{stepExecutionContext[fileName]}") Resource file) throws Exception {
ClassifierCompositeItemWriter<String> writer = new ClassifierCompositeItemWriter<>();
SubclassClassifier classifier = new SubclassClassifier<>();
Map typeMap = new HashMap<>();
typeMap.put(RecordType1.class, recordType1FlatFileItemWriter());
typeMap.put(RecordType2.class, recordType2FlatFileItemWriter());
typeMap.put(RecordType3.class, recordType3FlatFileItemWriter());
classifier.setTypeMap(typeMap);
writer.setClassifier(classifier);
return writer;
}
Писатель
@Bean
public FlatFileItemWriter<RecordType1> recordType1FlatFileItemWriter() throws Exception{
FlatFileItemWriter<RecordType1> writer = new FlatFileItemWriter<>();
writer.setResource( new FileSystemResource(outputFolder + "test.txt"));
writer.setAppendAllowed(true);
writer.setLineAggregator(new DelimitedLineAggregator<RecordType1>() {{
setDelimiter("#");
setFieldExtractor(new BeanWrapperFieldExtractor<RecordType1>() {
{
setNames(new String[] { "RecordType", "ID1", "ID2", "ID3");
}
});
}});
return writer;
}
@Bean
public FlatFileItemWriter<RecordType2> recordType2FlatFileItemWriter() throws Exception{
FlatFileItemWriter<RecordType2> writer = new FlatFileItemWriter<>();
writer.setResource( new FileSystemResource(outputFolder + "test.txt"));
writer.setAppendAllowed(true);
writer.setLineAggregator(new DelimitedLineAggregator<RecordType2>() {{
setDelimiter("#");
setFieldExtractor(new BeanWrapperFieldExtractor<RecordType2>() {
{
setNames(new String[] { "RecordType", "ID9", "ID8",);
}
});
}});
return writer;
}
Привет, Махмуд, приведенное выше решение отлично сработало. Но я не могу динамически передать имя файла в потоке StepBuilderFactory
в FlatFileItemWriter
. В моем сценарии в качестве входных данных будет несколько мультиформатных файлов, и такое же количество файлов будет создано в качестве выходных с тем же именем файла. Есть ли решение для этого?
Вы можете использовать средство записи с пошаговой областью (или пошаговую последовательность задания) и выполнить позднюю привязку имени файла из параметра задания. Взгляните на этот раздел из документации: docs.spring.io/spring-batch/docs/4.3.x/reference/html/…
Опубликую здесь после того, как я реализую рабочее решение, используя этот подход. Спасибо, Махмуд, очень признателен.