Не удалось преобразовать значение свойства типа java.lang.String в требуемый тип java.sql.Date
Я написал весеннее пакетное задание, которое читает 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типа.
Ответы
Звучит так, как будто ваша партия может только разбить ваши строки на 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;
}