Java spring批处理中内存目标中步骤作用域的实现

Java spring批处理中内存目标中步骤作用域的实现,java,spring,java-8,spring-batch,batch-processing,Java,Spring,Java 8,Spring Batch,Batch Processing,当使用SpringBatch创建一个二进制格式的大型报告时,我有一个要求,该报告保存在数据库中。任何工作数据都不能直接写入文件或JobExecutionContext之外的工作表 我知道,通常情况下,您只需编写作业执行上下文,但对于如何使用如此大的报告(可能是几百兆字节),我有点困惑 目前,我的Writer实现依赖于聚合器类,聚合器类作为bean注入,然后有一个TaskLet注入聚合器,将完成的报告写入数据库 问题是,我无法将聚合器的范围限定到步骤上下文,因此,如果两个作业同时运行,它们将写入同

当使用SpringBatch创建一个二进制格式的大型报告时,我有一个要求,该报告保存在数据库中。任何工作数据都不能直接写入文件或
JobExecutionContext
之外的工作表

我知道,通常情况下,您只需编写作业执行上下文,但对于如何使用如此大的报告(可能是几百兆字节),我有点困惑

目前,我的
Writer
实现依赖于聚合器类,聚合器类作为bean注入,然后有一个TaskLet注入聚合器,将完成的报告写入数据库

问题是,我无法将聚合器的范围限定到
步骤
上下文,因此,如果两个作业同时运行,它们将写入同一聚合器

这是我当前的实现

域类 作家 上下文 这是因为你只需要在步骤中确定某些事情的范围


我如何使用执行上下文作为我的数据块的接收器,记住它们将有很多,而且它们将非常大?

我已经设法解决了这个问题。虽然不是很好,但它符合我的要求

从本质上讲,有太多的数据要输入和输出上下文。解决方案是在编写器本身中维护状态,并使其成为
步骤执行侦听器
,通过
事务回调
步骤
结束时保存

更新的
编写器
公共类FooWriter扩展StepExecutionListenerSupport实现ItemWriter{
私有字符串reportId;
私有映射byteArrayMap=新的ConcurrentSkipListMap();
私有事务模板TransactionTemplate;
私有报告存储库;
@凌驾
公共同步无效写入(列表)
public class DataChunk {
    private int pageNumber;
    private byte[] data;
}
public class FooWriter implements ItemWriter<DataChunk> {

    private DataChunkAggregator dataChunkAggregator;

    public void write(List<? extends DataChunk> dataChunks) throws Exception {
        dataChunks.stream().forEach(chunk -> dataChunkAggregator.addChunk(chunk.getPageNumber(), chunk.getData()));
    }
}
public class FooAggregator {
    private Map<int, byte> pagedData; // Key sorted implementation

    public void addChunk(int pageNumber, byte[] data) {
        pagedData.put(pageNumber, data)
    }

    public byte[] aggregate() {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        pagedData.values.stream().forEach(data -> baos.write(data));
        return baos.toByteArray();
    }
}
public class ReportWritingTasklet implements TaskLet {

    private ReportRepository reportRepository;
    private FooAggregator fooAggregator;

    public RepeatStatus execute(StepContribution contribution, ChunkContext context) {
        byte[] data = fooAggregator.aggregate();
        reportRepository.getOne(reportId).setDataBytes(data);
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id=fooWriter class="FooWriter" scope="step"
        p:fooAggregator-ref="fooAggregator"/>

    <bean id="fooAggregator" class="FooAggregator"/>

    <bean id="reportWritingTasklet" class="ReportWritingTasklet" scope="step"
        p:fooAggregator-ref="fooAggregator"/>

    <batch:job id="fooJob">
        <batch:step id="generateReport" next="assembleReport">
            <batch:chunk reader="fooReader" processor="fooProcessor" writer="fooWriter"/>
        </batch:step>
        <batch:step id="assembleReport">
            <batch:tasklet class="ReportWritingTasklet"/>
        </batch:step>
     </batch:job>
</beans>
Caused by: java.lang.IllegalStateException: Cannot convert value of type    [com.sun.proxy.$Proxy98 implementing org.springframework.aop.scope.ScopedObject,java.io.Serializable,org.springframework.aop.framework.AopInfrastructureBean,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised] to required type [FooAggregator] for property 'fooAggregator': no matching editors or conversion strategy found
public class FooWriter extends StepExecutionListenerSupport implements ItemWriter<DataChunk> {

    private String reportId;
    private Map<Integer, byte[]> byteArrayMap = new ConcurrentSkipListMap<>();

    private TransactionTemplate transactionTemplate;
    private ReportRepository reportRepository;

    @Override
    public synchronized void write(List<? extends CaseChunk> caseChunks) throws Exception {
        caseChunks.stream().forEach(chunk -> {
            byteArrayMap.put(chunk.getPageNumber(), chunk.getBytes());
        });
    }

    @Override
    public void beforeStep(StepExecution stepExecution) {
        // No-op
    }

    @Override
    public ExitStatus afterStep(StepExecution stepExecution) {
        StringBuilder sb = new StringBuilder();
        for (byte[] byteArrayOutputStream : byteArrayMap.values()) {
            sb.append(new String(Base64.decode(byteArrayOutputStream)));
        }
        String encodedReportData = new String(Base64.encode(sb.toString().getBytes()));

        TransactionCallback<Report> transactionCallback = transactionStatus -> {
            Report report = reportRepository.getOne(this.reportId);
            report.setReportData(encodedReportData);
            reportRepository.save(report);
            return report;
        };

        // TransactionTemplate throws its own declared TransactionException, rethrows encountered RuntimeExceptions
        // and also Errors. Any problem writing the date kills the job, so it's OK to catch Throwable here instead
        // of trying to
        try {
            transactionTemplate.execute(transactionCallback);
        } catch (Throwable t) {
            LOGGER.error("Error saving report data ID:[{}]", reportId);
            return ExitStatus.FAILED.addExitDescription(t);
        }
        return ExitStatus.COMPLETED;
    }

}