迁移到Spring Boot 2并使用Spring Batch 4

时间:2018-04-03 08:10:29

标签: spring-boot spring-batch

我正在将Spring Boot从1.4.2迁移到2.0.0,其中还包括将Spring批处理从3.0.7迁移到4.0.0,当我尝试使用新Spring运行它时看起来批处理不再有效批量版本。

当我尝试调试时,我发现批处理尝试从batch_job_execution_context获取数据时出现问题。

我可以看到从数据库获取数据工作正常,但新版本的批处理无法解析数据库数据

  

{ “地图”:[{ “条目”:[{ “串”:[ “名称”, “”]},{ “串”:[ “发送者”, “”]},{ “字符串”: [ “ID”, “”]},{ “串”:[ “NAV”, “”]},{ “串”:[ “创建”,140418]}]}]}

出现此错误:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (START_OBJECT), expected VALUE_STRING: need JSON String that contains type id (for subtype of java.lang.Object) at [Source: (ByteArrayInputStream); line: 1, column: 9] (through reference chain: java.util.HashMap["map"])

我发现当我删除所有批处理元数据表并从头开始重新创建它们时,批处理似乎再次起作用。看起来元数据JSON格式已更改为此

  

{ “名称”: “”, “发送者”: “145844”, “ID”: “”, “NAV”: “”, “创建”: “160909”}

我不想删除旧数据以使其重新运行,所以有什么方法可以解决这个问题吗?

是否有其他人试图进行此升级?很高兴知道是否还有其他一些我可能没有注意到的突破性变化。

由于

3 个答案:

答案 0 :(得分:2)

基于上述迈克尔的回答,此代码块为我扩展了默认配置-我不得不将序列化器连接到JobRepository.class和JobExplorer.class:

@Configuration
@EnableBatchProcessing
MyBatchConfigurer extends DefaultBatchConfigurer {
    private final DataSource dataSource;

    @Autowired
    public BatchConfiguration(final DataSource dataSource) throws Exception {
        this.dataSource = dataSource;
    }

    @Bean
    ExecutionContextSerializer getSerializer() {
        return new XStreamExecutionContextStringSerializer();
    }


    @Override
    protected JobRepository createJobRepository() throws Exception {
        final JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
        factory.setDataSource(dataSource);
        factory.setSerializer(getSerializer());
        factory.setTransactionManager(getTransactionManager());
        factory.afterPropertiesSet();
        return factory.getObject();
    }

    @Override
    protected JobExplorer createJobExplorer() throws Exception {
        final JobExplorerFactoryBean jobExplorerFactoryBean = new JobExplorerFactoryBean();
        jobExplorerFactoryBean.setDataSource(dataSource);
        jobExplorerFactoryBean.setSerializer(getSerializer());
        jobExplorerFactoryBean.afterPropertiesSet();
        return jobExplorerFactoryBean.getObject();
    }
}

答案 1 :(得分:1)

在Spring Batch 4之前,ExecutionContext的默认序列化机制是通过XStream。现在它默认使用Jackson,与旧的序列化格式兼容。我们仍然可以使用旧版本(XStreamExecutionContextStringSerializer),但您需要通过实施BatchConfigurer并覆盖JobRepositoryFactoryBean中的配置来自行配置。

对于记录,这与此问题有关:https://jira.spring.io/browse/BATCH-2575

答案 2 :(得分:0)

在@anotherdave和@ michael-minella的解决方案中,您还可以使用以下类的实例替换普通的XStreamExecutionContextStringSerializer。反序列化时,它接受两种格式,然后序列化为新格式。

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.springframework.batch.core.repository.ExecutionContextSerializer;
import org.springframework.batch.core.repository.dao.Jackson2ExecutionContextStringSerializer;
import org.springframework.batch.core.repository.dao.XStreamExecutionContextStringSerializer;


/**
 * Enables Spring Batch 4 to read both ExecutionContext entries written by ealier versions and the Spring 5 format. Entries are
 * written in Spring 5 format.
 */
@SuppressWarnings("deprecation")
class XStreamOrJackson2ExecutionContextSerializer implements ExecutionContextSerializer {
    private final XStreamExecutionContextStringSerializer xStream = new XStreamExecutionContextStringSerializer();
    private final Jackson2ExecutionContextStringSerializer jackson = new Jackson2ExecutionContextStringSerializer();

    public XStreamOrJackson2ExecutionContextSerializer() throws Exception {
        xStream.afterPropertiesSet();
    }

    // The caller closes the stream; and the decoration by ensureMarkSupported does not need any cleanup.
    @SuppressWarnings("resource")
    @Override
    public Map<String, Object> deserialize(InputStream inputStream) throws IOException {
        InputStream repeatableInputStream = ensureMarkSupported(inputStream);
        repeatableInputStream.mark(Integer.MAX_VALUE);

        try {
            return jackson.deserialize(repeatableInputStream);
        } catch (JsonProcessingException e) {
            repeatableInputStream.reset();
            return xStream.deserialize(repeatableInputStream);
        }
    }

    private static InputStream ensureMarkSupported(InputStream in) {
        return in.markSupported() ? in : new BufferedInputStream(in);
    }

    @Override
    public void serialize(Map<String, Object> object, OutputStream outputStream) throws IOException {
        jackson.serialize(object, outputStream);
    }
}