Не удалось преобразовать значение свойства типа java.lang.String в требуемый тип java.sql.Date

Oct 08 2020

Я написал весеннее пакетное задание, которое читает CSV и записывает в базу данных SQL-сервера. В CSV-файле есть несколько полей типа Dateи TimeStamp. Я использую FlatFileItemReader и JdbcBatchItemWriter.

Формат даты в моем CSV - yyyy-MM-dd, а формат TimeStamp - 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для чтения из БД на другом этапе.

Читатель в классе конфигурации выглядит так:

@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]

Это дает ту же ошибку для других полей даты и времени ( dateOfJoiningи timeStampReg)

РЕДАКТИРОВАТЬ:

Я разрешил ошибку для 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);
        
    }
}

и используйте его объект вместо фактического BeanWrapperFieldSetMapperв flatFileItemReaderbean-компоненте, заменив его на:

MyCustomBeanWrapperFieldSetMapper<User> fieldSetMapper = new MyCustomBeanWrapperFieldSetMapper<>();

PS: Пользовательский редактор не работал для java.sql.Dateи java.sql.TimeStamp. Поэтому мне пришлось перейти на java.util.Date. Это решило проблему для Dateтипа, но ошибка по-прежнему сохраняется для TimeStampтипа.

Ответы

2 ThomasKåsene Oct 08 2020 at 21:27

Звучит так, как будто ваша партия может только разбить ваши строки на Stringзначения, но не может преобразовать более экзотические типы. В JavaDocBeanWrapperFieldSetMapper говорится:

Чтобы настроить способ преобразования значений FieldSet в нужный тип для внедрения в прототип, есть несколько вариантов. Вы можете внедрить экземпляры PropertyEditor напрямую через свойство customEditors, или вы можете переопределить методы createBinder (Object) и initBinder (DataBinder), или вы можете предоставить настраиваемую реализацию FieldSet. Вы также можете использовать ConversionService для преобразования в нужный тип с помощью свойства conversionService.

Я предлагаю поэкспериментировать с некоторыми из этих альтернатив, исходя из того, какие из них лучше всего соответствуют вашим потребностям.

РЕДАКТИРОВАТЬ: Возможным решением может быть просто использование ApplicationConversionServiceкласса, который Spring Boot предоставляет из коробки. Я создал рабочий пример на GitHub, если вы хотите взглянуть на мое решение и, что более важно, на мои предположения о вашем проекте. Обратите внимание, что я использую java.sql.*классы в своем примерном проекте, потому что ApplicationConversionService, похоже, они отлично работают с ними.

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