Spring Batch — невозможно десериализовать контекст выполнения — OffsetDateTime — невозможно десериализовать

Я пытаюсь создать весеннее пакетное задание с несколькими шагами и передавать объект от шага к шагу. Для этого я использую 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 с помощью «swagger-codegen-maven-plugin» и configOptions/dateLibrary=java8, поэтому я не могу их изменить.

я пытался добавить

<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», либо заставить Джексона читать свой собственный формат?


person Tyrael    schedule 04.11.2019    source источник


Ответы (2)


Ваш преобразователь объектов должен быть установлен на 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();
}
person Mahmoud Ben Hassine    schedule 05.11.2019

EDIT: Плохо, я только что увидел, что у меня есть

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

Это обеспечивает 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;
    }
}

И класс с определением работы:

@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+ позволяет настраивать ObjectMapper в Jackson2ExecutionContextStringSerializer (https://jira.spring.io/browse/BATCH-2828) Это то, что вы предлагаете? (другой информации не нашел)

person Tyrael    schedule 05.11.2019