Nie można przekształcić wartości właściwości typu „java.lang.String” na wymagany typ „java.sql.Date”
Napisałem wiosenne zadanie wsadowe, które czyta CSV i zapisuje w bazie danych SQL Server. Plik CSV zawiera pola typu Datei TimeStamp. Używam FlatFileItemReader i JdbcBatchItemWriter.
Format daty w moim pliku CSV to rrrr-MM-dd, a format znacznika czasu to rrrr-MM-dd GG: mm: ss.SSSSSS
UWAGA: używam java.sql.Date i TimeStamp, NIE java.util.Date i TimeStamp
Klasa użytkownika:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User{
private String firstName;
private String lastName;
private Date dateOfBirth;
private Date dateOfJoining;
private TimeStamp timeStampReg;
...
Używam tej samej klasy modelu Userdo odczytu z bazy danych w innym kroku.
Czytnik w klasie konfiguracji wygląda następująco:
@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;
}
...
Podczas wykonywania pracy otrzymuję błędy:
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]
Daje ten sam błąd dla innych pól daty i znacznika czasu ( dateOfJoiningi timeStampReg)
EDYTOWAĆ:
Postanowiłem błędu dla Datepól typu przez następujące rozwiązanie, ale jestem wciąż otrzymuję ten sam błąd na TimeStamppolu Typ.
Utworzyłem custom BeanWrapperFieldSetMapperi nadpisałem, initBinderaby dodać 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);
}
}
i użyj jego obiektu zamiast rzeczywistego BeanWrapperFieldSetMapperw flatFileItemReaderziarnie, zastępując go:
MyCustomBeanWrapperFieldSetMapper<User> fieldSetMapper = new MyCustomBeanWrapperFieldSetMapper<>();
PS: Edytor niestandardowy nie działał dla java.sql.Datei java.sql.TimeStamp. Więc musiałem się przełączyć java.util.Date. To rozwiązało problem dla Datetypu, ale błąd nadal występuje dla TimeStamptypu.
Odpowiedzi
Wygląda na to, że Twoja partia może tylko podzielić linie na Stringwartości, ale nie może przekonwertować bardziej egzotycznych typów. JavaDoc dlaBeanWrapperFieldSetMapper mówi:
Istnieje kilka możliwości dostosowania sposobu, w jaki wartości FieldSet są konwertowane na żądany typ do wstrzyknięcia do prototypu. Wystąpienia PropertyEditor można wstrzykiwać bezpośrednio za pomocą właściwości customEditors lub można przesłonić metody createBinder (Object) i initBinder (DataBinder) lub udostępnić niestandardową implementację FieldSet. Możesz również użyć ConversionService, aby przekonwertować na żądany typ za pomocą właściwości conversionService.
Proponuję poeksperymentować z niektórymi z tych alternatyw w oparciu o te, które najlepiej odpowiadają Twoim potrzebom.
EDYCJA: Możliwym rozwiązaniem może być po prostu użycie ApplicationConversionServiceklasy, którą Spring Boot zapewnia po wyjęciu z pudełka. Założyłem przykład pracę na GitHub jeśli chcesz spojrzeć na moje rozwiązanie, a co ważniejsze, moje założenia dotyczące projektu. Zauważ, że używam java.sql.*klas w moim przykładowym projekcie, ponieważ ApplicationConversionServicepozornie działa dobrze z nimi.
@Bean
FieldSetMapper<User> fieldSetMapper() {
BeanWrapperFieldSetMapper<User> fieldSetMapper = new BeanWrapperFieldSetMapper<>();
fieldSetMapper.setTargetType(User.class);
fieldSetMapper.setConversionService(ApplicationConversionService.getSharedInstance());
return fieldSetMapper;
}