无法将类型 'java.lang.String' 的 属性 值转换为所需类型 'java.sql.Date'

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

我写了一个 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

用户class:

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

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

配置 Class 中的 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<User> fieldSetMapper = new MyCustomBeanWrapperFieldSetMapper<>();

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

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

To customize the way that FieldSet values are converted to the desired type for injecting into the prototype there are several choices. You can inject PropertyEditor instances directly through the customEditors property, or you can override the createBinder(Object) and initBinder(DataBinder) methods, or you can provide a custom FieldSet implementation. You can also use a ConversionService to convert to the desired type through the conversionService property.

我建议根据最适合您需要的替代方案进行试验。

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

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