Spring Batch: запись данных в несколько файлов с динамическим именем файла
У меня есть требование получить данные из базы данных и записать эти данные в файлы на основе имени файла, указанного в базе данных.
Вот как данные определяются в базе данных:
Columns --> FILE_NAME, REC_ID, NAME
Data --> file_1.csv, 1, ABC
Data --> file_1.csv, 2, BCD
Data --> file_1.csv, 3, DEF
Data --> file_2.csv, 4, FGH
Data --> file_2.csv, 5, DEF
Data --> file_3.csv, 6, FGH
Data --> file_3.csv, 7, DEF
Data --> file_4.csv, 8, FGH
Как вы видите, в основном имена файлов вместе с данными определены в базе данных, поэтому SpringBatch должен получить эти данные и записать их в соответствующий файл, указанный в базе данных (т. Е. file_1.csv
должно содержать только 3 записи (1,2,3), file_2.csv
должны содержать только записи 4 и 5 и т. д.)
Можно ли использовать MultiResourceItemWriter
для этого требования (обратите внимание, что полное имя файла является динамическим и должно быть получено из базы данных).
2 ответа
Я не уверен, но я не думаю, что есть простой способ получить это. Вы можете попробовать создать свой собственный ItemWriter следующим образом:
public class DynamicItemWriter implements ItemStream, ItemWriter<YourEntry> {
private Map<String, FlatFileItemWriter<YourEntry>> writers = new HashMap<>();
private LineAggregator<YourEntry> lineAggregator;
private ExecutionContext executionContext;
@Override
public void open(ExecutionContext executionContext) throws ItemStreamException {
this.executionContext = executionContext;
}
@Override
public void update(ExecutionContext executionContext) throws ItemStreamException {
}
@Override
public void close() throws ItemStreamException {
for(FlatFileItemWriter f:writers.values()){
f.close();
}
}
@Override
public void write(List<? extends YourEntry> items) throws Exception {
for (YourEntry item : items) {
FlatFileItemWriter<YourEntry> ffiw = getFlatFileItemWriter(item);
ffiw.write(Arrays.asList(item));
}
}
public LineAggregator<YourEntry> getLineAggregator() {
return lineAggregator;
}
public void setLineAggregator(LineAggregator<YourEntry> lineAggregator) {
this.lineAggregator = lineAggregator;
}
public FlatFileItemWriter<YourEntry> getFlatFileItemWriter(YourEntry item) {
String key = item.FileName();
FlatFileItemWriter<YourEntry> rr = writers.get(key);
if(rr == null){
rr = new FlatFileItemWriter<>();
rr.setLineAggregator(lineAggregator);
try {
UrlResource resource = new UrlResource("file:"+key);
rr.setResource(resource);
rr.open(executionContext);
} catch (MalformedURLException e) {
e.printStackTrace();
}
writers.put(key, rr);
//rr.afterPropertiesSet();
}
return rr;
}
}
и настройте его как писателя:
<bean id="csvWriter" class="com....DynamicItemWriter">
<property name="lineAggregator">
<bean
class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="delimiter" value=","/>
<property name="fieldExtractor" ref="csvFieldExtractor"/>
</bean>
</property>
В Spring-Batch вы можете сделать это с помощью ClassifierCompositeItemWriter.
поскольку ClassifierCompositeItemWriter
дает вам доступ к вашему объекту во время записи, вы можете написать собственную логику, чтобы инструктировать Spring писать в разные файлы.
Взгляните на образец ниже. ClassifierCompositeItemWriter
нуждается в реализации Classifier
интерфейс. Ниже вы можете видеть, что я создал лямбду, где я реализую classify()
метод Classifier
интерфейс. classify()
метод, где вы будете создавать свои ItemWriter
, В нашем примере ниже мы создали FlatFileItemWriter
который получает имя файла из item
сам, а затем создает ресурс для этого.
@Bean
public ClassifierCompositeItemWriter<YourDataObject> yourDataObjectItemWriter(
Classifier<YourDataObject, ItemWriter<? super YourDataObject>> itemWriterClassifier
) {
ClassifierCompositeItemWriter<YourDataObject> compositeItemWriter = new ClassifierCompositeItemWriter<>();
compositeItemWriter.setClassifier(itemWriterClassifier);
return compositeItemWriter;
}
@Bean
public Classifier<YourDataObject, ItemWriter<? super YourDataObject>> itemWriterClassifier() {
return yourDataObject -> {
String fileName = yourDataObject.getFileName();
BeanWrapperFieldExtractor<YourDataObject> fieldExtractor = new BeanWrapperFieldExtractor<>();
fieldExtractor.setNames(new String[]{"recId", "name"});
DelimitedLineAggregator<YourDataObject> lineAggregator = new DelimitedLineAggregator<>();
lineAggregator.setFieldExtractor(fieldExtractor);
FlatFileItemWriter<YourDataObject> itemWriter = new FlatFileItemWriter<>();
itemWriter.setResource(new FileSystemResource(fileName));
itemWriter.setAppendAllowed(true);
itemWriter.setLineAggregator(lineAggregator);
itemWriter.setHeaderCallback(writer -> writer.write("REC_ID,NAME"));
itemWriter.open(new ExecutionContext());
return itemWriter;
};
}
Наконец, вы можете прикрепить ClassifierCompositeItemWriter
на шаге партии, как вы обычно прикрепляете свой ItemWriter.
@Bean
public Step myCustomStep(
StepBuilderFactory stepBuilderFactory
) {
return stepBuilderFactory.get("myCustomStep")
.<?, ?>chunk(1000)
.reader(myCustomReader())
.writer(yourDataObjectItemWriter(itemWriterClassifier(null)))
.build();
}