Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Batch not deserialising dates

I'm adding an object in the JobExecution context of spring batch, which contains an Instant field.

It's getting serialised as follows:

{
  "startFrom": {
    "nano": 0,
    "epochSecond": 1541116800
   }
 }

However, Spring Batch doesn't seem to be able to deserialise it.

Caused by: java.lang.IllegalArgumentException: Unable to deserialize the execution context
    at org.springframework.batch.core.repository.dao.JdbcExecutionContextDao$ExecutionContextRowMapper.mapRow(JdbcExecutionContextDao.java:325)
    at org.springframework.batch.core.repository.dao.JdbcExecutionContextDao$ExecutionContextRowMapper.mapRow(JdbcExecutionContextDao.java:309)
    at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:93)
    at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:60)
    at org.springframework.jdbc.core.JdbcTemplate$1.doInPreparedStatement(JdbcTemplate.java:667)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:605)
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:657)
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:688)
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:700)
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:756)
    at org.springframework.batch.core.repository.dao.JdbcExecutionContextDao.getExecutionContext(JdbcExecutionContextDao.java:112)
    at org.springframework.batch.core.explore.support.SimpleJobExplorer.getJobExecutionDependencies(SimpleJobExplorer.java:202)
    at org.springframework.batch.core.explore.support.SimpleJobExplorer.getJobExecutions(SimpleJobExplorer.java:83)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
    at org.springframework.aop.framework.ReflectiveMethod

When doing some research I see that Jackson has a JavaTimeModule to serialise/deserialise Instant and other date classes. However, in the Jackson2ExecutionContextStringSerializer class, it creates the ObjectMapper as follows, not registering the right module:

public Jackson2ExecutionContextStringSerializer() {
    this.objectMapper = new ObjectMapper();
    this.objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
    this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
    this.objectMapper.enableDefaultTyping();
    this.objectMapper.registerModule(new JobParametersModule());
}

Is there a reason for Spring Batch not to use the autowired ObjectMapper? Or a reason for them not to register the JavaTimeModule? Is there maybe a workaround this issue?

Thanks!

Edit:

I've found how to overwrite this object mapper:

  @Bean
  public JobRepository createJobRepository() throws Exception {
    ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule()).findAndRegisterModules();

    Jackson2ExecutionContextStringSerializer defaultSerializer = new Jackson2ExecutionContextStringSerializer();
    defaultSerializer.setObjectMapper(objectMapper);

    JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
    factory.setDataSource(dataSource);
    factory.setTransactionManager(transactionManager);
    factory.setSerializer(defaultSerializer);
    factory.afterPropertiesSet();
    return factory.getObject();
  }

However, even with this, the issue persists.

like image 491
Nicolas Widart Avatar asked Aug 07 '26 01:08

Nicolas Widart


1 Answers

The following worked for me. I am extending Mahmoud Ben Hassine's answer above which didn't work for Nicolas Widart. (I don't have enough reputation to just comment).

@Bean
public BatchConfigurer configurer(DataSource dataSource, PlatformTransactionManager transactionManager,
  ObjectMapper objectMapper) {

    return new DefaultBatchConfigurer(dataSource) {
        final Jackson2ExecutionContextStringSerializer serializer = new Jackson2ExecutionContextStringSerializer();

        @Override
        protected JobRepository createJobRepository() throws Exception {
            serializer.setObjectMapper(objectMapper);

            JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
            factory.setDataSource(dataSource);
            factory.setTransactionManager(transactionManager);
            factory.setSerializer(serializer);
            factory.afterPropertiesSet();
            return factory.getObject();
        }

        @Override
        protected JobExplorer createJobExplorer() throws Exception {
            serializer.setObjectMapper(objectMapper);

            JobExplorerFactoryBean jobExplorerFactoryBean = new JobExplorerFactoryBean();
            jobExplorerFactoryBean.setSerializer(serializer);
            jobExplorerFactoryBean.setDataSource(dataSource);
            jobExplorerFactoryBean.afterPropertiesSet();
            return jobExplorerFactoryBean.getObject();
        }
    };
}

(I have defined an ObjectMapper bean in another @Configuration class.) The important thing here is to also override the createJobExplorer() method and set the correct ExecutionContextSerializer there, as this method will call getTarget() on the JobExplorerFactoryBean, which will set a vanilla ExecutionContextSerializer if there is none set and which causes the error you described.

like image 135
Luca Liechti Avatar answered Aug 10 '26 13:08

Luca Liechti



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!