Spring Batch读取CSV时获取文件总记录数的最简单方法是什么?
Spring Batch 读取CSV获取原始总记录数解决方案
你之前在Processor中计数异常,通常是两个原因导致:一是配置了跳过策略后,Processor抛出校验异常时对应记录的process逻辑不会走完,计数漏掉;二是多线程场景下普通计数器线程不安全,导致数值异常。
最简单实现:使用ItemReadListener监听读取事件
Spring Batch的读监听会在FlatFileItemReader成功读取每条记录后触发,完全不受后续Processor校验、跳过逻辑的影响,刚好匹配统计原始文件总条数的需求,且对现有业务逻辑侵入性极低。
实现步骤
- 自定义计数监听器,使用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() {} }
- 将监听器绑定到你的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
相关产品推荐
相关产品推荐

