Échec de la conversion de la valeur de propriété de type «java.lang.String» en type requis «java.sql.Date»
J'ai écrit un travail par lots de printemps qui lit un CSV et écrit dans la base de données du serveur SQL. Le fichier CSV contient des champs de type Dateet TimeStamp. J'utilise FlatFileItemReader et JdbcBatchItemWriter.
Le format de la date dans mon CSV est aaaa-MM-jj et le format de l'horodatage est aaaa-MM-jj HH: mm: ss.SSSSSS
REMARQUE: j'utilise java.sql.Date et TimeStamp, PAS java.util.Date et TimeStamp
Classe d'utilisateur:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User{
private String firstName;
private String lastName;
private Date dateOfBirth;
private Date dateOfJoining;
private TimeStamp timeStampReg;
...
J'utilise la même classe de modèle Userpour lire à partir de la base de données à une étape différente.
Le lecteur dans la classe de configuration ressemble à:
@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;
}
...
Lors de l'exécution du travail, j'obtiens les erreurs:
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]
Il donne la même erreur pour les autres champs de date et d'horodatage ( dateOfJoininget timeStampReg)
ÉDITER:
J'ai résolu l'erreur pour les Datechamps de type par la solution suivante, mais j'obtiens toujours la même erreur pour le TimeStampchamp de type.
J'ai créé un custom BeanWrapperFieldSetMapperet le remplacer initBinderpour ajouter un fichier 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);
}
}
et utilisez son objet à la place du réel BeanWrapperFieldSetMapperdans le flatFileItemReaderbean en le remplaçant par:
MyCustomBeanWrapperFieldSetMapper<User> fieldSetMapper = new MyCustomBeanWrapperFieldSetMapper<>();
PS: l'éditeur personnalisé n'a pas fonctionné pour java.sql.Dateet java.sql.TimeStamp. J'ai donc dû passer à java.util.Date. Cela l'a résolu pour le Datetype, mais l'erreur persiste pour le TimeStamptype.
Réponses
Il semble que votre lot ne puisse diviser vos lignes qu'en Stringvaleurs, mais ne parvient pas à convertir les types les plus exotiques. Le JavaDoc pourBeanWrapperFieldSetMapper dit:
Pour personnaliser la façon dont les valeurs FieldSet sont converties dans le type souhaité pour l'injection dans le prototype, il existe plusieurs choix. Vous pouvez injecter des instances PropertyEditor directement via la propriété customEditors, ou vous pouvez remplacer les méthodes createBinder (Object) et initBinder (DataBinder), ou vous pouvez fournir une implémentation FieldSet personnalisée. Vous pouvez également utiliser un ConversionService pour convertir au type souhaité via la propriété conversionService.
Je suggère d'expérimenter certaines de ces alternatives en fonction de celles qui correspondent le mieux à vos besoins.
EDIT: Une solution possible peut être d'utiliser simplement la ApplicationConversionServiceclasse, fournie par Spring Boot. J'ai mis en place un exemple de travail sur GitHub si vous souhaitez jeter un œil à ma solution et, plus important encore, à mes hypothèses sur votre projet. Notez que j'utilise les java.sql.*classes dans mon exemple de projet car cela ApplicationConversionServicefonctionne bien avec celles-ci, apparemment.
@Bean
FieldSetMapper<User> fieldSetMapper() {
BeanWrapperFieldSetMapper<User> fieldSetMapper = new BeanWrapperFieldSetMapper<>();
fieldSetMapper.setTargetType(User.class);
fieldSetMapper.setConversionService(ApplicationConversionService.getSharedInstance());
return fieldSetMapper;
}