同一 Spring 批处理步骤中不同类型的多个编写器
Multiple writers for different types in the same Spring Batch step
我正在使用以下工作流程编写 Spring Batch 应用程序:
- 阅读一些
A
类型的项目(使用 FlatFileItemReader<A>
)。
- 处理一个项目,将其从
A
转换为 B
。
- 写入
B
类型的已处理项(使用 JdbcBatchItemWriter<B>
)
- 最终,我应该调用外部服务(一个RESTful API,但它可能是一个
SimpleMailMessageItemWriter<A>
)使用来自源类型的数据 A
.
如何配置这样的工作流程?
到目前为止,我找到了以下解决方法:
- 配置一个
CompositeItemWriter<B>
委托给:
- 实际
ItemWriter<B>
- 自定义
ItemWriter<B>
实现,将 B
转换回 A
,然后写入 A
但这是一个麻烦的解决方案,因为它迫使我:
- 重复处理逻辑:从
A
到 B
然后再返回。
- 在
B
中偷偷隐藏源对象 A
的一些属性,污染域模型。
注意:由于我的 A
自定义项目编写器需要调用外部服务,因此我想在 B
完成后执行此操作成功写入。
这里是批量配置代码的相关部分。
@Bean
public Step step(StepBuilderFactory steps, ItemReader<A> reader, ItemProcessor<A, B> processor, CompositeItemWriter<B> writer) {
return steps.get("step")
.<A, B>chunk(10)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}
@Bean
public CompositeItemWriter<B> writer(JdbcBatchItemWriter<B> jdbcBatchItemWriter, CustomItemWriter<B, A> customItemWriter) {
return new CompositeItemWriterBuilder<B>()
.delegates(jdbcBatchItemWriter, customItemWriter)
.build();
}
对于您的用例,我会将 A 和 B 封装在包装器类型中,例如 AB:
class AB {
private A originalItem;
private B transformedItem;
}
这样,您将拥有:ItemReader<A>
、ItemProcessor<A, AB>
和 ItemWriter<AB>
。处理器创建 AB 实例,其中保留对原始项目的引用。然后作者可以访问这两种类型并根据需要委托给 JdbcBatchItemReader<B>
和 SimpleMailMessageItemWriter<A>
,例如:
class ABItemWriter implements ItemWriter<AB> {
private JdbcBatchItemWriter<B> jdbcBatchItemWriter;
private SimpleMailMessageItemWriter mailMessageItemWriter;
// constructor with delegates
@Override
public void write(List<? extends AB> items) throws Exception {
jdbcBatchItemWriter.write(getBs(items));
mailMessageItemWriter.write(getAs(items)); // this would not be called if the jdbc writer fails
}
}
方法 getAs
和 getBs
将从 AB 中提取类型 A/B 的项目。封装为赢!顺便说一句,Java 记录是 AB 类型的不错选择。
我正在使用以下工作流程编写 Spring Batch 应用程序:
- 阅读一些
A
类型的项目(使用FlatFileItemReader<A>
)。 - 处理一个项目,将其从
A
转换为B
。 - 写入
B
类型的已处理项(使用JdbcBatchItemWriter<B>
) - 最终,我应该调用外部服务(一个RESTful API,但它可能是一个
SimpleMailMessageItemWriter<A>
)使用来自源类型的数据A
.
如何配置这样的工作流程?
到目前为止,我找到了以下解决方法:
- 配置一个
CompositeItemWriter<B>
委托给:- 实际
ItemWriter<B>
- 自定义
ItemWriter<B>
实现,将B
转换回A
,然后写入A
- 实际
但这是一个麻烦的解决方案,因为它迫使我:
- 重复处理逻辑:从
A
到B
然后再返回。 - 在
B
中偷偷隐藏源对象A
的一些属性,污染域模型。
注意:由于我的 A
自定义项目编写器需要调用外部服务,因此我想在 B
完成后执行此操作成功写入。
这里是批量配置代码的相关部分。
@Bean
public Step step(StepBuilderFactory steps, ItemReader<A> reader, ItemProcessor<A, B> processor, CompositeItemWriter<B> writer) {
return steps.get("step")
.<A, B>chunk(10)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}
@Bean
public CompositeItemWriter<B> writer(JdbcBatchItemWriter<B> jdbcBatchItemWriter, CustomItemWriter<B, A> customItemWriter) {
return new CompositeItemWriterBuilder<B>()
.delegates(jdbcBatchItemWriter, customItemWriter)
.build();
}
对于您的用例,我会将 A 和 B 封装在包装器类型中,例如 AB:
class AB {
private A originalItem;
private B transformedItem;
}
这样,您将拥有:ItemReader<A>
、ItemProcessor<A, AB>
和 ItemWriter<AB>
。处理器创建 AB 实例,其中保留对原始项目的引用。然后作者可以访问这两种类型并根据需要委托给 JdbcBatchItemReader<B>
和 SimpleMailMessageItemWriter<A>
,例如:
class ABItemWriter implements ItemWriter<AB> {
private JdbcBatchItemWriter<B> jdbcBatchItemWriter;
private SimpleMailMessageItemWriter mailMessageItemWriter;
// constructor with delegates
@Override
public void write(List<? extends AB> items) throws Exception {
jdbcBatchItemWriter.write(getBs(items));
mailMessageItemWriter.write(getAs(items)); // this would not be called if the jdbc writer fails
}
}
方法 getAs
和 getBs
将从 AB 中提取类型 A/B 的项目。封装为赢!顺便说一句,Java 记录是 AB 类型的不错选择。