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的单元测试。

相关推荐
晨非辰11 分钟前
C++波澜壮阔40年|类和对象篇:拷贝构造与赋值重载的演进与实现
运维·开发语言·c++·人工智能·后端·python·深度学习
未来龙皇小蓝12 分钟前
策略模式:Spring Bean策略与枚举 Lambda策略
java·windows·spring boot·spring·策略模式
张乔2431 分钟前
spring boot项目中设置默认的方法实现
java·数据库·spring boot
小北方城市网33 分钟前
SpringBoot 集成 Redis 实战(缓存与分布式锁):提升系统性能与并发能力
spring boot·python·rabbitmq·java-rabbitmq·数据库架构
韩立学长40 分钟前
基于Springboot琴行学生课程信息管理系统2gt392wb(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
数据库·spring boot·后端
XXOOXRT1 小时前
基于SpringBoot的用户登录
java·spring boot·后端
齐 飞1 小时前
springboot整合shardingsphere-jdbc5.1.1-按月分表
数据库·spring boot
努力也学不会java1 小时前
【Spring Cloud】环境和工程基本搭建
java·人工智能·后端·spring·spring cloud·容器
源代码•宸1 小时前
Golang原理剖析(interface)
服务器·开发语言·后端·golang·interface·type·itab
burning_maple1 小时前
设计数据密集型应用阅读笔记
分布式·后端·中间件