Spring Batch 多写:分类写与组合写

Spring Batch 多写:分类写与组合写

在本篇文章中,我们将实现 Spring Batch 的多写(Multi-Write),分别介绍 ClassifierCompositeItemWriterCompositeItemWriter 两种方式,并提供示例代码。下一篇文章中,我们将为这些实现补充单元测试。

注意 :项目中不要忘记添加 @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的单元测试。

相关推荐
二两小咸鱼儿23 分钟前
Java Demo - JUnit :Unit Test(Assert Methods)
java·后端·junit
字节源流27 分钟前
【spring】配置类和整合Junit
java·后端·spring
武昌库里写JAVA2 小时前
原生iOS集成react-native (react-native 0.65+)
vue.js·spring boot·毕业设计·layui·课程设计
m0_748230942 小时前
Spring Boot 整合 Redis 步骤详解
spring boot·redis·bootstrap
zhuyasen2 小时前
Go语言配置解析:基于viper的conf库优雅解析配置文件
后端·go
2a3b4c2 小时前
读取 Resource 目录下文件内容
后端
Asthenia04122 小时前
NIO:Buffer对象均是在Jvm堆中分配么?听说过DirectByteBuffer和MappedByteBuffer么?
后端
m0_748240023 小时前
Rust与Cargo版本关系(Rust版本、rustc、rustup)
开发语言·后端·rust
灵山悟空3 小时前
rust语言闭包trait类型编译器推导总结
开发语言·后端·rust
老胖闲聊3 小时前
Flask 全栈学习指南
后端·python·flask