如何通过实体字段动态生成文件名并批量追加写入Person实体数据
问题描述
接收一批1000个Person实体,希望根据实体字段(如country)及计数动态生成文件名,并向对应文件中追加数据。现有代码如下:
private static final Date now = new Date(); private String[] headerFields = {"name","country"}; @Bean @StepScope public FlatFileItemWriter<Person> test(){ return new FlatFileItemWriterBuilder<Person>() .name("generatePersonWriter") .headerCallback(fileWriter -> fileWriter.write("name,country")) .resource(new FileSystemResource("output/person_{{country}}_10.csv")) .append(true) .delimited() .names(headerFields) .build(); }
预期生成的文件名示例:output/person_France_10.csv
实现方案
要实现按实体字段动态生成文件名并追加数据,需解决按字段路由到对应文件、按计数控制文件分片两个核心问题,具体实现如下:
1. 修正基础代码错误
先修复原代码中的拼写与语法问题:
rsourve改为resourcefileleWriter改为fileWriterbuild补充括号改为build()headerFields补充赋值符号=
2. 按实体字段分组写入动态文件
使用Spring Batch的ClassifierCompositeItemWriter,根据country字段将实体路由到对应文件,同时实现动态创建/追加逻辑:
自定义文件资源生成方法
根据实体字段和计数生成目标文件路径:
private FileSystemResource getTargetResource(Person person, int totalCount) { // 每10条数据生成一个文件,示例中的_10对应分片大小 int fileNumber = totalCount / 10 + 1; String fileName = String.format("output/person_%s_%d.csv", person.getCountry(), fileNumber); return new FileSystemResource(fileName); }
实现动态路由Writer
@Bean @StepScope public ClassifierCompositeItemWriter<Person> dynamicPersonWriter() { ClassifierCompositeItemWriter<Person> compositeWriter = new ClassifierCompositeItemWriter<>(); compositeWriter.setClassifier(new Classifier<Person, ItemWriter<? super Person>>() { // 缓存已创建的Writer,避免重复初始化 private final Map<String, FlatFileItemWriter<Person>> writerCache = new HashMap<>(); private int totalRecordCount = 0; @Override public ItemWriter<? super Person> classify(Person person) { totalRecordCount++; String country = person.getCountry(); int fileNumber = totalRecordCount / 10 + 1; String cacheKey = country + "_" + fileNumber; // 缓存中没有对应Writer时,动态创建并初始化 if (!writerCache.containsKey(cacheKey)) { FlatFileItemWriter<Person> itemWriter = new FlatFileItemWriterBuilder<Person>() .name("personWriter_" + cacheKey) .headerCallback(writer -> writer.write(String.join(",", headerFields))) .resource(getTargetResource(person, totalRecordCount)) .append(true) .delimited() .names(headerFields) .build(); try { itemWriter.open(new ExecutionContext()); } catch (Exception e) { throw new RuntimeException("初始化Writer失败:" + cacheKey, e); } writerCache.put(cacheKey, itemWriter); } return writerCache.get(cacheKey); } }); return compositeWriter; }
3. 计数逻辑说明
示例中文件名的数字代表分片序号(每10条数据一个文件),通过totalRecordCount / 10 + 1计算当前文件序号,确保达到指定条数时切换到新文件。如果需要按总计数(如第10个文件),可直接调整计数逻辑。
4. 关键注意事项
- Writer缓存:用HashMap缓存已创建的Writer,避免重复初始化,提升处理效率。
- 追加模式:设置
.append(true),确保文件存在时直接追加数据,而非覆盖。 - StepScope注解:必须标注
@StepScope,保证Writer在Step级别初始化,支持动态参数传递。
内容的提问来源于stack exchange,提问作者Henrique Vital
相关产品推荐
相关产品推荐

