乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • 学习路径
  • 第1章 批处理概述与 Spring Batch 核心理念

    • Spring Batch 概述
    • Job-Instance-Execution 三层生命周期
    • 三层架构
    • Chunk 处理模型
    • Tasklet 处理模型
  • 第2章 Job与作业配置

    • Job 详解
    • Job 配置
    • JobLauncher 详解
    • JobParameters 详解
    • JobRepository 详解
  • 第3章 Step与执行模型

    • Step 详解
    • Step 配置
    • ExecutionContext 详解
    • ItemStream 与状态管理
    • StepScope 与 JobScope
  • 第4章 ItemReader数据读取

    • ItemReader 详解
    • FlatFileItemReader 详解
    • JdbcItemReader 详解
    • MultiResourceItemReader 详解
  • 第5章 ItemProcessor 数据处理

    • ItemProcessor 详解
  • 第6章 ItemWriter 数据写出

    • ItemWriter 详解
    • FlatFileItemWriter 详解
    • CompositeItemWriter 详解
    • JdbcBatchItemWriter 详解
  • 第7章 Chunk 处理与事务边界

    • Chunk 处理模型详解
    • 事务边界
  • 第8章 作业参数与启动

    • 命令行与 Web 启动
  • 第9章 监听器与拦截器

    • 监听器详解
  • 第10章 重启重试与跳过策略

    • 重启与重试
    • 跳过策略
  • 第11章 作业流与条件决策

    • 作业流与条件决策
  • 第12章 分区与并行处理

    • 分区详解
    • 并行 Step 与 Split
  • 第13章 与 Spring Boot 集成实践

    • Spring Boot 集成
  • 第15章 运维与监控

    • 大作业设计模式
    • 运维与监控

FlatFileItemWriter 详解

本章定位:深入掌握 FlatFileItemWriter——将批处理结果导出为 CSV/定长文件,包括格式化、头和尾回调、文件管理和资源清理。

定义与作用

FlatFileItemWriter 将 Chunk 中的数据逐行写入平面文件。支持定界符(CSV/TSV)和定长两种格式,通过 LineAggregator 将领域对象转换为文本行。

小崔在导出杨英需要的报表时发现:

"FlatFileItemWriter 不只是写文件——headerCallback 写 CSV 标题行,footerCallback 写统计信息,LineAggregator 控制每行的格式。导出 10 万条数据就像写 1 条一样简单。"

核心原理

完整示例

场景一:CSV 成绩报表导出

@Bean
@StepScope
public FlatFileItemWriter<GradeReport> gradeReportWriter(
        @Value("#{jobParameters['output.file']}") String outputFile) {

    // 自定义 LineAggregator:格式化每行
    DelimitedLineAggregator<GradeReport> aggregator = new DelimitedLineAggregator<>();
    aggregator.setDelimiter(",");
    aggregator.setFieldExtractor(report -> new String[]{
        report.getStudentId(),
        report.getStudentName(),
        report.getCourse(),
        String.format("%.1f", report.getScore()),
        report.getGrade()
    });

    return new FlatFileItemWriterBuilder<GradeReport>()
            .name("gradeReportWriter")
            .resource(new FileSystemResource(outputFile))
            .encoding("UTF-8")
            .lineAggregator(aggregator)
            .headerCallback(writer -> writer.write("学号,姓名,课程,成绩,等级"))
            .footerCallback(writer -> {
                writer.write("# 报表生成时间: " + LocalDateTime.now());
            })
            .shouldDeleteIfExists(true)    // 先删除旧文件
            .transactional(true)           // 参与 Step 事务
            .build();
}

输出文件(grades_report.csv):

学号,姓名,课程,成绩,等级
STU001,张三,数据结构,85.0,B
STU002,李四,操作系统,92.0,A
STU003,王五,计算机网络,78.0,C
# 报表生成时间: 2026-06-13T14:30:00

场景二:定长银行对账文件

@Bean
public FlatFileItemWriter<BankRecord> fixedLengthWriter() {
    FormatterLineAggregator<BankRecord> aggregator = new FormatterLineAggregator<>();
    aggregator.setFormat("%-8s%-6s%-20s%-30s%03d");  // 定长格式
    aggregator.setFieldExtractor(record -> new Object[]{
        record.getDate(),
        record.getStudentId(),
        record.getName(),
        record.getCourse(),
        record.getScore()
    });

    return new FlatFileItemWriterBuilder<BankRecord>()
            .name("bankRecordWriter")
            .resource(new FileSystemResource("bank_output.txt"))
            .lineAggregator(aggregator)
            .build();
}

易错场景与避坑

反例一:重启时不删除旧文件导致追加

// ❌ shouldDeleteIfExists=false 且 restart 时 → 数据追加
// 第一次执行写了 1000 行,重启后又写 1000 行 → 文件有 2000 行
return new FlatFileItemWriterBuilder<>()
        .resource(new FileSystemResource("output.csv"))
        .shouldDeleteIfExists(false)  // ❌ 重启时文件还在
        .build();

正确做法:设置 shouldDeleteIfExists(true) 或使用 shouldDeleteIfEmpty(true)。

反例二:忘记 transactional 导致数据不一致

// ❌ transactional=false → Writer 操作不参与 Chunk 事务
return new FlatFileItemWriterBuilder<>()
        .transactional(false)  // ❌ 数据库写入成功但文件写入失败时无法回滚
        .build();

面试高频考点

Q1:headerCallback 和 footerCallback 的调用时机?

headerCallback 在 FlatFileItemWriter.open() 中调用(文件刚打开时)。footerCallback 在 FlatFileItemWriter.close() 中调用(文件关闭前,所有数据行写入后)。两者都只调用一次。

Q2:如何实现按条件动态切换输出文件?

使用 ClassifierCompositeItemWriter,根据数据特征(如 type 字段)将记录路由到不同的 FlatFileItemWriter。每个 Writer 配置不同的 resource,实现分类写入。


上一章:ItemWriter 详解下一章:JdbcBatchItemWriter 详解

上一页
ItemWriter 详解
下一页
CompositeItemWriter 详解