LoginSignup
1
1

More than 3 years have passed since last update.

Spring Batchでstepをループ

Posted at

イメージとしてはstepをfor文でループしたい。これをspring-batchのjob定義でどのように行うのか、について。

ソースコード

pom.xml

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>

    <groupId>kagami</groupId>
    <artifactId>springbatchsample</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>springbatchsample</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version><!-- https://qiita.com/kagamihoge/items/fbfe90837192fd88fee9 -->
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-batch</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.batch</groupId>
            <artifactId>spring-batch-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

App

package kagami.springbatchsample.deciders;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
import org.springframework.batch.core.job.flow.JobExecutionDecider;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@EnableBatchProcessing
@SpringBootApplication
public class App {

  @Bean
  public Job job(JobBuilderFactory jobs, @Qualifier("s1") Step s1, JobExecutionDecider decider) {
    return jobs
        .get("myJob")
        .incrementer(new RunIdIncrementer())
        .start(s1)
        .next(decider)
          .on("COMPLETED").end()
          .on("CONTINUE").to(s1)
         .end()
        .build();
  }

  @Bean(name = "s1")
  public Step step1(StepBuilderFactory steps) {
    return steps.get("step1").tasklet((stepContribution, chunkContext) -> {
      System.out.println("step 1");
      return RepeatStatus.FINISHED;
    }).build();
  }

  @Bean
  public JobExecutionDecider decider() {
    return new JobExecutionDecider() {
      int count = 0;
      @Override
      public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
        if (++count >= 5) {
          return new FlowExecutionStatus("COMPLETED");
        } else {
          return new FlowExecutionStatus("CONTINUE");
        }
      }
    };
  }

  public static void main(String[] args) {
    SpringApplication.run(App.class, args);
  }

}

spring-batchにはJobExecutionDeciderを使用することで、次に実行するstepを決定するためのロジックを実装できる。上のサンプルコードではcountがそのロジックに相当する。JobExecutionDeciderこちらを参考に実装しました。

次に、job定義でJobExecutionDeciderの実装を使用する。上のサンプルコードでは、JobExecutionDeciderの戻り値が"COMPLETED"であれば終了、"CONTINUE"であればstepを実行、としている。これによって、countの条件を満たすまでstepが繰り返し実行される。

以下は実行時のログ。

2019-06-20 11:50:20.305  INFO 10084 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
step 1
2019-06-20 11:50:20.347  INFO 10084 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Duplicate step [step1] detected in execution of job=[myJob]. If either step fails, both will be executed again on restart.
2019-06-20 11:50:20.349  INFO 10084 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
step 1
2019-06-20 11:50:20.356  INFO 10084 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Duplicate step [step1] detected in execution of job=[myJob]. If either step fails, both will be executed again on restart.
2019-06-20 11:50:20.358  INFO 10084 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
step 1
2019-06-20 11:50:20.366  INFO 10084 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Duplicate step [step1] detected in execution of job=[myJob]. If either step fails, both will be executed again on restart.
2019-06-20 11:50:20.368  INFO 10084 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
step 1
2019-06-20 11:50:20.375  INFO 10084 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Duplicate step [step1] detected in execution of job=[myJob]. If either step fails, both will be executed again on restart.
2019-06-20 11:50:20.377  INFO 10084 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
step 1

Duplicate step [step1] detected ...について。ログにあるように、同一のstepが単一のjobで複数使われており、どちらかのstepが失敗するとリスタート時に両方とも実行される、とある。こういう現象になるのでリスタート時には考慮してね、という意味合いと思われる。

参考文献

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1