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

Spring Batch读取CSV时获取文件总记录数的最简单方法是什么?

Spring Batch 读取CSV获取原始总记录数解决方案

你之前在Processor中计数异常,通常是两个原因导致:一是配置了跳过策略后,Processor抛出校验异常时对应记录的process逻辑不会走完,计数漏掉;二是多线程场景下普通计数器线程不安全,导致数值异常。

最简单实现:使用ItemReadListener监听读取事件

Spring Batch的读监听会在FlatFileItemReader成功读取每条记录后触发,完全不受后续Processor校验、跳过逻辑的影响,刚好匹配统计原始文件总条数的需求,且对现有业务逻辑侵入性极低。

实现步骤

  1. 自定义计数监听器,使用Spring Batch内置ExecutionContext存储计数,天然支持任务重启、分片、多线程场景:
import org.springframework.batch.core.annotation.BeforeStep;
import org.springframework.batch.core.annotation.AfterStep;
import org.springframework.batch.core.ItemReadListener;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.core.scope.context.StepSynchronizationManager;

// 替换泛型T为你实际的CSV映射实体类
@Component
public class CsvRecordCountListener implements ItemReadListener<T> {
    private static final String TOTAL_RECORD_KEY = "originalCsvTotalCount";

    @BeforeStep
    public void initCounter(StepExecution stepExecution) {
        // 步骤启动前初始化计数器
        stepExecution.getExecutionContext().putLong(TOTAL_RECORD_KEY, 0L);
    }

    @Override
    public void afterRead(T item) {
        // 每成功读取一行有效记录计数+1,配置跳过的表头不会被计入
        ExecutionContext context = StepSynchronizationManager.getContext().getStepExecution().getExecutionContext();
        long current = context.getLong(TOTAL_RECORD_KEY);
        context.putLong(TOTAL_RECORD_KEY, current + 1);
    }

    @Override
    public void onReadError(Exception ex) {
        // 如需统计包含CSV格式错误的行,可在这里追加计数
        // ExecutionContext context = StepSynchronizationManager.getContext().getStepExecution().getExecutionContext();
        // long current = context.getLong(TOTAL_RECORD_KEY);
        // context.putLong(TOTAL_RECORD_KEY, current + 1);
    }

    @AfterStep
    public void getFinalCount(StepExecution stepExecution) {
        Long totalCount = stepExecution.getExecutionContext().getLong(TOTAL_RECORD_KEY);
        // 此处可将总条数持久化到业务表、或存入任务上下文供后续逻辑使用
        System.out.println("CSV文件原始有效记录数:" + totalCount);
    }

    // 空实现不需要用到的监听方法
    @Override public void beforeRead() {}
}
  1. 将监听器绑定到你的CSV导入步骤中:
@Bean
public Step csvImportStep(FlatFileItemReader<T> csvReader, 
                          CsvRecordCountListener countListener,
                          ItemProcessor<T, T> csvProcessor,
                          ItemWriter<T> csvWriter) {
    return stepBuilderFactory.get("csvImportStep")
            .<T, T>chunk(1000)
            .reader(csvReader)
            .processor(csvProcessor)
            .writer(csvWriter)
            .listener(countListener) // 绑定计数监听器
            .build();
}

可选方案:封装计数委托Reader

如果需要统计包括读取失败(如CSV格式错误)的所有物理行,可直接封装原FlatFileItemReader实现全局计数:

import org.springframework.batch.item.ItemReader;
import java.util.concurrent.atomic.LongAdder;

public class CountableCsvReader<T> implements ItemReader<T> {
    private final ItemReader<T> delegateReader;
    private final LongAdder totalCounter = new LongAdder();

    public CountableCsvReader(ItemReader<T> delegateReader) {
        this.delegateReader = delegateReader;
    }

    @Override
    public T read() throws Exception {
        T item = null;
        try {
            item = delegateReader.read();
            return item;
        } finally {
            // 读取到有效记录时计数,如需统计所有物理行可去掉item!=null的判断
            if (item != null) {
                totalCounter.increment();
            }
        }
    }

    public long getTotalCount() {
        return totalCounter.sum();
    }
}

使用时将原有FlatFileItemReader作为参数传入该委托Reader,再把委托Reader配置到Step中即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.13 19:00:01