【问题标题】:Failed to convert property value of type 'java.lang.String' to required type 'java.sql.Date'无法将类型“java.lang.String”的属性值转换为所需类型“java.sql.Date”
【发布时间】:2021-01-23 23:17:46
【问题描述】:

我编写了一个 spring 批处理作业,它读取 CSV 并写入 SQL 服务器数据库。 CSV 文件有一些类型为DateTimeStamp 的字段。我正在使用 FlatFileItemReader 和 JdbcBatchItemWriter。

我的 CSV 中的日期格式是 yyyy-MM-dd 时间戳格式为 yyyy-MM-dd HH:mm:ss.SSSSSS

注意:我使用的是 java.sql.Date 和 TimeStamp,而不是 java.util.Date 和 TimeStamp

用户类别:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User{
    private String firstName;
    private String lastName;
    private Date dateOfBirth;
    private Date dateOfJoining;
    private TimeStamp timeStampReg;
...

我使用相同的模型类 User 在不同的步骤中从数据库中读取。

配置类中的 Reader 如下所示:

@Configuration
public class BatchConfigClass{

    
    //Step 1
    @Bean
    public FlatFileItemReader<User> itemReader() {

        FlatFileItemReader<User> flatFileItemReader = new FlatFileItemReader<>();
        flatFileItemReader.setResource(inputResource);
        flatFileItemReader.setName("CSV-Reader");
        flatFileItemReader.setLineMapper(lineMapper());
        return flatFileItemReader;
    }

    @Bean
    public LineMapper<User> lineMapper() {

        DefaultLineMapper<User> defaultLineMapper = new DefaultLineMapper<>();
        DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();

        lineTokenizer.setDelimiter(",");
        lineTokenizer.setStrict(false);
        lineTokenizer.setNames(new String[]{"firstName", "lastName", "dateOfBirth", "dateOfJoining","timeStampReg"});

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

        defaultLineMapper.setLineTokenizer(lineTokenizer);
        defaultLineMapper.setFieldSetMapper(fieldSetMapper);

        return defaultLineMapper;
    }
...

在运行作业时,我收到错误:

Parsing error at line: 1 in resource=Path to the CSV file

Failed to convert property value of type 'java.lang.String' to required type 'java.sql.Date' for property 'dateOfBirth';

Field error in object 'target' on field 'dateOfBirth': rejected value [1998-12-31]; codes [typeMismatch.target.dateOfBirth,typeMismatch.dateOfBirth,typeMismatch.java.sql.Date,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [target.dateOfBirth,dateOfBirth]; arguments []; default message [dateOfBirth]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.sql.Date' for property 'dateOfBirth'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'java.sql.Date' for property 'dateOfBirth': no matching editors or conversion strategy found]

它对其他日期和时间戳字段(dateOfJoiningtimeStampReg)给出相同的错误

编辑:

我通过以下解决方案解决了Date 类型字段的错误,但我仍然收到TimeStamp 类型字段的相同错误。

我创建了一个custom BeanWrapperFieldSetMapper 并覆盖了它的initBinder 以添加一个CustomDateEditor

public class MyCustomBeanWrapperFieldSetMapper<User> extends BeanWrapperFieldSetMapper<FSMChequePayment> {

    @Override
    protected void initBinder(DataBinder binder) {
        CustomDateEditor editor = new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true);
        binder.registerCustomEditor(Date.class, editor);

        CustomDateEditor editorTimeStamp = new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS"), true);
        binder.registerCustomEditor(TimeStamp.class,editorTimeStamp);
        
    }
}

并在flatFileItemReader bean 中使用它的对象而不是实际的BeanWrapperFieldSetMapper,方法是将其替换为:

MyCustomBeanWrapperFieldSetMapper&lt;User&gt; fieldSetMapper = new MyCustomBeanWrapperFieldSetMapper&lt;&gt;();

PS:自定义编辑器不适用于 java.sql.Datejava.sql.TimeStamp。所以我不得不切换到java.util.Date。这解决了Date 类型的问题,但TimeStamp 类型的错误仍然存​​在。

【问题讨论】:

    标签: java spring spring-boot type-conversion spring-batch


    【解决方案1】:

    听起来好像您的批处理只能将您的行拆分为 String 值,但无法转换更奇特的类型。 JavaDoc for BeanWrapperFieldSetMapper 说:

    要自定义将 FieldSet 值转换为所需类型以注入原型的方式,有多种选择。您可以通过 customEditors 属性直接注入 PropertyEditor 实例,也可以重写 createBinder(Object) 和 initBinder(DataBinder) 方法,也可以提供自定义 FieldSet 实现。您还可以使用 ConversionService 通过 conversionService 属性转换为所需的类型。

    我建议根据最适合您的需求尝试其中一些替代方案。

    编辑: 一个可能的解决方案是简单地使用 Spring Boot 开箱即用的 ApplicationConversionService 类。如果你想看看我的解决方案,更重要的是,我对你的项目的假设,我设置了一个working example on GitHub。请注意,我在示例项目中使用了 java.sql.* 类,因为 ApplicationConversionService 似乎可以很好地处理这些类。

    @Bean
    FieldSetMapper<User> fieldSetMapper() {
        BeanWrapperFieldSetMapper<User> fieldSetMapper = new BeanWrapperFieldSetMapper<>();
        fieldSetMapper.setTargetType(User.class);
        fieldSetMapper.setConversionService(ApplicationConversionService.getSharedInstance());
        return fieldSetMapper;
    }
    
    

    【讨论】:

    • 我实现了自定义BeanWrapperFieldSetMapper,通过覆盖initBinder 添加CustomDateEditor。它解决了Date 类型的问题,但TimeStamp 类型的问题仍然存在。虽然我不得不从java.sql.Date 切换到java.util.Date。我已经更新了我的问题中的详细信息。您可以查看 EDIT 部分。
    • 我修改了我的答案以提供一个实际的解决方案。这对我来说似乎很简单 - 看看你是否喜欢它。
    猜你喜欢
    • 2020-09-07
    • 2021-06-13
    • 2017-05-12
    • 2018-08-21
    • 2017-04-21
    • 2018-10-13
    • 2017-06-14
    • 2016-07-15
    • 2019-06-20
    相关资源
    最近更新 更多