Spring Batch: как прочитать нижний колонтитул CSV-файла и проверить его с помощью FlatFileItemReader - PullRequest
0 голосов
/ 01 апреля 2019

Я использую Spring Batch и FlatFileItemReader для чтения файла .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;
}

1 Ответ

0 голосов
/ 01 апреля 2019

Вы можете использовать шаг, ориентированный на чанк, в качестве шага проверки перед бизнес-логикой вашей работы. Этот шаг будет использовать 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

Надеюсь, это поможет.

...