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

Spring Batch多文件写入疑问:如何基于CommonDTO生成3个.dat文件?

解决方案:Spring Batch 多文件拆分写入设计

针对你的需求,核心问题在于直接用FlatFileItemWriter<List<Child1DTO>>不符合Spring Batch的Writer设计规范——FlatFileItemWriter的泛型参数是单个待写入的Item类型(比如Child1DTO),而非集合。如果强行传入List,它会把整个集合当作单个Item处理,默认的行聚合器无法解析集合结构,导致文件生成异常。

下面给出两种可行的实现方案,你可以根据场景选择:


方案一:自定义ItemWriter(推荐,灵活可控)

直接编写一个ItemWriter<CommonDTO>,内部封装三个独立的FlatFileItemWriter,分别处理Parent和两个Child数据集的写入逻辑,同时自动维护Parent与Child的ID关联。

1. 定义基础FlatFileItemWriter

先为每个数据类型创建对应的文件Writer,负责单行数据的格式化写入:

// ParentDTO 对应的Writer
@Bean
public FlatFileItemWriter<ParentDTO> parentFileWriter() {
    return new FlatFileItemWriterBuilder<ParentDTO>()
            .name("parent-dat-writer")
            .resource(new FileSystemResource("parent.dat"))
            .lineAggregator(new DelimitedLineAggregator<ParentDTO>() {{
                setDelimiter("|"); // 按业务需求设置分隔符
                setFieldExtractor(new BeanWrapperFieldExtractor<ParentDTO>() {{
                    setNames(new String[]{"id", "name", "createTime"}); // 替换为ParentDTO的实际字段
                }});
            }})
            .append(true) // 批量处理时追加写入,避免覆盖
            .build();
}

// Child1DTO 对应的Writer
@Bean
public FlatFileItemWriter<Child1DTO> child1FileWriter() {
    return new FlatFileItemWriterBuilder<Child1DTO>()
            .name("child1-dat-writer")
            .resource(new FileSystemResource("child1.dat"))
            .lineAggregator(new DelimitedLineAggregator<Child1DTO>() {{
                setDelimiter("|");
                setFieldExtractor(new BeanWrapperFieldExtractor<Child1DTO>() {{
                    setNames(new String[]{"parentId", "childId", "childData"}); // 必须包含parentId关联字段
                }});
            }})
            .append(true)
            .build();
}

// Child2DTO 对应的Writer(与Child1逻辑一致,替换字段即可)
@Bean
public FlatFileItemWriter<Child2DTO> child2FileWriter() {
    // ... 省略重复代码,参考child1FileWriter实现
}

2. 编写CommonDTO处理的自定义Writer

注入三个基础Writer,遍历每个CommonDTO,拆分数据并调用对应Writer写入:

@Component
public class CommonDTOItemWriter implements ItemWriter<CommonDTO> {

    private final FlatFileItemWriter<ParentDTO> parentWriter;
    private final FlatFileItemWriter<Child1DTO> child1Writer;
    private final FlatFileItemWriter<Child2DTO> child2Writer;

    // 构造注入三个基础Writer
    public CommonDTOItemWriter(FlatFileItemWriter<ParentDTO> parentWriter,
                               FlatFileItemWriter<Child1DTO> child1Writer,
                               FlatFileItemWriter<Child2DTO> child2Writer) {
        this.parentWriter = parentWriter;
        this.child1Writer = child1Writer;
        this.child2Writer = child2Writer;
    }

    @Override
    public void write(List<? extends CommonDTO> items) throws Exception {
        for (CommonDTO dto : items) {
            ParentDTO parent = dto.getParentDTO();
            // 写入Parent数据
            parentWriter.write(Collections.singletonList(parent));
            
            // 写入Child1数据,自动设置parentId关联
            List<Child1DTO> child1List = dto.getChild1DTOList();
            if (child1List != null && !child1List.isEmpty()) {
                child1List.forEach(child -> child.setParentId(parent.getId()));
                child1Writer.write(child1List);
            }
            
            // 写入Child2数据,同理
            List<Child2DTO> child2List = dto.getChild2DTOList();
            if (child2List != null && !child2List.isEmpty()) {
                child2List.forEach(child -> child.setParentId(parent.getId()));
                child2Writer.write(child2List);
            }
        }
    }
}

