You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过实体字段动态生成文件名并批量追加写入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 改为 resource
  • fileleWriter 改为 fileWriter
  • build 补充括号改为 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 18:45:05