Impossibile convertire il valore della proprietà di tipo "java.lang.String" nel tipo richiesto "java.sql.Date"

Oct 08 2020

Ho scritto un lavoro batch primaverile che legge un CSV e scrive nel database del server SQL. Il file CSV ha alcuni campi di tipo Datee TimeStamp. Sto usando FlatFileItemReader e JdbcBatchItemWriter.

Il formato della data nel mio CSV è yyyy-MM-dd e il formato TimeStamp è yyyy-MM-dd HH: mm: ss.SSSSSS

NOTA: sto usando java.sql.Date e TimeStamp, NON java.util.Date e TimeStamp

Classe utente:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User{
    private String firstName;
    private String lastName;
    private Date dateOfBirth;
    private Date dateOfJoining;
    private TimeStamp timeStampReg;
...

Uso la stessa classe del modello Userper leggere dal DB in un passaggio diverso.

La classe Reader in Configuration ha il seguente aspetto:

@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;
    }
...

Durante l'esecuzione del lavoro, ottengo gli errori:

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]

Fornisce lo stesso errore per altri campi data e timestamp ( dateOfJoininge timeStampReg)

MODIFICARE:

Ho risolto l'errore per i Datecampi tipo con la seguente soluzione, ma continuo a ricevere lo stesso errore per il TimeStampcampo tipo.

Ho creato un custom BeanWrapperFieldSetMappere sovrascrivo il suo initBinderper aggiungere un file 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);
        
    }
}

e usa il suo oggetto invece dell'effettivo BeanWrapperFieldSetMappernel flatFileItemReaderbean sostituendolo con:

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

PS: l'editor personalizzato non ha funzionato per java.sql.Datee java.sql.TimeStamp. Quindi ho dovuto passare a java.util.Date. Questo lo ha risolto per il Datetipo ma l'errore persiste ancora per il TimeStamptipo.

Risposte

2 ThomasKåsene Oct 08 2020 at 21:27

Sembra che il tuo batch sia solo in grado di dividere le tue linee in Stringvalori, ma non riesce a convertire i tipi più esotici. Il JavaDoc perBeanWrapperFieldSetMapper dice:

Per personalizzare il modo in cui i valori FieldSet vengono convertiti nel tipo desiderato per l'iniezione nel prototipo, sono disponibili diverse scelte. Puoi iniettare istanze di PropertyEditor direttamente tramite la proprietà customEditors oppure puoi sovrascrivere i metodi createBinder (Object) e initBinder (DataBinder) oppure puoi fornire un'implementazione FieldSet personalizzata. È inoltre possibile utilizzare un ConversionService per convertire nel tipo desiderato tramite la proprietà conversionService.

Suggerisco di sperimentare alcune di queste alternative in base a quelle che si adattano meglio alle tue esigenze.

EDIT: Una possibile soluzione può essere quella di utilizzare semplicemente la ApplicationConversionServiceclasse, che Spring Boot fornisce fuori dagli schemi. Ho impostato un esempio funzionante su GitHub se vuoi dare un'occhiata alla mia soluzione e, soprattutto, alle mie ipotesi sul tuo progetto. Nota che sto usando le java.sql.*classi nel mio progetto di esempio perché ApplicationConversionService, apparentemente, funziona bene con quelle.

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