【问题标题】:Spring Batch - Unable to deserialize the execution context - OffsetDateTime - cannot deserializeSpring Batch - 无法反序列化执行上下文 - OffsetDateTime - 无法反序列化
【发布时间】:2020-02-29 22:43:26
【问题描述】:

我正在尝试创建一个包含多个步骤并逐步传递对象的弹簧批处理作业。 为此,我使用了从步骤到作业上下文的 ExecutionContext。 在第一次运行时,没有问题数据一步一步地走

在下一次运行时,我收到错误: “无法反序列化执行上下文” 原因:com.fasterxml.jackson.databind.exc.InvalidDefinitionException:无法构造java.time.OffsetDateTime 的实例(没有创建者,如默认构造,存在):无法从对象值反序列化(没有委托-或基于属性的创建者)

我像这样在 ItemWriter 中编写上下文:

@Override
public void write(List<? extends Employee> items) throws Exception {
    ExecutionContext stepContext = this.stepExecution.getExecutionContext();
    List<Employee> e = new ArrayList<Employee>();
    e.addAll(items);
    stepContext.put("someKey", e);
}

然后在 ItemReader(从另一个步骤)中读回它:

@BeforeStep
public void retrieveInterstepData(StepExecution stepExecution) {
    JobExecution jobExecution = stepExecution.getJobExecution();
    ExecutionContext jobContext = jobExecution.getExecutionContext();
    this.someObject = (List<Employee>) jobContext.get("someKey");
}

我检查 spring 数据库上下文,我的日期(LocalDate、OffsetDateTime、...)存储如下:

"LocalDate": {
    "year": 2019,
    "month": "OCTOBER",
    "dayOfMonth": 30,
    "monthValue": 10,
    "era": ["java.time.chrono.IsoEra", "CE"],
    "dayOfWeek": "WEDNESDAY",
    "dayOfYear": 303,
    "leapYear": false,
    "chronology": {
        "id": "ISO",
        "calendarType": "iso8601"
    }
}
"OffsetDateTime": {
    "offset": {
        "totalSeconds": 0,
        "id": "Z",
        "rules": {
            "fixedOffset": true,
            "transitionRules": ["java.util.Collections$UnmodifiableRandomAccessList", []],
            "transitions": ["java.util.Collections$UnmodifiableRandomAccessList", []]
        }
    },
    "month": "OCTOBER",
    "year": 2019,
    "dayOfMonth": 28,
    "hour": 13,
    "minute": 42,
    "monthValue": 10,
    "nano": 511651000,
    "second": 36,
    "dayOfWeek": "MONDAY",
    "dayOfYear": 301
}

我想杰克逊选择这样存储它(我没有自定义) 但似乎杰克逊在下次运行时无法读取它自己的格式?!

我的存根是使用“swagger-codegen-maven-plugin”和 configOptions/dateLibrary=java8 从 swagger 生成的,所以我无法更改它们。

我尝试添加

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId> 
</dependency>

还有

@PostConstruct
public void init() {
    objectMapper.registerModule(new JavaTimeModule());
    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}

在@SpringBootApplication 没有变化

有什么想法吗?要么更简单地存储日期,如“2019-11-04”,要么让杰克逊读取它自己的格式?

【问题讨论】:

    标签: serialization jackson spring-batch datetimeoffset localdate


    【解决方案1】:

    您的对象映射器应设置在作业存储库使用的Jackson2ExecutionContextStringSerializer 上。您可以扩展DefaultBatchConfigurer 并覆盖createJobRepository

    @Bean
    public JobRepository createJobRepository() throws Exception {
        ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule());
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    
        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();
    }
    

    【讨论】:

      【解决方案2】:

      编辑: 我的坏我刚刚看到我有一个

      @Bean
          public BatchConfigurer batchConfigurer(@Qualifier("batchDataSource") DataSource dataSource) {
          return new DefaultBatchConfigurer(dataSource);
      }
      

      为 spring 提供 2 个 batchConfigurer。 谢谢!

      原创:

      谢谢,这似乎很有希望。 但是我找不到在哪里扩展和使用它,在哪个类上。

      我有一个批处理类配置:

      @Configuration
      @EnableConfigurationProperties(BatchProperties.class)
      public class BatchDatabaseConfiguration {
      
          @Value("${spring.datasource.driver-class-name}")
          private String driverClassName;
          @Value("${spring.datasource.url}")
          private String dbURL;
      
          @Bean("batchDataSource")
          public DataSource batchDataSource() {
          final DriverManagerDataSource dataSource = new DriverManagerDataSource();
          dataSource.setDriverClassName(driverClassName);
          dataSource.setUrl(dbURL);
          return dataSource;
          }
      
          @Bean
          public BatchConfigurer batchConfigurer(@Qualifier("batchDataSource") DataSource dataSource) {
          return new DefaultBatchConfigurer(dataSource);
          }
      
          @Bean(name = "batchTransactionManager")
          public PlatformTransactionManager batchTransactionManager(@Qualifier("batchDataSource") DataSource dataSource) {
          DataSourceTransactionManager tm = new DataSourceTransactionManager();
          tm.setDataSource(dataSource);
          return tm;
          }
      }
      

      还有一个带有 Job 定义的类:

      @Configuration
      @EnableBatchProcessing
      public class ExtractionJobConfiguration {
          @Autowired
          private JobBuilderFactory jobBuilderFactory;
      
          @Autowired
          private StepBuilderFactory stepBuilderFactory;
      
          @Bean
          public Job creationJob() {
              ...
          }
          [...]
      }
      

      还有主要的:

      @EntityScan(basePackages = { "..." })
      @SpringBootApplication
      @EnableAsync
      public class App {
      public static void main(String[] args) {
          ApplicationContext ctx = SpringApplication.run(App.class, args);
      }
      

      你怎么看? 我还读到 Spring Batch 4.2.0+ 允许在 Jackson2ExecutionContextStringSerializer (https://jira.spring.io/browse/BATCH-2828) 中自定义 ObjectMapper 这是你提议的吗? (我没有找到其他信息)

      【讨论】:

        最近更新 更多