【问题标题】:Spring Batch : How to read footer of CSV file and validation using FlatFileItemReaderSpring Batch:如何使用 FlatFileItemReader 读取 CSV 文件的页脚和验证
【发布时间】:2019-08-22 14:02:57
【问题描述】:

我正在使用 Spring BatchFlatFileItemReader 来读取 .CSV 文件。一个文件有一个标题(第一行)、详细信息和页脚(最后一行)。因此,我想通过页脚行验证详细信息的总数。

这是我的示例 .csv 文件。

movie.csv

姓名|类型|年份
诺丁山|浪漫喜剧|1999
玩具总动员 3|动画|2010
美国队长:第一复仇者|动作|2011
3

来自示例文件
第一行是标题(我忽略了它)。
第 2-4 行是细节行,最后是页脚。

我想读取页脚并获取值(最后一行 = 3)
然后,获取详细信息的总数记录(在这种情况下,我们有 3 行)
最后我将验证页脚 (3) 的总数和详细信息的总数记录 (3) 是否相等?


这是我的代码。

@Bean
@StepScope
public FlatFileItemReader<Movie> movieItemReader(String filePath) {
        FlatFileItemReader<Movie> reader = new FlatFileItemReader<>();
        reader.setLinesToSkip(1);   //skip header line
        reader.setResource(new PathResource(filePath));

        DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer("|");
        DefaultLineMapper<Movie> movieLineMapper = new DefaultLineMapper<>();
        FieldSetMapper<Movie> movieMapper = movieFieldSetMapper();

        movieLineMapper.setLineTokenizer(tokenizer);
        movieLineMapper.setFieldSetMapper(movieFieldSetMapper);
        movieLineMapper.afterPropertiesSet();
        reader.setLineMapper(movieLineMapper);
        return reader;
}

public FieldSetMapper<Movie> movieFieldSetMapper() {
        BeanWrapperFieldSetMapper<Movie> movieMapper = new BeanWrapperFieldSetMapper<>();
        movieMapper.setTargetType(Movie.class);
        return movieMapper;
}

【问题讨论】:

    标签: java spring spring-boot spring-batch flatfilereader


    【解决方案1】:

    您可以在工作的业务逻辑之前使用面向块的步骤作为验证步骤。此步骤将使用ItemReadListener 保存最后一项,并使用StepExecutionListener 进行验证。这是一个简单的例子:

    import org.springframework.batch.core.ExitStatus;
    import org.springframework.batch.core.ItemReadListener;
    import org.springframework.batch.core.Job;
    import org.springframework.batch.core.JobParameters;
    import org.springframework.batch.core.Step;
    import org.springframework.batch.core.StepExecution;
    import org.springframework.batch.core.StepExecutionListener;
    import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
    import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
    import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
    import org.springframework.batch.core.configuration.annotation.StepScope;
    import org.springframework.batch.core.launch.JobLauncher;
    import org.springframework.batch.core.listener.StepExecutionListenerSupport;
    import org.springframework.batch.item.ItemWriter;
    import org.springframework.batch.item.file.FlatFileItemReader;
    import org.springframework.batch.item.file.mapping.PassThroughLineMapper;
    import org.springframework.batch.repeat.RepeatStatus;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.io.ByteArrayResource;
    
    @Configuration
    @EnableBatchProcessing
    public class MyJob {
    
        @Autowired
        private JobBuilderFactory jobs;
    
        @Autowired
        private StepBuilderFactory steps;
    
        @Bean
        @StepScope
        public FlatFileItemReader<String> itemReader() {
            FlatFileItemReader<String> reader = new FlatFileItemReader<>();
            reader.setLinesToSkip(1);   //skip header line
            reader.setResource(new ByteArrayResource("header\nitem1\nitem2\n2".getBytes()));
            reader.setLineMapper(new PassThroughLineMapper());
            return reader;
        }
    
        @Bean
        public ItemWriter<String> itemWriter() {
            return items -> {
                for (String item : items) {
                    System.out.println("item = " + item);
                }
            };
        }
    
        @Bean
        public Step step1() {
            MyListener myListener = new MyListener();
            return steps.get("step1")
                    .<String, String>chunk(5)
                    .reader(itemReader())
                    .writer(itemWriter())
                    .listener((ItemReadListener<String>) myListener)
                    .listener((StepExecutionListener) myListener)
                    .build();
        }
    
        @Bean
        public Step step2() {
            return steps.get("step2")
                    .tasklet((contribution, chunkContext) -> {
                        System.out.println("Total count is ok as validated by step1");
                        return RepeatStatus.FINISHED;
                    })
                    .build();
        }
    
        @Bean
        public Job job() {
            return jobs.get("job")
                    .start(step1())
                    .next(step2())
                    .build();
        }
    
        static class MyListener extends StepExecutionListenerSupport implements ItemReadListener<String> {
    
            private String lastItem;
    
            @Override
            public void beforeRead() {
            }
    
            @Override
            public void afterRead(String item) {
                this.lastItem = item;
            }
    
            @Override
            public void onReadError(Exception ex) {
    
            }
    
            @Override
            public ExitStatus afterStep(StepExecution stepExecution) {
                int readCount = stepExecution.getReadCount();
                int totalCountInFooter = Integer.valueOf(this.lastItem); // TODO sanity checks (number format, etc)
                System.out.println("readCount = " + (readCount - 1)); // substract footer from the read count
                System.out.println("totalCountInFooter = " + totalCountInFooter);
                // TODO do validation on readCount vs totalCountInFooter
                return ExitStatus.COMPLETED; // return appropriate exit status according to validation result
            }
        }
    
        public static void main(String[] args) throws Exception {
            ApplicationContext context = new AnnotationConfigApplicationContext(MyJob.class);
            JobLauncher jobLauncher = context.getBean(JobLauncher.class);
            Job job = context.getBean(Job.class);
            jobLauncher.run(job, new JobParameters());
        }
    
    }
    

    这个例子打印:

    item = item1
    item = item2
    item = 2
    readCount = 2
    totalCountInFooter = 2
    Total count is ok as validated by step1
    

    希望这会有所帮助。

    【讨论】:

    • Mahmoud 如果我已经有一个处理和写入的步骤,你提到的这个步骤是否应该是一个新步骤(在处理之前)?改写:在处理步骤之前我需要单独的验证步骤吗?如果记录数与页脚编号不匹配,我根本不想处理。谢谢。
    • 是的,在我的示例中,这是一个预验证步骤,因此如果计数不匹配,我们可以停止进一步处理(从侦听器返回 ExitStatus.FAILED)。
    猜你喜欢
    • 2020-02-10
    • 2019-01-26
    • 2014-02-07
    • 2023-03-17
    • 2021-11-12
    • 1970-01-01
    • 1970-01-01
    • 2015-06-13
    • 2017-06-29
    相关资源
    最近更新 更多