Spring Batch - Невозможно десериализовать контекст выполнения - OffsetDateTime - не может десериализовать - PullRequest
0 голосов
/ 04 ноября 2019

Я пытаюсь создать пакетное задание с несколькими шагами и передачей объекта от шага к шагу. Для этого я использую 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);
}

В приложении @SpringBoot нет изменений

Есть идеи? Или хранить даты более просто, например, «2019-11-04», или заставить Джексона читать его собственный формат?

Ответы [ 2 ]

0 голосов
/ 05 ноября 2019

РЕДАКТИРОВАТЬ: Мое плохое, я только что увидел, что у меня есть

@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) Это то, что вы предлагаете? (Я не нахожу другую информацию)

0 голосов
/ 05 ноября 2019

Ваш объект сопоставления должен быть установлен на 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();
}
...