如何通过Spring Batch的MultiResourceItemReader统计单个文件记录数?
Spring Batch 统计单个文件记录数实现方案
方案一:自定义跟踪型ItemReader包装MultiResourceItemReader
MultiResourceItemReader会逐个加载资源文件,我们可以包装它来跟踪当前文件的记录数,在文件切换或读取结束时写入主表:
- 实现
ItemStreamReader接口,内部持有MultiResourceItemReader和统计相关变量:
public class TrackingItemReader<T> implements ItemStreamReader<T> { private MultiResourceItemReader<T> delegate; private int currentFileRecordCount; private Resource currentResource; private MainDataWriter mainDataWriter; // 注入主数据表写入器 @Override public T read() throws Exception { T item = delegate.read(); if (item != null) { currentFileRecordCount++; } else { // 当前文件读取完毕,写入统计 writeCurrentFileStats(); resetTrackingState(); } return item; } @Override public void open(ExecutionContext executionContext) throws ItemStreamException { delegate.open(executionContext); currentResource = delegate.getCurrentResource(); } @Override public void update(ExecutionContext executionContext) throws ItemStreamException { delegate.update(executionContext); Resource newResource = delegate.getCurrentResource(); // 检测到文件切换,先写入上一个文件的统计 if (!Objects.equals(currentResource, newResource)) { writeCurrentFileStats(); currentResource = newResource; currentFileRecordCount = 0; } } @Override public void close() throws ItemStreamException { delegate.close(); // 处理最后一个文件的统计 writeCurrentFileStats(); } private void writeCurrentFileStats() { if (currentResource != null) { mainDataWriter.write(Collections.singletonList( new MainData(currentResource.getFilename(), currentFileRecordCount) )); } } private void resetTrackingState() { currentFileRecordCount = 0; currentResource = null; } // getter/setter 注入delegate和mainDataWriter }
- 配置Step时,用这个自定义Reader替代直接使用
MultiResourceItemReader,把原MultiResourceItemReader作为delegate注入即可。
方案二:结合ItemReadListener跟踪文件切换
利用MultiResourceItemReader的getCurrentResource()方法,在监听器中实时跟踪当前文件,切换时完成上一个文件的统计:
- 自定义监听器实现
ItemReadListener和ItemStream:
public class FileRecordCountListener<T> implements ItemReadListener<T>, ItemStream { private int currentCount; private Resource currentFile; private MultiResourceItemReader<T> multiResourceReader; private MainDataWriter mainDataWriter; @Override public void afterRead(T item) { Resource activeResource = multiResourceReader.getCurrentResource(); if (!Objects.equals(currentFile, activeResource)) { // 切换文件,先写入上一个文件的统计 if (currentFile != null) { writeStats(currentFile, currentCount); } currentFile = activeResource; currentCount = 1; } else { currentCount++; } } @Override public void open(ExecutionContext executionContext) { currentCount = 0; currentFile = null; } @Override public void close() { // 处理最后一个文件 if (currentFile != null) { writeStats(currentFile, currentCount); } } private void writeStats(Resource file, int count) { mainDataWriter.write(Collections.singletonList( new MainData(file.getFilename(), count) )); } // getter/setter 注入multiResourceReader和mainDataWriter }
- 将该监听器注册到Step中,确保注入
MultiResourceItemReader实例。
方案三:给单个文件的Reader添加统计逻辑
MultiResourceItemReader内部用delegate Reader处理单个文件,我们可以给每个文件的Reader包装统计逻辑,在文件读取结束时写入主表:
- 实现带统计功能的Reader:
public class CountingItemReader<T> extends AbstractItemCountingItemStreamItemReader<T> { private ItemReader<T> delegate; private int recordCount; private Resource currentFile; private MainDataWriter mainDataWriter; @Override protected T doRead() throws Exception { T item = delegate.read(); if (item != null) { recordCount++; } return item; } @Override protected void doOpen() throws Exception { delegate.open(getExecutionContext()); recordCount = 0; } @Override protected void doClose() throws Exception { delegate.close(); // 文件读取完毕,写入统计 if (currentFile != null) { mainDataWriter.write(Collections.singletonList( new MainData(currentFile.getFilename(), recordCount) )); } } // getter/setter 注入delegate、currentFile、mainDataWriter }
- 配置
MultiResourceItemReader时,设置resourceAwareItemReaderDelegate为这个CountingItemReader,让它自动给每个加载的资源设置到currentFile属性。
内容的提问来源于stack exchange,提问作者dandelion
相关产品推荐
相关产品推荐

