繁体   English   中英

使用 Spring Batch 将文件中的日期解析为 LocalDateTime

[英]Using Spring Batch to parse date from file into LocalDateTime

我正在尝试使用 Spring Batch 读取带有日期的 CSV 文件,但是我无法将日期解析为LocalDateTime对象:

字段“日期”上的对象“目标”中的字段错误:拒绝值 [2017-07-20 04:15:25.0]; 代码 [typeMismatch.target.date,typeMismatch.date,typeMismatch.java.time.LocalDateTime,typeMismatch]; 参数 [org.springframework.context.support.DefaultMessageSourceResolvable: 代码 [target.date,date]; 参数 []; 默认消息 [日期]]; 默认消息 [无法将“java.lang.String”类型的属性值转换为属性“date”所需的类型“java.time.LocalDateTime”; 嵌套异常是 java.lang.IllegalStateException:无法将“java.lang.String”类型的值转换为属性“date”所需的类型“java.time.LocalDateTime”:找不到匹配的编辑器或转换策略]

Main.java :

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringBatchDateParseConfig.class);

        JobLauncher jobLauncher = context.getBean(JobLauncher.class);
        Job job = context.getBean("job", Job.class);

        JobParameters jobParameters = new JobParametersBuilder().toJobParameters();

        try {
            JobExecution jobExecution = jobLauncher.run(job, jobParameters);
        }
        catch (Exception e) {
            e.printStackTrace();
        }

    }
}

SpringBatchDateParseConfig.java

@Configuration
@EnableBatchProcessing
public class SpringBatchDateParseConfig {
    @Inject
    private JobBuilderFactory jobBuilderFactory;

    @Inject
    private StepBuilderFactory stepBuilderFactory;

    @Bean
    public Step step1() {
        return stepBuilderFactory.get("step1")
                                 .<TestClass, TestClass>chunk(2)
                                 .reader(testClassItemReader())
                                 .writer(testClassItemWriter())
                                 .build();
    }

    @Bean
    public Job job(Step step1) {
        return jobBuilderFactory.get("job")
                .start(step1)
                .build();
    }

    @Bean
    FlatFileItemReader<TestClass> testClassItemReader() {
        FlatFileItemReader<TestClass> flatFileItemReader = new FlatFileItemReader<>();
        flatFileItemReader.setResource(new ClassPathResource("test.csv"));
        flatFileItemReader.setLinesToSkip(1);
        DefaultLineMapper defaultLineMapper = new DefaultLineMapper();
        DelimitedLineTokenizer delimitedLineTokenizer = new DelimitedLineTokenizer();
        delimitedLineTokenizer.setNames(new String[]{"foo", "bar", "date"});

        BeanWrapperFieldSetMapper<TestClass> fieldSetMapper = new BeanWrapperFieldSetMapper<>();
        fieldSetMapper.setTargetType(TestClass.class);

        defaultLineMapper.setLineTokenizer(delimitedLineTokenizer);
        defaultLineMapper.setFieldSetMapper(fieldSetMapper);
        flatFileItemReader.setLineMapper(defaultLineMapper);

        return flatFileItemReader;

    }

    @Bean
    ItemWriter<TestClass> testClassItemWriter() {
        return new ItemWriter<TestClass>() {
            @Override
            public void write(List<? extends TestClass> items) throws Exception {
                for (TestClass TestClass : items) {
                    System.out.println(TestClass.toString());
                }
            }
        };
    }
}

TestClass.java :

public class TestClass {

    private String foo;
    private String bar;
    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME, pattern = "yyyy-MM-dd H:m:s.S")
    private LocalDateTime date;

    public String getFoo() {
        return foo;
    }

    public void setFoo(String foo) {
        this.foo = foo;
    }

    public String getBar() {
        return bar;
    }

    public void setBar(String bar) {
        this.bar = bar;
    }

    public LocalDateTime getDate() {
        return date;
    }

    public void setDate(LocalDateTime date) {
        this.date = date;
    }
}

test.csv

foo,bar,date
asdf,fdsa,2017-07-20 04:15:25.0
qwerty,ytrewq,2017-07-20 04:15:25.0

