No se pudo convertir el valor de propiedad del tipo 'java.lang.String' al tipo requerido 'java.sql.Date'
Escribí un trabajo por lotes de primavera que lee un CSV y escribe en la base de datos del servidor SQL. El archivo CSV tiene algunos campos de tipo Datey TimeStamp. Estoy usando FlatFileItemReader y JdbcBatchItemWriter.
El formato de fecha en mi CSV es aaaa-MM-dd y el formato de marca de tiempo es aaaa-MM-dd HH: mm: ss.SSSSSS
NOTA: Estoy usando java.sql.Date y TimeStamp, NO java.util.Date y TimeStamp
Clase de usuario:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User{
private String firstName;
private String lastName;
private Date dateOfBirth;
private Date dateOfJoining;
private TimeStamp timeStampReg;
...
Utilizo la misma clase de modelo Userpara leer de la base de datos en un paso diferente.
El lector en la clase de configuración se ve así:
@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;
}
...
Al ejecutar el trabajo, obtengo los errores:
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]
Da el mismo error para otros campos de fecha y marca de tiempo ( dateOfJoiningy timeStampReg)
EDITAR:
Resolví el error para los Datecampos de tipo con la siguiente solución, pero sigo recibiendo el mismo error para el TimeStampcampo de tipo.
Creé un custom BeanWrapperFieldSetMappery anularlo initBinderpara agregar un 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);
}
}
y use su objeto en lugar del actual BeanWrapperFieldSetMapperen el flatFileItemReaderbean reemplazándolo con:
MyCustomBeanWrapperFieldSetMapper<User> fieldSetMapper = new MyCustomBeanWrapperFieldSetMapper<>();
PD: El editor personalizado no funcionó para java.sql.Datey java.sql.TimeStamp. Así que tuve que cambiarme a java.util.Date. Eso lo resolvió para el Datetipo, pero el error aún persiste para el TimeStamptipo.
Respuestas
Parece que su lote solo puede dividir sus líneas en Stringvalores, pero no puede convertir los tipos más exóticos. El JavaDoc paraBeanWrapperFieldSetMapper dice:
Para personalizar la forma en que los valores de FieldSet se convierten al tipo deseado para inyectar en el prototipo, existen varias opciones. Puede inyectar instancias de PropertyEditor directamente a través de la propiedad customEditors, o puede anular los métodos createBinder (Object) e initBinder (DataBinder), o puede proporcionar una implementación de FieldSet personalizada. También puede utilizar ConversionService para convertir al tipo deseado a través de la propiedad conversionService.
Sugiero experimentar con algunas de esas alternativas en función de las que se adapten mejor a sus necesidades.
EDITAR: Una posible solución puede ser simplemente usar la ApplicationConversionServiceclase, que Spring Boot proporciona lista para usar . Configuré un ejemplo de trabajo en GitHub si desea ver mi solución y, lo que es más importante, mis suposiciones sobre su proyecto. Tenga en cuenta que estoy usando las java.sql.*clases en mi proyecto de ejemplo porque ApplicationConversionServiceaparentemente funciona bien con ellas.
@Bean
FieldSetMapper<User> fieldSetMapper() {
BeanWrapperFieldSetMapper<User> fieldSetMapper = new BeanWrapperFieldSetMapper<>();
fieldSetMapper.setTargetType(User.class);
fieldSetMapper.setConversionService(ApplicationConversionService.getSharedInstance());
return fieldSetMapper;
}