0

我创建了一个通用 Spring Batch 作业,用于处理数据并将其存储到 CSV 中。我需要一些来自阅读器的数据传递给我正在尝试使用 JobExecution 执行的编写器。然而令人惊讶的是,代码似乎首先调用 getWriter() 而不是 getReader() 函数。我的配置如下。有人可以解释为什么会发生这种情况,以及是否有其他方法可以将数据从读取器传递到写入器。

@Bean
@StepScope
public ItemReader<Map<String, Object>> getDataReader() throws Exception {
    return springBatchReader.getReader();
}

@Bean
@StepScope
public FlatFileItemWriter<Map<String, Object>> getDataWriter() throws Exception {
    return (FlatFileItemWriter<Map<String, Object>>) springBatchWriter.getWriter();
}

@Bean
public Job SpringBatchJob(Step generateReport) throws Exception {
    return jobBuilderFactory.get("SpringBatchJob" + System.currentTimeMillis())
            .preventRestart()
            .incrementer(new RunIdIncrementer())
            .flow(generateReport)
            .end()
            .build();
}

@Bean
public Step generateReport() throws Exception {
    return stepBuilderFactory.get("generateReport").<Map<String, Object>, Map<String, Object>>chunk(batchSize)
            .reader(getDataReader()).writer(getDataWriter()).build();
}

我想从 Reader 传递给 Writer 的数据是 CSV 的列名。由于我的阅读器运行可变 SQL 查询(传递 SQL 查询以作为命令行参数运行),因此结果集/列不是静态的,而是根据给定的查询而变化。在 setHeaderCallback 中向 writer 提供要为特定执行写入的列名是从 Reader 向 Writer 发送数据的基本原理。

由于数据的可变性,Reader simple 运行给定的查询并将数据放入 Map<String, Object> 而不是任何 POJO。这里 Map 的键代表列名,而相应的对象保存该列的值。所以本质上我希望作者 setHeaderCallback 能够访问传递的 Map 的键或以某种方式将键从 Reader 传递给 Writer。

编写器代码如下:

public FlatFileItemWriter<Map<String, Object>> getWriter() throws Exception {
        String reportName = getReportName();
        saveToContext(reportName, reportPath);

        FileSystemResource resource = new FileSystemResource(String.join(File.separator, reportPath, getReportName()));

        FlatFileItemWriter<Map<String, Object>> flatFileItemWriter = new FlatFileItemWriter<>();
        flatFileItemWriter.setResource(resource);
//NEED HELP HERE..HOW TO SET THE HEADER TO BE THE KEYS OF THE MAP
//flatFileItemWriter.setHeaderCallback();
        flatFileItemWriter.setLineAggregator(new DelimitedLineAggregator<Map<String, Object>>() {

            {
                setDelimiter(delimiter);
                setFieldExtractor(
                        new PassThroughFieldExtractor<>()
                );
            }
        });
        flatFileItemWriter.afterPropertiesSet();
        return flatFileItemWriter;
    } 
4

1 回答 1

0

这些方法的执行顺序无关紧要。您不应该寻找一种使用执行上下文将数据从读取器传递到写入器的方法,Spring Batch 提供的面向块的 Tasklet 实现将为您做到这一点。

执行上下文可用于将数据从一个步骤传递到另一个步骤,但不能在同一步骤中从读取器传递到写入器。

编辑:根据评论更新答案:

您的问题是您正在调用saveToContext(reportName, reportPath);getWriter 方法。此方法在配置时调用,而不是在运行时调用。

您真正需要的是通过作业参数提供列名,或者通过步骤将它们放入执行上下文中,然后使用配置了这些标题的步骤范围的 Header 回调。

你可以在这里找到一个例子:https ://stackoverflow.com/a/56719077/5019386 。此示例适用于 lineMapper,但您可以对 headerCallback 执行相同操作。如果您不想使用作业参数方法,您可以创建一个 tasklet 步骤来确定列名并将它们放在执行上下文中,然后使用执行上下文中的这些名称配置步骤范围的标头回调,例如:

@Bean
@StepScope
public FlatFileHeaderCallback headerCallback(@Value("#{jobExecutionContext['columnNames']}") String columnNames) {
    return new FlatFileHeaderCallback() {
        @Override
        public void writeHeader(Writer writer) throws IOException {
            // use columnNames here
        }
    };
}
于 2021-05-24T20:20:43.317 回答