I've written a spring batch job that reads a CSV and writes into SQL server db. The CSV file has some fields of type Date
and TimeStamp
. I'm using FlatFileItemReader and JdbcBatchItemWriter.
The Date format in my CSV is yyyy-MM-dd and the TimeStamp format is yyyy-MM-dd HH:mm:ss.SSSSSS
NOTE: I'm using java.sql.Date and TimeStamp, NOT java.util.Date and TimeStamp
User class:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User{
private String firstName;
private String lastName;
private Date dateOfBirth;
private Date dateOfJoining;
private TimeStamp timeStampReg;
...
I use the same model class User
to read from the DB in a different step.
The Reader in Configuration Class looks like:
@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;
}
...
On running the job, I get the errors:
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]
It gives the same error for other date and timestamp fields( dateOfJoining
and timeStampReg
)
EDIT:
I resolved the error for the Date
type fields by the following solution, But I'm still getting the same error for TimeStamp
type field.
I created a custom BeanWrapperFieldSetMapper
and override its initBinder
to add a 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);
}
}
and use its object instead of the actual BeanWrapperFieldSetMapper
in the flatFileItemReader
bean by replacing it with:
MyCustomBeanWrapperFieldSetMapper<User> fieldSetMapper = new MyCustomBeanWrapperFieldSetMapper<>();
PS: The Custom Editor didn't work for java.sql.Date
and java.sql.TimeStamp
. So I had to Switch to java.util.Date
. That resolved it for Date
type but the error still persists for TimeStamp
type.
It sounds as though your batch is only able to split your lines into String
values, but fails to convert the more exotic types. The JavaDoc for BeanWrapperFieldSetMapper
says:
To customize the way that FieldSet values are converted to the desired type for injecting into the prototype there are several choices. You can inject PropertyEditor instances directly through the customEditors property, or you can override the createBinder(Object) and initBinder(DataBinder) methods, or you can provide a custom FieldSet implementation. You can also use a ConversionService to convert to the desired type through the conversionService property.
I suggest experimenting with some of those alternatives based on which ones fit your need best.
EDIT: A possible solution can be to simply use the ApplicationConversionService
class, which Spring Boot provides out of the box. I set up a working example on GitHub if you want to take a look at my solution and, more importantly, my assumptions about your project. Note that I'm using the java.sql.*
classes in my example project because the ApplicationConversionService
works fine with those, seemingly.
@Bean
FieldSetMapper<User> fieldSetMapper() {
BeanWrapperFieldSetMapper<User> fieldSetMapper = new BeanWrapperFieldSetMapper<>();
fieldSetMapper.setTargetType(User.class);
fieldSetMapper.setConversionService(ApplicationConversionService.getSharedInstance());
return fieldSetMapper;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.