Spring Batch使用

356 阅读5分钟

介绍

Spring Batch 是一个轻量级、全面的批处理框架。Spring Batch 不是一个调度框架。在商业和开源领域都有许多优秀的企业调度程序(例如 Quartz、Tivoli、Control-M 等)。Spring Batch 旨在与调度程序结合使用,而不是替代调度程序。
Spring Batch 提供了处理大量记录所必需的可重用功能,包括日志记录和跟踪、事务管理、作业处理统计、作业重启、跳过和资源管理。它还提供更高级的技术服务和功能,可通过优化和分区技术实现极高容量和高性能的批处理作业。您可以在简单的用例(例如将文件读入数据库或运行存储过程)和复杂的大容量用例(例如在数据库之间移动大量数据、转换数据等)中使用 Spring Batch . 大批量批处理作业可以以高度可扩展的方式使用该框架来处理大量信息。

主要功能

  • Transaction management(事务管理)
  • Chunk based processing(基于块的处理)
  • Declarative I/O(声明式的输入输出)
  • Start/Stop/Restart(启动/停止/再启动)
  • Retry/Skip(重试/跳过)

使用场景

  • 从数据库、文件或队列中读取大量记录。
  • 以某种方式处理数据。
  • 以修改后的形式写回数据。

四大角色

  1. JobLauncher:任务启动器 ,是batch程序的入口,通过它来启动任务。
  2. Job:表示一个具体的任务
  3. Step:表示一个具体的步骤,一个Job可以包含一个Step,也可以包含多个Step,由任务启动器JobLauncher进行启动。任务的具体执行内容,一个Step的执行过程包括读数据(ItemReader)、处理数据(ItemProcessor)、写数据(ItemWriter)。
  4. JobRepository:表示存储数据的地方,可以看做是一个数据库的接口,在任务执行的时候需要通过它记录任务状态等等信息。

spring-batch-reference-model.png

搭建工程

maven依赖

包括springboot整合的batch启动器starter,mysql驱动等。
注:springbatch会自动创建一些表,本次接入的mysql,还可以支持oracle等其他数据库,在后续application.yaml文件中需要配置对应的脚本文件路径。

<!--  SpringBatch依赖 -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.1.0</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

yaml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://ip:3306/batch?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
    username: root
    password: password
    # 指定数据库表的位置
    schema: classpath:/org/springframework/batch/core/schema-mysql.sql
  # 启动时总是创建表,创建完后可改为never
  batch:
    initialize-schema: always

# mybatis-plus相关配置
mybatis-plus:
  # xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)
  mapper-locations: classpath:mapper/*.xml
  # 以下配置均有默认值,可以不设置
  global-config:
    db-config:
      #主键类型 AUTO:"数据库ID自增" INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
      id-type: auto
      #字段策略 IGNORED:"忽略判断"  NOT_NULL:"非 NULL 判断")  NOT_EMPTY:"非空判断"
      field-strategy: NOT_EMPTY
      #数据库类型
      db-type: MYSQL
  configuration:
    # 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射
    map-underscore-to-camel-case: true
    # 如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段
    call-setters-on-nulls: true
    # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

启动类

@SpringBootApplication
@EnableBatchProcessing
public class SpringBatchApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBatchApplication.class,args);
    }
}

测试类

@Configuration
public class BatchTestJob {

    //任务工厂
    private final JobBuilderFactory jobBuilderFactory;
    //步骤工厂
    private final StepBuilderFactory stepBuilderFactory;

    public BatchTestJob(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory) {
        this.jobBuilderFactory = jobBuilderFactory;
        this.stepBuilderFactory = stepBuilderFactory;
    }

    @Bean
    public Job job1() {
        //创建一个名为MyBatchTestJob的job,并将step1放入这个任务中
        return jobBuilderFactory.get("MyBatchTestJob")
                //执行step
                .start(step1()).build();
    }

    @Bean
    public Step step1() {
        //创建一个名为MyBatchTestStep1的step
        return stepBuilderFactory.get("MyBatchTestStep1").tasklet((stepContribution, chunkContext) -> {
            System.out.println("Test OK!");
            // 返回本次step的执行状态
            return RepeatStatus.FINISHED;
        }).build();
    }
}
执行结果

image.png
image.png

  1. BATCH_JOB_INSTANCE:BATCH_JOB_INSTANCE表包含与JobInstance相关的所有信息。
  2. BATCH_JOB_EXECUTION_PARAMS:BATCH_JOB_EXECUTION_PARAMS表包含与JobParameters对象相关的所有信息。
  3. BATCH_JOB_EXECUTION:BATCH_JOB_EXECUTION表保存与JobExecution对象相关的数据。每次运行作业时都会添加一个新行。
  4. BATCH_STEP_EXECUTION:BATCH_STEP_EXECUTION表包含与StepExecution对象相关的所有信息。
  5. BATCH_JOB_EXECUTION_CONTEXT:BATCH_JOB_EXECUTION_CONTEXT表保存与Job的ExecutionContext相关的数据。每个JobExecution都只有一个Job ExecutionContext,它包含该特定作业执行所需的所有作业级数据。此数据通常表示在发生故障后必须检索的状态,以便JobInstance可以从失败的位置重新启动。
  6. BATCH_STEP_EXECUTION_CONTEXT:BATCH_STEP_EXECUTION_CONTEXT表保存与Step的ExecutionContext相关的数据。每个StepExecution都只有一个ExecutionContext,它包含了特定步骤执行所需的所有数据。此数据通常表示在发生故障后必须检索的状态,以便JobInstance可以从失败的位置重新启动。
  7. BATCH_JOB_EXECUTION_SEQ:该表保存作业的数据执行顺序。
  8. BATCH_STEP_EXECUTION_SEQ:此表保存步骤执行序列的数据。
  9. BATCH_JOB_SEQ:此表保存作业序列的数据,以防我们有多个作业,我们将得到多行。

多步骤执行

@Configuration
public class BatchTestMultiJob {

    //任务工厂
    private final JobBuilderFactory jobBuilderFactory;
    //步骤工厂
    private final StepBuilderFactory stepBuilderFactory;

    public BatchTestMultiJob(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory) {
        this.jobBuilderFactory = jobBuilderFactory;
        this.stepBuilderFactory = stepBuilderFactory;
    }

    @Bean
    public Job multiJob1() {
        //创建一个名为BatchTestMultiJob的job,multiStep1,2,3放入这个任务中
        return jobBuilderFactory.get("BatchTestMultiJob")
                //执行step1,2,3
                .start(multiStep1())
                .next(multiStep2())
                .next(multiStep3())
                .build();
    }

    @Bean
    public Step multiStep1() {
        //创建一个名为multiStep1的step
        return stepBuilderFactory.get("multiStep1").tasklet((stepContribution, chunkContext) -> {
            System.out.println("step1 OK!");
            // 返回本次step的执行状态
            return RepeatStatus.FINISHED;
        }).build();
    }

    @Bean
    public Step multiStep2() {
        //创建一个名为multiStep2的step
        return stepBuilderFactory.get("multiStep2").tasklet((stepContribution, chunkContext) -> {
            System.out.println("step2 OK!");
            // 返回本次step的执行状态
            return RepeatStatus.FINISHED;
        }).build();
    }

    @Bean
    public Step multiStep3() {
        //创建一个名为multiStep3的step
        return stepBuilderFactory.get("multiStep3").tasklet((stepContribution, chunkContext) -> {
            System.out.println("step3 OK!");
            // 返回本次step的执行状态
            return RepeatStatus.FINISHED;
        }).build();
    }
}
执行结果

依次输出step1,step2,step3
image.png