3. 配置Step

将自定义Writer绑定到Step中即可:

@Bean
public Step dataProcessStep(ItemReader<Parent> parentReader,
                            ItemProcessor<Parent, CommonDTO> commonDTOProcessor,
                            CommonDTOItemWriter commonDTOWriter,
                            JobRepository jobRepository,
                            PlatformTransactionManager transactionManager) {
    return new StepBuilder("data-process-step", jobRepository)
            .<Parent, CommonDTO>chunk(100, transactionManager) // 按业务设置Chunk大小
            .reader(parentReader)
            .processor(commonDTOProcessor)
            .writer(commonDTOWriter)
            .build();
}

方案二:使用CompositeItemWriter+数据提取Processor

如果希望复用Spring Batch的组件组合能力,可以用CompositeItemWriter配合三个ItemProcessor,每个Processor从CommonDTO中提取对应的数据,再交给各自的Writer处理。

核心要点:

  • 每个Processor负责从CommonDTO中提取单个类型的数据(比如ParentDTO或单个Child1DTO)
  • 注意Child数据需要先设置parentId再传递给Writer
  • CompositeItemWriter会将同一个CommonDTO传给所有子Writer,通过Processor过滤出对应数据

示例代码(仅展示关键部分):

// Parent数据提取Processor
public class ParentExtractProcessor implements ItemProcessor<CommonDTO, ParentDTO> {
    @Override
    public ParentDTO process(CommonDTO item) {
        return item.getParentDTO();
    }
}

// Child1数据提取Processor(返回Iterable类型,适配Writer的批量写入)
public class Child1ExtractProcessor implements ItemProcessor<CommonDTO, Iterable<Child1DTO>> {
    @Override
    public Iterable<Child1DTO> process(CommonDTO item) {
        item.getChild1DTOList().forEach(child -> child.setParentId(item.getParentDTO().getId()));
        return item.getChild1DTOList();
    }
}

// 自定义委托类,绑定Processor和Writer
public class ItemWriterDelegate<I, O> implements ItemWriter<I> {
    private final FlatFileItemWriter<O> delegate;
    private final ItemProcessor<I, O> processor;

    public ItemWriterDelegate(FlatFileItemWriter<O> delegate, ItemProcessor<I, O> processor) {
        this.delegate = delegate;
        this.processor = processor;
    }

    @Override
    public void write(List<? extends I> items) throws Exception {
        List<O> processedItems = new ArrayList<>();
        for (I item : items) {
            O processed = processor.process(item);
            if (processed != null) {
                if (processed instanceof Iterable) {
                    ((Iterable<O>) processed).forEach(processedItems::add);
                } else {
                    processedItems.add(processed);
                }
            }
        }
        delegate.write(processedItems);
    }
}

// 配置CompositeItemWriter
@Bean
public CompositeItemWriter<CommonDTO> compositeWriter() {
    List<ItemWriter<? super CommonDTO>> writers = new ArrayList<>();
    writers.add(new ItemWriterDelegate<>(parentFileWriter(), new ParentExtractProcessor()));
    writers.add(new ItemWriterDelegate<>(child1FileWriter(), new Child1ExtractProcessor()));
    writers.add(new ItemWriterDelegate<>(child2FileWriter(), new Child2ExtractProcessor()));

    CompositeItemWriter<CommonDTO> compositeWriter = new CompositeItemWriter<>();
    compositeWriter.setDelegates(writers);
    return compositeWriter;
}

关键注意事项

  1. 事务一致性:如果需要保证三个文件的写入原子性(要么都成功,要么都失败),需确保Writer的事务配置正确,Spring Batch的Chunk事务会覆盖整个Writer的写入操作。
  2. 文件覆盖/追加:根据业务场景设置FlatFileItemWriter的append属性,避免批量处理时覆盖已有数据。
  3. 字段映射正确性:确保BeanWrapperFieldExtractor的字段名称与DTO类的属性完全一致,否则会导致数据缺失。

内容的提问来源于stack exchange,提问作者angel princess

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 00:53:16