我错过了什么吗?

您可以覆盖BeanWrapperFieldSetMapper<T> initBinder方法:

public class BeanWrapperFieldSetMapperCustom<T> extends BeanWrapperFieldSetMapper<T> {

    @Override
    protected void initBinder(DataBinder binder) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) throws IllegalArgumentException {
                if (StringUtils.isNotEmpty(text)) {
                    setValue(LocalDate.parse(text, formatter));
                } else {
                    setValue(null);
                }
            }

            @Override
            public String getAsText() throws IllegalArgumentException {
                Object date = getValue();
                if (date != null) {
                    return formatter.format((LocalDate) date);
                } else {
                    return "";
                }
            }
        });
    }
}

请使用标准方法进行数据转换:

  1. 数据转换由 ConversionService 负责。
  2. 由于 afterPropertiesSet() 方法中的断言,最好将 FieldSetMapper 作为独立 bean 公开(这样可以节省分析错误配置的时间)。
  3. 最好使用 FlatFileItemReaderBuilder。

您可以轻松扩展和自定义支持的数据类型

@Configuration
@EnableBatchProcessing
public class SpringBatchDateParseConfig {

    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Bean
    public ConversionService testConversionService() {
        DefaultConversionService testConversionService = new DefaultConversionService();
        DefaultConversionService.addDefaultConverters(testConversionService);
        testConversionService.addConverter(new Converter<String, LocalDateTime>() {
            @Override
            public LocalDateTime convert(String text) {
                return LocalDateTime.parse(text, DateTimeFormatter.ISO_DATE_TIME);
            }
        });

        return testConversionService;
    }

    @Bean
    public FieldSetMapper<TestClass> testClassRowMapper(ConversionService testConversionService) {
        BeanWrapperFieldSetMapper<TestClass> mapper = new BeanWrapperFieldSetMapper<>();
        mapper.setConversionService(testConversionService);
        mapper.setTargetType(TestClass.class);
        return mapper;
    }

    @Bean
    FlatFileItemReader<TestClass> testClassItemReader(FieldSetMapper<TestClass> testClassRowMapper) {
        return new FlatFileItemReaderBuilder<TestClass>().delimited()
                .delimiter(",")
                .names(new String[]{"foo", "bar", "date"})
                .linesToSkip(1)
                .resource(new ClassPathResource("test.csv"))
                .fieldSetMapper(testClassRowMapper)
                .build();
    }

    @Bean
    public Step step1(FieldSetMapper<TestClass> testClassRowMapper) {
        return stepBuilderFactory.get("step1")
                .<TestClass, TestClass>chunk(2)
                .reader(testClassItemReader(testClassRowMapper))
                .writer(testClassItemWriter())
                .build();
    }

    @Bean
    public Job job(Step step1) {
        return jobBuilderFactory.get("job")
                .start(step1)
                .build();
    }

    @Bean
    ItemWriter<TestClass> testClassItemWriter() {
        return new ItemWriter<TestClass>() {
            @Override
            public void write(List<? extends TestClass> items) throws Exception {
                for (TestClass TestClass : items) {
                    System.out.println(TestClass.toString());
                }
            }
        };
    }
}

我使用了 Pavel 的解决方案,因为将 Power 赋予 ConversionService 对我来说似乎是一个更清晰的解决方案。

我的 CSV 中的日期格式如下:10.01.20 所以模式是:“dd.MM.yy”,我需要从中创建 LocalDate 对象。

这是我的自定义 ConversionService:

public ConversionService createConversionService() {
    DefaultConversionService conversionService = new DefaultConversionService();
    DefaultConversionService.addDefaultConverters(conversionService);
    conversionService.addConverter(new Converter<String, LocalDate>() {
        @Override
        public LocalDate convert(String text) {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yy");
            return LocalDate.parse(text, formatter);
        }
    });
    return conversionService;
}

然后我将此自定义 ConversionService 添加到我在 ItemReader 中使用的 BeanWrapperFieldSetMapper。

fieldSetMapper.setConversionService(createConversionService());

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM