我在 Spring Batch 的 JobExecution 上下文中添加一个对象,其中包含一个
Instant
字段。
它的连载如下:
{
"startFrom": {
"nano": 0,
"epochSecond": 1541116800
}
}
但是,Spring Batch 似乎无法反序列化它。
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
在进行一些研究时,我发现 Jackson 有一个
JavaTimeModule
来序列化/反序列化 Instant
和其他日期类。
但是,在 Jackson2ExecutionContextStringSerializer
类中,它按如下方式创建 ObjectMapper
,而不是注册正确的模块:
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());
}
Spring Batch 是否有理由不使用自动装配的 ObjectMapper?或者他们不注册
JavaTimeModule
的原因?
这个问题有解决办法吗?
谢谢!
编辑:
我找到了如何覆盖这个对象映射器:
@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();
}
但是,即使这样,问题仍然存在。
以下内容对我有用。我扩展了马哈茂德·本·哈辛的上述答案,该答案对尼古拉斯·维达特不起作用。 (我没有足够的声誉来发表评论)。
@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();
}
};
}
(我在另一个
ObjectMapper
类中定义了一个 @Configuration
bean。)这里重要的是还要重写 createJobExplorer()
方法并在那里设置正确的 ExecutionContextSerializer
,因为此方法将在getTarget()
,如果没有设置,这将设置一个普通的JobExplorerFactoryBean
,这会导致您所描述的错误。ExecutionContextSerializer
希望这有帮助。
Spring Batch 现在有一个修复程序,允许您自定义
@Bean
public JobRepository jobRepository() throws Exception {
ObjectMapper objectMapper = null; // configure the object mapper as required
Jackson2ExecutionContextStringSerializer serializer = new Jackson2ExecutionContextStringSerializer();
serializer.setObjectMapper(objectMapper);
JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
jobRepositoryFactoryBean.setSerializer(serializer);
// set other properties on the jobRepositoryFactoryBean
jobRepositoryFactoryBean.afterPropertiesSet();
return jobRepositoryFactoryBean.getObject();
}
,同时还可以获取
ObjectMapper
在解决此问题后在内部执行的所有默认设置:https://jira.spring.io/browse/BATCH -2828. 修复已在
Jackson2ExecutionContextStringSerialier
中,在撰写本文时当前位于
4.2.0
。原答案
我注意到类似的问题。我需要自定义
RC1
以添加
ObjectMapper
来处理 kotlin 数据类。不幸的是,目前KotlinModule
的编写方式可扩展性不是很好。正如您从创建的默认值 Jackson2ExecutionContextStringSerializer
中看到的,添加了一些默认值,包括修复作业参数的反序列化问题:https://jira.spring.io/browse/BATCH-2680. 他们设置的
ObjectMapper
是一个
JobParametersModule
类,因此我们无法设置具有相同功能的 private
并添加到其中。 我在 Jira 中提出了一个问题:https://jira.spring.io/browse/BATCH-2828
ObjectMapper
的源代码,以便您可以在覆盖中注册相同的代码。
对我来说,为了使用具有该模块的 Jackson 实例,什么是有效的
JobParmetersModule
jackson-datatype-jsr310 : support for Java 8 Date & Time API type
如下
请注意,我还必须重写 getTransactionManager(),并且由于我使用 JPA,我还必须将 Create 的隔离级别设置为
@Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
return new Jackson2ObjectMapperBuilder().findModulesViaServiceLoader(true);
}
ISOLATION_DEFAULT
:
factory.setIsolationLevelForCreate("ISOLATION_DEFAULT");