Spring Batch 多写:分类写与组合写
在本篇文章中,我们将实现 Spring Batch 的多写(Multi-Write),分别介绍 ClassifierCompositeItemWriter
和 CompositeItemWriter
两种方式,并提供示例代码。下一篇文章中,我们将为这些实现补充单元测试。
注意 :项目中不要忘记添加
@EnableBatchProcessing
注解,否则 Spring Batch 可能无法正常工作。
1 多写方式简介
1.1 分类写(ClassifierCompositeItemWriter)
ClassifierCompositeItemWriter
允许根据不同的分类器(Classifier)将数据分派到不同的 ItemWriter
。
适用场景:
- 例如账务系统需要将不同渠道的交易数据输出到不同的平台,以便对账。
关键点:
- 通过
Classifier
进行分类,将数据映射到不同的Writer
。 - 若数据未匹配到特定
Writer
,则使用默认Writer
进行处理。 - 通过
CompositeItemStream
让 Spring 管理 Bean,避免writer must be open before it can be written to
相关错误。 writerMap
通过数据库动态获取渠道列表,实现可扩展性,新增渠道无需修改代码。
1.2 组合写(CompositeItemWriter)
CompositeItemWriter
允许将数据同时输出到多个 ItemWriter
。
适用场景:
- 例如在数据同步场景下,需要同时写入多个目标位置。
关键点:
CompositeItemWriter
可以同时委派多个ItemWriter
,实现数据同步写入多个目标。- 由于
CompositeItemWriter
实现了ItemStream
接口,无需在Step
中额外注册Stream
。
2 示例代码
2.1 分类写示例(ClassifierCompositeItemWriter)
java
@Configuration
public class MultiWriteJobConfig {
public static final String DEFAULT = "default";
@Bean
@StepScope
public JdbcPagingItemReader<OrderEntity> myReader() {
JdbcPagingItemReaderBuilder<OrderEntity> reader = new JdbcPagingItemReaderBuilder<>();
reader.pageSize(100);
reader.rowMapper(new BeanPropertyRowMapper<>(OrderEntity.class));
MySqlPagingQueryProvider provider = new MySqlPagingQueryProvider();
provider.setSelectClause("id, channel_id, order_no, amount");
provider.setFromClause("from order_table");
provider.setSortKeys(Collections.singletonMap("id", Order.ASCENDING));
reader.queryProvider(provider);
return reader.build();
}
@Bean
@StepScope
public ClassifierCompositeItemWriter<OrderEntity> classifierCompositeItemWriter(
Classifier<OrderEntity, ItemWriter<? super OrderEntity>> classifier) {
ClassifierCompositeItemWriter<OrderEntity> writer = new ClassifierCompositeItemWriter<>();
writer.setClassifier(classifier);
return writer;
}
@Bean
@StepScope
public Classifier<OrderEntity, ItemWriter<? super OrderEntity>> classifier(Map<String, FlatFileItemWriter<OrderEntity>> writerMap) {
return orderEntity -> writerMap.getOrDefault(orderEntity.getChannelId(), writerMap.get(DEFAULT));
}
@Bean
@StepScope
public Map<String, FlatFileItemWriter<OrderEntity>> writerMap() {
Map<String, FlatFileItemWriter<OrderEntity>> writerMap = new HashMap<>();
getAllChannelIdsFromDB().forEach(channelId -> writerMap.put(channelId, createWriter(channelId)));
writerMap.put(DEFAULT, createWriter(DEFAULT));
return writerMap;
}
@Bean
@StepScope
public CompositeItemStream compositeItemStream(Map<String, FlatFileItemWriter<OrderEntity>> writerMap) {
CompositeItemStream compositeItemStream = new CompositeItemStream();
compositeItemStream.setStreams(writerMap.values().toArray(new ItemStream[0]));
return compositeItemStream;
}
@Bean
public Step step1(StepBuilderFactory stepBuilderFactory, JdbcPagingItemReader<OrderEntity> myReader,
ClassifierCompositeItemWriter<OrderEntity> classifierCompositeItemWriter,
CompositeItemStream compositeItemStream) {
return stepBuilderFactory.get("step1")
.<OrderEntity, OrderEntity>chunk(100)
.reader(myReader)
.writer(classifierCompositeItemWriter)
.stream(compositeItemStream)
.build();
}
@Bean
public Job job1(JobBuilderFactory jobBuilderFactory, Step step1) {
return jobBuilderFactory.get("job1")
.incrementer(new RunIdIncrementer())
.start(step1)
.build();
}
private FlatFileItemWriter<OrderEntity> createWriter(String channelId) {
return new FlatFileItemWriterBuilder<OrderEntity>()
.name("orderItemWriter")
.resource(new FileSystemResource("E:\\output\\orders_" + channelId + ".csv"))
.delimited()
.delimiter(",")
.names("id", "channelId", "amount")
.build();
}
private List<String> getAllChannelIdsFromDB() {
return Arrays.asList("WEIXIN", "ZHIFUBAO");
}
}
2.2 组合写示例(CompositeItemWriter)
java
@Configuration
public class MultiWriteJobConfig {
@Bean
@StepScope
public CompositeItemWriter<OrderEntity> compositeItemWriter() {
CompositeItemWriter<OrderEntity> writer = new CompositeItemWriter<>();
writer.setDelegates(getAllChannelIdsFromDB().stream()
.map(this::createWriter)
.collect(Collectors.toList()));
return writer;
}
@Bean
public Step step2(StepBuilderFactory stepBuilderFactory, JdbcPagingItemReader<OrderEntity> myReader,
CompositeItemWriter<OrderEntity> compositeItemWriter) {
return stepBuilderFactory.get("step2")
.<OrderEntity, OrderEntity>chunk(100)
.reader(myReader)
.writer(compositeItemWriter)
.build();
}
@Bean
public Job job2(JobBuilderFactory jobBuilderFactory, Step step2) {
return jobBuilderFactory.get("job2")
.incrementer(new RunIdIncrementer())
.start(step2)
.build();
}
}
下一篇文章,我们将基于Junit实现Job的单元测试。