CLOVERšŸ€

That was when it all began.

Spring Batch Ɨ Spring Integrationļ¼ˆSpring Batch Integrationļ¼‰ć‚’試恙

恓悌ćÆ态ćŖć«ć‚’ć—ćŸćć¦ę›øć„ćŸć‚‚ć®ļ¼Ÿ

Spring Batchć®ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆć‚’č¦‹ć¦ć„ć¦ć€Spring IntegrationćØēµ„ćæåˆć‚ć›ć‚‰ć‚Œćć†ć ć£ćŸć®ć§ć€ć”ć‚‡ć£ćØ試恗恦ćæ悈恆恋ćŖćØ怂

Spring Batch Integration

Spring BatchćØ态Spring Integration恮ēµ„ćæåˆć‚ć›ć«ć¤ć„ć¦ćÆ态恓恔悉恫čØ˜č¼‰ćŒć‚ć‚Šć¾ć™ć€‚

Spring Batch Integration

Spring Batch IntegrationćØå‘¼ć¶ć‚ˆć†ć§ć™ć€‚

Spring Batchć®ćƒ¦ćƒ¼ć‚¶ćƒ¼ć€Spring Integrationć®ćƒ¦ćƒ¼ć‚¶ćƒ¼ćØć‚‚ć«ć€åŒę–¹ć‚’åˆć‚ć›ć¦ä½æ恆恓ćØć§č¦ä»¶ć‚’ć‚ˆć‚ŠåŠ¹ēŽ‡ēš„ć«å®Ÿē¾ć§ćć‚‹ć‚±ćƒ¼ć‚¹ć«
遭遇恙悋åÆčƒ½ę€§ćŒć‚ć‚‹ć€ćØć„ć†č©±ć®ć‚ˆć†ć§ć™ć€‚

Many users of Spring Batch may encounter requirements that are outside the scope of Spring Batch but that may be efficiently and concisely implemented by using Spring Integration. Conversely, Spring Integration users may encounter Spring Batch requirements and need a way to efficiently integrate both frameworks.

Spring Batch Integration / Spring Batch Integration Introduction

ćƒćƒƒćƒćƒ—ćƒ­ć‚»ć‚¹ć«ćƒ”ćƒƒć‚»ćƒ¼ć‚øćƒ³ć‚°ć‚’čæ½åŠ ć™ć‚‹ć“ćØ恧态ć‚Ŗćƒšćƒ¬ćƒ¼ć‚·ćƒ§ćƒ³ć®č‡Ŗ動化ćØć€ć‚­ćƒ¼ćØćŖ悋ę‡øåæµäŗ‹é …ć®åˆ†é›¢ćŒåÆčƒ½ć«ćŖć‚Šć¾ć™ć€‚

Adding messaging to a batch process enables automation of operations and also separation and strategizing of key concerns.

ć–ć£ćć‚ŠčØ€ć†ćØć€ćƒ”ćƒƒć‚»ćƒ¼ć‚øćƒ³ć‚°ć®ä»•ēµ„ćæ恋悉Spring Batch恮Jobć‚’čµ·å‹•ć™ć‚‹ćŸć‚ć®ć‚‚ć®ć€ćæ恟恄恧恙怂
ć¾ćŸć€ćƒ”ćƒƒć‚»ćƒ¼ć‚øćƒ³ć‚°ć®ä»•ēµ„ćæ悒ć‚øćƒ§ćƒ–ć«åŸ‹ć‚č¾¼ćæ态ćƒÆćƒ¼ć‚Æćƒ­ćƒ¼ćƒ‰ć‚’č¤‡ę•°ć®ćƒÆćƒ¼ć‚«ćƒ¼ļ¼ˆćƒŖćƒ¢ćƒ¼ćƒˆćƒ‘ćƒ¼ćƒ†ć‚£ć‚·ćƒ§ćƒ‹ćƒ³ć‚°ć€ćƒŖćƒ¢ćƒ¼ćƒˆćƒćƒ£ćƒ³ć‚Æļ¼‰ć«
åˆ†ę•£ć—ćŸć‚Šć‚‚ć§ćć‚‹ć‚ˆć†ć§ć™ć€‚

ćƒ”ćƒƒć‚»ćƒ¼ć‚øćƒ³ć‚°ć‚’ä½æć£ćŸSpring Batchć®čµ·å‹•ć«ć¤ć„ć¦ć€‚

Spring Batch Integration / Spring Batch Integration Introduction / Launching Batch Jobs through Messages

Spring Batch悒CommandLineJobRunner悒ä½æć£ć¦čµ·å‹•ć—ćŸć‚Šć€Webć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć«ćŠć„ć¦JobOperator悒ē›“ꎄꉱ恆ä½æć„ę–¹ć‚‚
ć‚ć‚Šć¾ć™ćŒć€ć‚ˆć‚Šč¤‡é›‘ćŖćƒ¦ćƒ¼ć‚¹ć‚±ćƒ¼ć‚¹ć«ć¤ć„ć¦ęŒ™ć’ć¦ćæć¾ć™ć€‚

惐惃惁ć‚øćƒ§ćƒ–ć®ćƒ‡ćƒ¼ć‚æć‚’å–å¾—ć™ć‚‹ćŸć‚ć«ć€ćƒŖćƒ¢ćƒ¼ćƒˆć®FTP悄SFTPć‚µćƒ¼ćƒćƒ¼ćøć®ćƒćƒ¼ćƒŖćƒ³ć‚°ć‚’č”Œć†åæ…č¦ćŒć‚ć£ćŸć‚Šć€č¤‡ę•°ć®ē•°ćŖ悋
ćƒ‡ćƒ¼ć‚æć‚½ćƒ¼ć‚¹ć‚’ć‚µćƒćƒ¼ćƒˆć™ć‚‹åæ…č¦ćŒć‚ć‚‹ć‹ć‚‚ć—ć‚Œć¾ć›ć‚“ć€‚

Maybe you need to poll a remote (S)FTP server to retrieve the data for the Batch Job or your application has to support multiple different data sources simultaneously.

Web恠恑恧ćŖ恏态FTPćŖć©ć‹ć‚‰ć‚½ćƒ¼ć‚¹ćØćŖć‚‹ćƒ‡ćƒ¼ć‚æćƒ•ć‚”ć‚¤ćƒ«ć‚’å—ć‘å–ć‚‹ć‹ć‚‚ć—ć‚Œć¾ć›ć‚“ć€‚Spring Batchć‚’å‘¼ć³å‡ŗć™å‰ć«ć€å…„åŠ›ćƒ•ć‚”ć‚¤ćƒ«ć®
å¤‰ę›ćŒåæ…要恫ćŖć‚‹å “åˆć‚‚ć‚ć‚‹ć‹ć‚‚ć—ć‚Œć¾ć›ć‚“ć€‚

For example, you may receive data files not only from the web, but also from FTP and other sources. Maybe additional transformation of the input files is needed before invoking Spring Batch.

ć“ć®ć‚ˆć†ćŖ堓合ćÆ态Spring IntegrationćØćć®å¤šę•°ć®ć‚¢ćƒ€ćƒ—ć‚æćƒ¼ć‚’ä½æć£ć¦ćƒćƒƒćƒć‚øćƒ§ćƒ–ć‚’å®Ÿč”Œć™ć‚‹ćØć€ć‚ˆć‚Šå¼·åŠ›ćŖ仕ēµ„ćæ恫ćŖ悋恧恗悇恆怂

Therefore, it would be much more powerful to execute the batch job using Spring Integration and its numerous adapters.

恟ćØ恈恰态File Inbound Channel Adapter悒ä½æē”Øć—ć¦ćƒ•ć‚”ć‚¤ćƒ«ć‚·ć‚¹ćƒ†ćƒ å†…ć®ćƒ‡ć‚£ćƒ¬ć‚Æ惈ćƒŖ悒ē›£č¦–ć—ć€å…„åŠ›ćƒ•ć‚”ć‚¤ćƒ«ćŒåˆ°ē€ć—ćŸć‚‰ć™ćć«
惐惃惁ć‚øćƒ§ćƒ–ć‚’é–‹å§‹ć™ć‚‹ć“ćØćŒć§ćć¾ć™ć€‚ć•ć‚‰ć«ć€č¤‡ę•°ć®ē•°ćŖć‚‹ć‚¢ćƒ€ćƒ—ć‚æćƒ¼ć‚’ä½æē”Ø恗恦Spring Integrationć®ćƒ•ćƒ­ćƒ¼ć‚’ä½œęˆć—ć€
Configuration恮ćæć§č¤‡ę•°ć®ć‚½ćƒ¼ć‚¹ć‹ć‚‰åŒę™‚ć«ćƒćƒƒćƒć‚øćƒ§ćƒ–ć®ćƒ‡ćƒ¼ć‚æć‚’å–ć‚Šč¾¼ć‚€ć“ćØć‚‚ć§ćć¾ć™ć€‚

For example, you can use a File Inbound Channel Adapter to monitor a directory in the file-system and start the Batch Job as soon as the input file arrives. Additionally, you can create Spring Integration flows that use multiple different adapters to easily ingest data for your batch jobs from multiple sources simultaneously using only configuration.

恓恮仕ēµ„ćæć‚’å®Ÿē¾ć™ć‚‹ćŸć‚ć«ć€Spring Batch Integration恧ćÆ仄äø‹ć‚’ęä¾›ć—ć¾ć™ć€‚

  • 惐惃惁ć‚øćƒ§ćƒ–ć®čµ·å‹•ć‚’č”Œć†JobLaunchingMessageHandler
  • JobLaunchingMessageHandlerć®å…„åŠ›ćØćŖć‚‹ćƒšć‚¤ćƒ­ćƒ¼ćƒ‰ć‚’ęŒć¤JobLaunchRequest

JobLaunchRequestćÆ态惐惃惁ć‚øćƒ§ćƒ–ć‚’čµ·å‹•ć™ć‚‹ć®ć«åæ…要ćŖJobParametersćØJobć®ćƒ©ćƒƒćƒ‘ćƒ¼ć§ć™ć€‚

恂ćØćÆ态Spring Integration恮Message恋悉JobLaunchRequestćøå¤‰ę›ć™ć‚‹ę–¹ę³•ć€

Spring Batch Integration / Spring Batch Integration Introduction / Launching Batch Jobs through Messages / Transforming a file into a JobLaunchRequest

JobExecution恫恤恄恦ļ¼ˆć‚øćƒ§ćƒ–ć®ćƒŖ惝ć‚ø惈ćƒŖć‚’å‚ē…§ć—ć¦ć‚¹ćƒ†ćƒ¼ć‚æć‚¹ć‚’ē¢ŗčŖć™ć‚‹ć“ćØļ¼‰ć€

Spring Batch Integration / Spring Batch Integration Introduction / Launching Batch Jobs through Messages / The JobExecution Response

Spring Batch Integration恮čØ­å®šä¾‹ć€

Spring Batch Integration / Spring Batch Integration Introduction / Launching Batch Jobs through Messages / Spring Batch Integration Configuration

ć‚øćƒ§ćƒ–ć®äø­ć§ä½æ悏悌悋ItemReader恮čح定例ćØć„ć£ćŸć‚‚ć®ćŒē¶šćć¾ć™ć€‚

Spring Batch Integration / Spring Batch Integration Introduction / Launching Batch Jobs through Messages / Example ItemReader Configuration

ćØć€ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆć ć‘ć‚’ēœŗć‚ć¦ć„ć¦ć‚‚ć‚ć‹ć‚‰ćŖć„ć®ć§ć€å®Ÿéš›ć«ä½æć£ć¦ć„ć£ć¦ćæć¾ć—ć‚‡ć†ć€‚

ćŠé”Œ

ä»Šå›žć®ćŠé”ŒćÆć€ć“ć®ć‚ˆć†ć«ć—ć¾ć™ć€‚

  • ē‰¹å®šć®ćƒ‡ć‚£ćƒ¬ć‚Æ惈ćƒŖć‚’ćƒćƒ¼ćƒŖćƒ³ć‚°ć—ć¦ć€CSVćƒ•ć‚”ć‚¤ćƒ«ć®é…ē½®ć‚’ē›£č¦–
  • CSVćƒ•ć‚”ć‚¤ćƒ«ćŒé…ē½®ć•ć‚ŒćŸć‚‰ć€Spring Batchć‚’čµ·å‹•
  • Spring Batch恮ć‚ø惧惖ćÆ态CSVćƒ•ć‚”ć‚¤ćƒ«ć‚’čŖ­ćæč¾¼ć‚€ItemReaderćØ态čŖ­ćæč¾¼ć‚“ć ćƒ‡ćƒ¼ć‚æć‚’ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ć«åę˜ ć™ć‚‹ItemWriter恧꧋ꈐ
    • ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ćøć®åę˜ ć«ćÆJPA悒ä½æē”Ø
  • åŒć˜ćƒ•ć‚”ć‚¤ćƒ«ć‚’ē½®ć„恦悂态ć‚øćƒ§ćƒ–ć‚’čµ·å‹•ć—ć¦äøŠę›øćę›“ę–°ć™ć‚‹
    • äøŠę›ø恍恗恟恓ćØćŒć‚ć‹ć‚‹ć‚ˆć†ć«ć€ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹äøŠć®ćƒ†ćƒ¼ćƒ–ćƒ«ć«ćÆꛓꖰꙂ間悒čØ­ć‘ć‚‹

ē’°å¢ƒ

ä»Šå›žć®ē’°å¢ƒćÆ态恓恔悉怂

$ java --version
openjdk 17.0.3 2022-04-19
OpenJDK Runtime Environment (build 17.0.3+7-Ubuntu-0ubuntu0.20.04.1)
OpenJDK 64-Bit Server VM (build 17.0.3+7-Ubuntu-0ubuntu0.20.04.1, mixed mode, sharing)


$ mvn --version
Apache Maven 3.8.5 (3599d3414f046de2324203b78ddcf9b5e4388aa0)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 17.0.3, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.4.0-110-generic", arch: "amd64", family: "unix"

ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ć«ćÆ态MySQL悒ä½æē”Øć—ć¾ć™ć€‚MySQLćÆ172.17.0.2ć§å‹•ä½œć—ć¦ć„ć‚‹ć‚‚ć®ćØć—ć¾ć™ć€‚

$ mysql --version
mysql  Ver 8.0.29 for Linux on x86_64 (MySQL Community Server - GPL)

惗惭ć‚ø悧ć‚Æćƒˆć‚’ä½œęˆć™ć‚‹

ć¾ćšćÆ态Spring Boot惗惭ć‚ø悧ć‚Æćƒˆć‚’ä½œęˆć—ć¾ć™ć€‚ä¾å­˜é–¢äæ‚恫ćÆbatch态integration态data-jpa态mysqlć‚’ęŒ‡å®šć€‚

$ curl -s https://start.spring.io/starter.tgz \
  -d bootVersion=2.6.7 \
  -d javaVersion=17 \
  -d name=batch-integration-example \
  -d groupId=org.littlewings \
  -d artifactId=batch-integration-example \
  -d version=0.0.1-SNAPSHOT \
  -d packageName=org.littlewings.spring.batch.integration \
  -d dependencies=batch,integration,data-jpa,mysql \
  -d baseDir=batch-integration-example | tar zxvf -

惗惭ć‚ø悧ć‚Æćƒˆå†…ćøē§»å‹•ć€‚

$ cd batch-integration-example

Maven依存関äæ‚ćŠć‚ˆć³ćƒ—ćƒ©ć‚°ć‚¤ćƒ³čØ­å®šć€‚

        <properties>
                <java.version>17</java.version>
        </properties>
        <dependencies>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-batch</artifactId>
                </dependency>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-data-jpa</artifactId>
                </dependency>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-integration</artifactId>
                </dependency>
                <dependency>
                        <groupId>org.springframework.integration</groupId>
                        <artifactId>spring-integration-jpa</artifactId>
                </dependency>

                <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</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>
                <dependency>
                        <groupId>org.springframework.integration</groupId>
                        <artifactId>spring-integration-test</artifactId>
                        <scope>test</scope>
                </dependency>
        </dependencies>

        <build>
                <plugins>
                        <plugin>
                                <groupId>org.springframework.boot</groupId>
                                <artifactId>spring-boot-maven-plugin</artifactId>
                        </plugin>
                </plugins>
        </build>

ē”Ÿęˆć•ć‚ŒćŸć‚½ćƒ¼ć‚¹ć‚³ćƒ¼ćƒ‰ćÆå‰Šé™¤ć—ć¦ćŠćć¾ć™ć€‚

$ rm src/main/java/org/littlewings/spring/batch/integration/BatchIntegrationExampleApplication.java src/test/java/org/littlewings/spring/batch/integration/BatchIntegrationExampleApplicationTests.java

今回ä½æć„ćŸć„ä¾å­˜ćƒ©ć‚¤ćƒ–ćƒ©ćƒŖćŒč¶³ć‚ŠćŖ恄恮ćØ态ä½æ悏ćŖć„ć‚‚ć®ć‚‚ć‚ć‚‹ć®ć§å°‘ć—å¤‰ę›“ć€‚

 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-batch</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-integration</artifactId>
        </dependency>
        <!--
       <dependency>
           <groupId>org.springframework.integration</groupId>
           <artifactId>spring-integration-jpa</artifactId>
       </dependency>
       -->
        <dependency>
            <groupId>org.springframework.batch</groupId>
            <artifactId>spring-batch-integration</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-file</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</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>
        <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

ć¾ćšć€ä»Šå›žć®äø»é”Œć§ć‚ć‚‹spring-batch-integration悒čæ½åŠ ć€ćć‚Œć‹ć‚‰Spring Integration悒ä½æć£ćŸćƒ•ć‚”ć‚¤ćƒ«ē›£č¦–ć®ćŸć‚ć«spring-integration-file悒čæ½åŠ ć€‚
ćć—ć¦ć€spring-integration-jpaćÆä½æ悏ćŖć„ć®ć§ć‚³ćƒ”ćƒ³ćƒˆć‚¢ć‚¦ćƒˆć—ć¦ćŠćć¾ć™ć€‚

ćƒ†ćƒ¼ćƒ–ćƒ«å®šē¾©ćÆć€ć“ć®ć‚ˆć†ć«ć—ć¦ćŠćć¾ć™ć€‚

src/main/resources/schema.sql

drop table if exists person;

create table person (
  id integer,
  last_name varchar(10),
  first_name varchar(10),
  age integer,
  updated_time datetime,
  primary key(id)
);

ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć®čح定ćÆ恓恔悉怂

src/main/resources/application.properties

spring.datasource.url=jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8
spring.datasource.username=kazuhira
spring.datasource.password=password

spring.sql.init.mode=always
spring.batch.jdbc.initialize-schema=always

spring.batch.job.enabled=false

schema.sqlćÆåøø恫適ē”Øć™ć‚‹ć‚ˆć†ć«ć—ć¦ć€Spring Batch恌ä½æē”Øć™ć‚‹ćƒ†ćƒ¼ćƒ–ćƒ«ć‚‚ä½œęˆć™ć‚‹ć‚ˆć†ć«ć—ć¾ć™ć€‚

ć‚‚ć†ć²ćØć¤ćƒć‚¤ćƒ³ćƒˆćŒć‚ć‚‹ć®ć§ć™ćŒć€ćć‚ŒćÆć¾ćŸčŖ¬ę˜Žć—ć¾ć™ć€‚

ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć‚’ä½œęˆć™ć‚‹

恧ćÆć€ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć‚’ä½œęˆć—ć¦ć„ćć¾ć—ć‚‡ć†ć€‚

ć¾ćšćÆmainć‚Æćƒ©ć‚¹ć®ä½œęˆć€‚

src/main/java/org/littlewings/spring/batch/integration/App.java

package org.littlewings.spring.batch.integration;

import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

@EnableBatchProcessingć‚¢ćƒŽćƒ†ćƒ¼ć‚·ćƒ§ćƒ³ćÆåæ…要恧恙怂Spring Batch恮JobBuilderFactory悄StepBuilderFactoryćŖć©ćŒ
AutoConfigurationć•ć‚Œć‚‹ć‚ˆć†ć«ć™ć‚‹åæ…č¦ćŒć‚ć‚‹ć®ć§ć€‚

JPA恮ć‚Øćƒ³ćƒ†ć‚£ćƒ†ć‚£ć‚Æćƒ©ć‚¹ć€‚

src/main/java/org/littlewings/spring/batch/integration/Person.java

package org.littlewings.spring.batch.integration;

import java.time.LocalDateTime;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "person")
public class Person {
    @Id
    @Column(name = "id")
    Integer id;

    @Column(name = "last_name")
    String lastName;

    @Column(name = "first_name")
    String firstName;

    @Column(name = "age")
    Integer age;

    @Column(name = "updated_time")
    LocalDateTime updatedTime;

    // getterļ¼setterćÆēœē•„
}

DDL恫ćÆ存åœØ恗ćŖć‹ć£ćŸupdatedTimećØć„ć†ćƒ•ć‚£ćƒ¼ćƒ«ćƒ‰ćŒć‚ć‚Šć¾ć™ćŒć€ć“ć”ć‚‰ćÆItemProcessor恧čØ­å®šć™ć‚‹ć“ćØć«ć—ć¾ć™ć€‚

恓恓恋悉ćÆ态恓恔悉悒見ćŖ恌悉Spring Integration悄Spring Batch恮čØ­å®šć‚’č”Œć£ć¦ć„ćć¾ć™ć€‚

Spring Batch Integration / Spring Batch Integration Introduction / Launching Batch Jobs through Messages / Spring Batch Integration Configuration

ęœ€åˆćÆ@Configurationć‚’ä»˜äøŽć—ćŸć‚Æćƒ©ć‚¹ć®é››å½¢ć‚’ä½œęˆć€‚

src/main/java/org/littlewings/spring/batch/integration/IntegrationJobConfig.java

package org.littlewings.spring.batch.integration;

import java.io.File;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.LocalDateTime;
import javax.persistence.EntityManagerFactory;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.launch.support.SimpleJobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.integration.launch.JobLaunchingGateway;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.database.JpaItemWriter;
import org.springframework.batch.item.database.builder.JpaItemWriterBuilder;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.PathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.dsl.Pollers;
import org.springframework.integration.file.dsl.Files;
import org.springframework.integration.file.filters.SimplePatternFileListFilter;
import org.springframework.integration.handler.LoggingHandler;

@Configuration
public class IntegrationJobConfig {

    // å¾Œć§
}

CSVćƒ•ć‚”ć‚¤ćƒ«ć®ē›£č¦–恋悉ę›øć„ć¦ć„ćć¾ć—ć‚‡ć†ć€‚Spring Integration恮File Support悒ä½æć„ć¾ć™ć€‚

File Support

    @Bean
    public IntegrationFlow integrationFlow() throws Exception {
        return IntegrationFlows
                .from(
                        Files
                                .inboundAdapter(new File("target/files"))
                                .filter(new SimplePatternFileListFilter("*.csv")),
                        c -> c.poller(Pollers.fixedRate(Duration.ofSeconds(1L)).maxMessagesPerPoll(1L))
                )
                .log(LoggingHandler.Level.INFO, "polling file")
                .transform(fileMessageToJobRequest(null))
                .log(LoggingHandler.Level.INFO, "transform job request")
                .handle(jobLaunchingGateway(null))
                .log(LoggingHandler.Level.INFO, "job execution result")
                .get();
    }

target/filesćƒ‡ć‚£ćƒ¬ć‚Æ惈ćƒŖ配äø‹ć®.csvę‹”å¼µå­ć®ćƒ•ć‚”ć‚¤ćƒ«ć‚’ć€1ē§’ćŠćć«ē›£č¦–ć—ć¾ć™ć€‚ć¾ćŸć€1å›žć§å–å¾—ć™ć‚‹ćƒ•ć‚”ć‚¤ćƒ«ćÆć²ćØć¤ć«ć—ć¦ć„ć¾ć™ć€‚

        return IntegrationFlows
                .from(
                        Files
                                .inboundAdapter(new File("target/files"))
                                .filter(new SimplePatternFileListFilter("*.csv")),
                        c -> c.poller(Pollers.fixedRate(Duration.ofSeconds(1L)).maxMessagesPerPoll(1L))
                )

ćƒ•ć‚”ć‚¤ćƒ«ć‚’ę¤œå‡ŗ恗恟悉态transformć—ćŸå¾Œć«å‡¦ē†ć‚’č”Œć„ć¾ć™ļ¼ˆhandleļ¼‰ć€‚å„ć‚¹ćƒ†ćƒƒćƒ—ć®é–“ć«ćÆ惭悰å‡ŗåŠ›ć‚’č”Œć†ć‚ˆć†ć«ć—ć¾ć—ć‚‡ć†ć€‚
logćƒ”ć‚½ćƒƒćƒ‰ć®ē¬¬2å¼•ę•°ćÆćƒ­ć‚¬ćƒ¼åć§ć™ć€‚

                .log(LoggingHandler.Level.INFO, "polling file")
                .transform(fileMessageToJobRequest(null))
                .log(LoggingHandler.Level.INFO, "transform job request")
                .handle(jobLaunchingGateway(null))
                .log(LoggingHandler.Level.INFO, "job execution result")
                .get();

transformćƒ”ć‚½ćƒƒćƒ‰ć«ęø”恗恦恄悋恮ćÆ态Spring Integration恮Message悒Spring Batch恮Jobć«å¤‰ę›ć™ć‚‹Transformer恧恙怂

ć§ć™ćŒć€ćć®å‰ć«handlećƒ”ć‚½ćƒƒćƒ‰ć«ęø”ć—ć¦ć„ć‚‹å‡¦ē†ć‹ć‚‰č¦‹ć¦ć„ćć¾ć—ć‚‡ć†ć€‚

恓恔悉恧ćÆ态JobLaunchingGatewayć‚’ä½œęˆć—ć¦ć„ć¾ć™ć€‚

    @Bean
    public JobLaunchingGateway jobLaunchingGateway(JobRepository jobRepository) throws Exception {
        SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
        jobLauncher.setJobRepository(jobRepository);
        jobLauncher.setTaskExecutor(new SyncTaskExecutor());
        jobLauncher.afterPropertiesSet();

        return new JobLaunchingGateway(jobLauncher);
    }

JobLaunchingGatewayćÆ态Spring Batch恮Jobć‚’å®Ÿč”Œć™ć‚‹ćŸć‚ć®MessageHandlerć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ć®å®Ÿč£…ć§ć™ć€‚

JobLaunchingGateway (Spring Batch 4.3.5 API)

ć“ć®ćŸć‚ć€JobLauncherć®ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ć‚’åæ…要ćØć—ć¾ć™ć€‚

Configuring and Running a Job / Configuring a JobLauncher

ć“ć“ć‹ć‚‰å…ˆćÆ态Spring Batch恮Jobć‚’ę§‹ęˆć—ć¦ć„ćć¾ć™ć€‚

Jobć®å®šē¾©ć€‚åŒć˜JobParametersć®ęŒ‡å®šć§ć‚‚č¤‡ę•°å›žčµ·å‹•ć§ćć‚‹ć‚ˆć†ć«ć€RunIdIncrementerć‚’ęŒ‡å®šć—ć¦ć„ć¾ć™ć€‚

    @Bean
    public Job loadFileToDatabaseJob(JobBuilderFactory jobBuilderFactory) {
        return jobBuilderFactory
                .get("loadFileToDatabaseJob")
                .incrementer(new RunIdIncrementer())
                .start(loadFileToDatabaseStep(null))
                .next(deleteFileStep(null))
                .build();
    }

StepćÆ2恤čØ­ć‘ć¾ć—ćŸć€‚

ć²ćØ恤ē›®ć®StepćÆć€ćƒ•ć‚”ć‚¤ćƒ«ć‚’čŖ­ćæč¾¼ć‚“ć§ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ć«ę›øćč¾¼ć‚€ć‚‚ć®ć§ć™ć€‚

    @Bean
    public Step loadFileToDatabaseStep(StepBuilderFactory stepBuilderFactory) {
        return stepBuilderFactory
                .get("loadFileToDatabaseStep")
                .<Person, Person>chunk(3)
                .reader(flatFilePersonItemReader(null))
                .processor(updateTimeProcessor())
                .writer(jpaPersonItemWriter(null))
                .build();
    }

ItemReader态ItemProcessor态ItemWriterć®å®šē¾©ćÆ恓恔悉怂
CSVćƒ•ć‚”ć‚¤ćƒ«ć‚’čŖ­ćæč¾¼ć‚€ ā†’ ć‚Øćƒ³ćƒ†ć‚£ćƒ†ć‚£ć«updatedTime悒čØ­å®šć™ć‚‹ ā†’ JPAć§ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ć«ę›øćč¾¼ć‚€ć€ćØć„ć†ęµć‚Œć«ćŖć£ć¦ć„ć¾ć™ć€‚

    @Bean
    @StepScope
    public FlatFileItemReader<Person> flatFilePersonItemReader(@Value("#{jobParameters['input.file.path']}") String path) {
        Resource resource = new PathResource(path);

        return new FlatFileItemReaderBuilder<Person>()
                .name("flatFilePersonItemReader")
                .resource(resource)
                .encoding("UTF-8")
                .delimited()
                .names(new String[]{"id", "lastName", "firstName", "age"})
                .linesToSkip(1)
                .targetType(Person.class)
                .build();
    }

    @Bean
    @StepScope
    public ItemProcessor<Person, Person> updateTimeProcessor() {
        return (person) -> {
            person.setUpdatedTime(LocalDateTime.now());
            return person;
        };
    }

    @Bean
    @StepScope
    public JpaItemWriter<Person> jpaPersonItemWriter(EntityManagerFactory entityManagerFactory) {
        return new JpaItemWriterBuilder<Person>()
                .entityManagerFactory(entityManagerFactory)
                .build();
    }

兄力ćØćŖ悋CSVćƒ•ć‚”ć‚¤ćƒ«ć®ćƒ‘ć‚¹ćÆ态JobParametersćØć—ć¦å–å¾—ć—ć¾ć™ć€‚

    @Bean
    @StepScope
    public FlatFileItemReader<Person> flatFilePersonItemReader(@Value("#{jobParameters['input.file.path']}") String path) {

ęœ€å¾ŒćÆ态čŖ­ćæč¾¼ć‚“ć ćƒ•ć‚”ć‚¤ćƒ«ć‚’å‰Šé™¤ć™ć‚‹Stepć§ć™ć€‚ćƒ•ć‚”ć‚¤ćƒ«ć‚’å‰Šé™¤ć™ć‚‹å‡¦ē†ć‚’å…„悌ćŖ恄ćØ态ē›£č¦–åÆ¾č±”ć®ćƒ‡ć‚£ćƒ¬ć‚Æ惈ćƒŖć«ćƒ•ć‚”ć‚¤ćƒ«ćŒ
ꮋ悊ē¶šć‘ć‚‹ć®ć§ć€ćƒ‡ćƒ¼ć‚æć®å–ć‚Šč¾¼ćæ処ē†ć‚’å»¶ć€…ćØē¹°ć‚Ščæ”ć—ć¦ć—ć¾ć„ć¾ć™ć€‚

    @Bean
    public Step deleteFileStep(StepBuilderFactory stepBuilderFactory) {
        return stepBuilderFactory
                .get("deleteFileStep")
                .tasklet(deleteFileTasklet(null))
                .build();
    }

ćƒ•ć‚”ć‚¤ćƒ«ć®å‰Šé™¤ć‚’č”Œć†Taskletć€‚ć“ć®ć‚ˆć†ćŖStep恧ćÆćŖćé•ć†ę–¹ę³•ć§ć‚‚ćƒ•ć‚”ć‚¤ćƒ«ć®å‰Šé™¤ćÆ恧恍悋ćØę€ć†ć®ć§ć™ćŒć€ć©ć“ć‹ć—ć‚‰ć«å‰Šé™¤ć®
処ē†ć‚’ę›ø恋ćŖ恄ćØ恄恑ćŖ恕恝恆ćŖ恓ćØćÆå¤‰ć‚ć‚‰ćŖć„ć®ć§ć€ä»Šå›žćÆć“ć®ę–¹ę³•ć‚’éøęŠžć—ć¾ć—ćŸć€‚

    @Bean
    @StepScope
    public Tasklet deleteFileTasklet(@Value("#{jobParameters['input.file.path']}") String path) {
        return (contribution, context) -> {
            java.nio.file.Files.delete(Paths.get(path));
            return RepeatStatus.FINISHED;
        };
    }

ćć—ć¦ć€ć“ć®Spring Batchć§å®šē¾©ć—ćŸJobćØ

    @Bean
    public Job loadFileToDatabaseJob(JobBuilderFactory jobBuilderFactory) {
        return jobBuilderFactory
                .get("loadFileToDatabaseJob")
                .incrementer(new RunIdIncrementer())
                .start(loadFileToDatabaseStep(null))
                .next(deleteFileStep(null))
                .build();
    }

Jobć‚’å®Ÿč”Œć™ć‚‹ćŸć‚ć«Spring Integration恮Message悒Jobć«å¤‰ę›ć™ć‚‹ć®ćŒć€Transformer恧恗恟怂

    public IntegrationFlow integrationFlow() throws Exception {
        return IntegrationFlows
                .from(
                        Files
                                .inboundAdapter(new File("target/files"))
                                .filter(new SimplePatternFileListFilter("*.csv")),
                        c -> c.poller(Pollers.fixedRate(Duration.ofSeconds(1L)).maxMessagesPerPoll(1L))
                )
                .log(LoggingHandler.Level.INFO, "polling file")
                .transform(fileMessageToJobRequest(null))
                .log(LoggingHandler.Level.INFO, "transform job request")
                .handle(jobLaunchingGateway(null))
                .log(LoggingHandler.Level.INFO, "job execution result")
                .get();

ćć®å‡¦ē†ć®å†…容ćÆ态恓恔悉怂

    @Bean
    public FileMessageToJobRequest fileMessageToJobRequest(JobExplorer jobExplorer) {
        FileMessageToJobRequest fileMessageToJobRequest = new FileMessageToJobRequest();
        fileMessageToJobRequest.setJob(loadFileToDatabaseJob(null));
        fileMessageToJobRequest.setJobExplorer(jobExplorer);

        return fileMessageToJobRequest;
    }

å‚č€ƒć«ć—ć¦ć„ć‚‹ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆćÆ态恓恔悉恧恙恭怂

Spring Batch Integration / Spring Batch Integration Introduction / Launching Batch Jobs through Messages / The JobExecution Response

JobćŠć‚ˆć³JobExplorer悒čØ­å®šć—ć¦ć„ć¾ć™ć€‚

        fileMessageToJobRequest.setJob(loadFileToDatabaseJob(null));
        fileMessageToJobRequest.setJobExplorer(jobExplorer);

ć‚Æćƒ©ć‚¹ć®å®šē¾©ćÆ态恓恔悉怂

src/main/java/org/littlewings/spring/batch/integration/FileMessageToJobRequest.java

package org.littlewings.spring.batch.integration;

import java.io.File;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.integration.launch.JobLaunchRequest;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.integration.annotation.Transformer;
import org.springframework.messaging.Message;

public class FileMessageToJobRequest {
    Job job;
    JobExplorer jobExplorer;

    @Transformer
    public JobLaunchRequest toRequest(Message<File> message) {
        JobParametersBuilder jobParametersBuilder = new JobParametersBuilder(jobExplorer);  // for JobParametersIncrementer

        jobParametersBuilder.getNextJobParameters(job)  // for JobParametersIncrementer
                .addString("input.file.path", message.getPayload().getAbsolutePath());

        return new JobLaunchRequest(job, jobParametersBuilder.toJobParameters());
    }

    public Job getJob() {
        return job;
    }

    public void setJob(Job job) {
        this.job = job;
    }

    public JobExplorer getJobExplorer() {
        return jobExplorer;
    }

    public void setJobExplorer(JobExplorer jobExplorer) {
        this.jobExplorer = jobExplorer;
    }
}

ćƒć‚¤ćƒ³ćƒˆćÆć€ć“ć”ć‚‰ć®ćƒ”ć‚½ćƒƒćƒ‰ć§ć™ć€‚@Transformerć‚¢ćƒŽćƒ†ćƒ¼ć‚·ćƒ§ćƒ³ć‚’ä»˜äøŽć—恦态Spring Integration恮File Support悒ä½æć£ć¦ę¤œå‡ŗ恗恟
CSVćƒ•ć‚”ć‚¤ćƒ«ļ¼ˆMessage<File>ļ¼‰ć‚’JobLaunchRequestć«å¤‰ę›ć—ć¾ć™ć€‚

    @Transformer
    public JobLaunchRequest toRequest(Message<File> message) {
        JobParametersBuilder jobParametersBuilder = new JobParametersBuilder(jobExplorer);  // for JobParametersIncrementer

        jobParametersBuilder.getNextJobParameters(job)  // for JobParametersIncrementer
                .addString("input.file.path", message.getPayload().getAbsolutePath());

        return new JobLaunchRequest(job, jobParametersBuilder.toJobParameters());
    }

CSVćƒ•ć‚”ć‚¤ćƒ«ć®ćƒ‘ć‚¹ćÆ态恓恮éƒØåˆ†ć§JobParametersćØ恗恦čØ­å®šć—ć¦ć„ć¾ć™ć€‚

                .addString("input.file.path", message.getPayload().getAbsolutePath());

ć“ć“ć§ć®ęŒ‡å®šćŒć€ć“ć”ć‚‰ć«ęø”恕悌悋悏恑恧恙恭怂

    @Bean
    @StepScope
    public FlatFileItemReader<Person> flatFilePersonItemReader(@Value("#{jobParameters['input.file.path']}") String path) {

ćØć“ć‚ć§ć‚µćƒ³ćƒ—ćƒ«ć‚³ćƒ¼ćƒ‰ć‚’č¦‹ć‚‹ćØ态JobExplorerćÆęŒ‡å®šć—ć¦ć„ćŖć„ć®ć§ć™ćŒć€‚

Spring Batch Integration / Spring Batch Integration Introduction / Launching Batch Jobs through Messages / The JobExecution Response

今回JobParametersIncrementerļ¼ˆRunIdIncrementerļ¼‰ć‚’ä½æć£ćŸć®ć§JobParametersBuilder恫JobExplorer恮čØ­å®šćŠć‚ˆć³ć€
JobParametersBuilder#getNextJobParameters`ć®å‘¼ć³å‡ŗ恗恌åæ…要恫ćŖć‚Šć¾ć™ć€‚

悂恗态RunIdIncrementerćŖć—ć§ć§ćć‚‹é™ć‚ŠåŒć˜JobParametersć§å®Ÿč”Œć—ć‚ˆć†ćØ恙悋ćØ态仄äø‹ć®ć‚ˆć†ć«Jobć®å®Ÿč”Œć®åŗ¦ć«ē•°ćŖ悋
JobParamters悒čØ­å®šć™ć‚‹ć‚ˆć†ćŖ恓ćØ悒恙悋åæ…č¦ćŒå‡ŗć¦ćć‚‹ć§ć—ć‚‡ć†ć­ć€‚

    @Transformer
    public JobLaunchRequest toRequest(Message<File> message) {
        JobParametersBuilder jobParametersBuilder =
            new JobParametersBuilder();

        jobParametersBuilder
                .addString("input.file.path", message.getPayload().getAbsolutePath())
                .addString("job.start.time", LocalDateTime.now().format(DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss")));

        return new JobLaunchRequest(job, jobParametersBuilder.toJobParameters());
    }

恓悌恧态Spring BatchćŠć‚ˆć³Spring Integration恮čح定ćÆć§ćć¾ć—ćŸć€‚

Spring Batch恮JobćŠć‚ˆć³Spring Integration恮IntegrationFlowć‚’å®šē¾©ć—恦恄悋ć‚Æćƒ©ć‚¹å…Øä½“ć‚’č¼‰ć›ć‚‹ćØ态恓悓ćŖę„Ÿć˜ć«ćŖć£ć¦ć„ć¾ć™ć€‚

src/main/java/org/littlewings/spring/batch/integration/IntegrationJobConfig.java

package org.littlewings.spring.batch.integration;

import java.io.File;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.LocalDateTime;
import javax.persistence.EntityManagerFactory;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.launch.support.SimpleJobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.integration.launch.JobLaunchingGateway;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.database.JpaItemWriter;
import org.springframework.batch.item.database.builder.JpaItemWriterBuilder;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.PathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.dsl.Pollers;
import org.springframework.integration.file.dsl.Files;
import org.springframework.integration.file.filters.SimplePatternFileListFilter;
import org.springframework.integration.handler.LoggingHandler;

@Configuration
public class IntegrationJobConfig {
    @Bean
    public IntegrationFlow integrationFlow() throws Exception {
        return IntegrationFlows
                .from(
                        Files
                                .inboundAdapter(new File("target/files"))
                                .filter(new SimplePatternFileListFilter("*.csv")),
                        c -> c.poller(Pollers.fixedRate(Duration.ofSeconds(1L)).maxMessagesPerPoll(1L))
                )
                .log(LoggingHandler.Level.INFO, "polling file")
                .transform(fileMessageToJobRequest(null))
                .log(LoggingHandler.Level.INFO, "transform job request")
                .handle(jobLaunchingGateway(null))
                .log(LoggingHandler.Level.INFO, "job execution result")
                .get();
    }

    @Bean
    public FileMessageToJobRequest fileMessageToJobRequest(JobExplorer jobExplorer) {
        FileMessageToJobRequest fileMessageToJobRequest = new FileMessageToJobRequest();
        fileMessageToJobRequest.setJob(loadFileToDatabaseJob(null));
        fileMessageToJobRequest.setJobExplorer(jobExplorer);

        return fileMessageToJobRequest;
    }

    @Bean
    public JobLaunchingGateway jobLaunchingGateway(JobRepository jobRepository) throws Exception {
        SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
        jobLauncher.setJobRepository(jobRepository);
        jobLauncher.setTaskExecutor(new SyncTaskExecutor());
        jobLauncher.afterPropertiesSet();

        return new JobLaunchingGateway(jobLauncher);
    }

    @Bean
    public Job loadFileToDatabaseJob(JobBuilderFactory jobBuilderFactory) {
        return jobBuilderFactory
                .get("loadFileToDatabaseJob")
                .incrementer(new RunIdIncrementer())
                .start(loadFileToDatabaseStep(null))
                .next(deleteFileStep(null))
                .build();
    }

    @Bean
    public Step loadFileToDatabaseStep(StepBuilderFactory stepBuilderFactory) {
        return stepBuilderFactory
                .get("loadFileToDatabaseStep")
                .<Person, Person>chunk(3)
                .reader(flatFilePersonItemReader(null))
                .processor(updateTimeProcessor())
                .writer(jpaPersonItemWriter(null))
                .build();
    }

    @Bean
    public Step deleteFileStep(StepBuilderFactory stepBuilderFactory) {
        return stepBuilderFactory
                .get("deleteFileStep")
                .tasklet(deleteFileTasklet(null))
                .build();
    }

    @Bean
    @StepScope
    public FlatFileItemReader<Person> flatFilePersonItemReader(@Value("#{jobParameters['input.file.path']}") String path) {
        Resource resource = new PathResource(path);

        return new FlatFileItemReaderBuilder<Person>()
                .name("flatFilePersonItemReader")
                .resource(resource)
                .encoding("UTF-8")
                .delimited()
                .names(new String[]{"id", "lastName", "firstName", "age"})
                .linesToSkip(1)
                .targetType(Person.class)
                .build();
    }

    @Bean
    @StepScope
    public ItemProcessor<Person, Person> updateTimeProcessor() {
        return (person) -> {
            person.setUpdatedTime(LocalDateTime.now());
            return person;
        };
    }

    @Bean
    @StepScope
    public JpaItemWriter<Person> jpaPersonItemWriter(EntityManagerFactory entityManagerFactory) {
        return new JpaItemWriterBuilder<Person>()
                .entityManagerFactory(entityManagerFactory)
                .build();
    }

    @Bean
    @StepScope
    public Tasklet deleteFileTasklet(@Value("#{jobParameters['input.file.path']}") String path) {
        return (contribution, context) -> {
            java.nio.file.Files.delete(Paths.get(path));
            return RepeatStatus.FINISHED;
        };
    }
}

動作ē¢ŗčŖć—恦ćæ悋

恧ćÆć€ä½œęˆć—ćŸć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć‚’å‹•ć‹ć—ć¦ćæć¾ć—ć‚‡ć†ć€‚

čŖ­ćæč¾¼ć‚€CSVćƒ•ć‚”ć‚¤ćƒ«ć‚’ć€2恤ē”Øę„ć—ć¾ć™ć€‚

src/main/resources/isono_family.csv

id,last_name,first_name,age
1,ē£Æ野,ć‚«ćƒ„ć‚Ŗ,11
2,ē£Æ野,ćƒÆć‚«ćƒ”,9
3,惕悰ē”°,悵悶ć‚Ø,23
4,惕悰ē”°,ćƒžć‚¹ć‚Ŗ,32
5,惕悰ē”°,ć‚æ惩ć‚Ŗ,3

src/main/resources/namino_family.csv

id,last_name,first_name,age
6,ę³¢é‡Ž,惎ćƒŖć‚¹ć‚±,24
7,ę³¢é‡Ž,ć‚æć‚¤ć‚³,22
8,ę³¢é‡Ž,悤ć‚Æ惩,1

ćŖ悓ćØćŖ恏src/main/resources恫ē½®ć„恦恄悋恮恧ć‚Æćƒ©ć‚¹ćƒ‘ć‚¹äøŠć«å­˜åœØć—ć¦ć„ć¾ć™ćŒć€ć‚Æćƒ©ć‚¹ćƒ‘ć‚¹ć‚’ē›£č¦–ć—ćŸć„ć‚ć‘ć§ćÆć‚ć‚Šć¾ć›ć‚“ć€‚

ćƒ‘ćƒƒć‚±ćƒ¼ć‚øćƒ³ć‚°ć—ć¦

$ mvn package

čµ·å‹•ć€‚

$ java -jar target/batch-integration-example-0.0.1-SNAPSHOT.jar

Webć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć§ćÆćŖć„ć®ć§ć™ćŒć€ć“ć®ć¾ć¾ć‚µćƒ¼ćƒćƒ¼ćØć—ć¦čµ·å‹•ć—ć¦ćć‚Œć¦ć„ć¾ć™ć€‚

恓恮Ꙃ态src/main/resources/application.properties恫仄äø‹ć®čØ­å®šć‚’å…„ć‚Œć¦ć„ć¾ć—ćŸć€‚

spring.batch.job.enabled=false

ć“ć‚Œć‚’å…„ć‚Œć¦ć„ćŖć„å “åˆćÆ态Spring Batchć®ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć®å‹•ä½œćØć—ć¦ć™ć¹ć¦ć®Job悒恗悈恆ćØ恙悋恮恧ę³Øꄏ恌åæ…要恧恙怂 今回ćÆSpring Batch Integration悒ä½æć£ćŸJob恗恋ćŖćć€ć‹ć¤ćƒ‡ć‚£ćƒ¬ć‚Æ惈ćƒŖē›£č¦–恧Jobć‚’čµ·å‹•ć—ćŸć„ć®ć§spring.batch.job.enabled悒falsećØ恗恦
ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³čµ·å‹•ę™‚ć«Jobć‚’å®Ÿč”Œć—ćŖć„ć‚ˆć†ć«ć—ć¾ć—ćŸć€‚

ćŖćŠć€ćƒ‡ć‚£ćƒ¬ć‚Æ惈ćƒŖē›£č¦–ć‚’č”Œć†ćØ态ē›£č¦–åÆ¾č±”ć®ćƒ‡ć‚£ćƒ¬ć‚Æ惈ćƒŖ恌ćŖć„å “åˆćÆä½œęˆć—ć‚ˆć†ćØ恙悋ćæ恟恄恧恙恭怂

$ ll target/files
合č؈ 8
drwxrwxr-x 2 xxxxx xxxxx 4096  5꜈ 20 01:19 ./
drwxrwxr-x 9 xxxxx xxxxx 4096  5꜈ 20 01:19 ../

ęœ€åˆć«ćƒ†ćƒ¼ćƒ–ćƒ«ć®ēŠ¶ę…‹ć‚’ē¢ŗčŖć€‚

mysql> select * from person;
Empty set (0.00 sec)

1ćƒ•ć‚”ć‚¤ćƒ«ē›®ć‚’ē›£č¦–åÆ¾č±”ć®ćƒ‡ć‚£ćƒ¬ć‚Æ惈ćƒŖć«ć‚³ćƒ”ćƒ¼ć—ć¦ćæć¾ć™ć€‚

$ cp src/main/resources/isono_family.csv target/files

恙悋ćØć€ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ćŒå‹•ćå§‹ć‚ć¾ć™ć€‚

2022-05-20 01:22:30.249  INFO 18890 --- [   scheduling-1] polling file                             : GenericMessage [payload=target/files/isono_family.csv, headers={file_originalFile=target/files/isono_family.csv, id=ddfff35a-5d0c-89ed-905e-f00d2fc00a6c, file_name=isono_family.csv, file_relativePath=isono_family.csv, timestamp=1652977350247}]
2022-05-20 01:22:30.249  INFO 18890 --- [   scheduling-1] o.s.i.h.s.MessagingMethodInvokerHelper   : Overriding default instance of MessageHandlerMethodFactory with provided one.
2022-05-20 01:22:30.307  INFO 18890 --- [   scheduling-1] transform job request                    : GenericMessage [payload=JobLaunchRequest: loadFileToDatabaseJob, parameters={run.id=1, input.file.path=/path/to/batch-integration-example/target/files/isono_family.csv}, headers={file_originalFile=target/files/isono_family.csv, id=37b5abff-b5b8-61ab-e0c1-d73813bf4c2f, file_name=isono_family.csv, file_relativePath=isono_family.csv, timestamp=1652977350307}]
2022-05-20 01:22:30.509  INFO 18890 --- [   scheduling-1] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=loadFileToDatabaseJob]] launched with the following parameters: [{run.id=1, input.file.path=/path/to/batch-integration-example/target/files/isono_family.csv}]
2022-05-20 01:22:30.668  INFO 18890 --- [   scheduling-1] o.s.batch.core.job.SimpleStepHandler     : Executing step: [loadFileToDatabaseStep]
2022-05-20 01:22:30.975  INFO 18890 --- [   scheduling-1] o.s.batch.core.step.AbstractStep         : Step: [loadFileToDatabaseStep] executed in 306ms
2022-05-20 01:22:31.061  INFO 18890 --- [   scheduling-1] o.s.batch.core.job.SimpleStepHandler     : Executing step: [deleteFileStep]
2022-05-20 01:22:31.169  INFO 18890 --- [   scheduling-1] o.s.batch.core.step.AbstractStep         : Step: [deleteFileStep] executed in 108ms
2022-05-20 01:22:31.224  INFO 18890 --- [   scheduling-1] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=loadFileToDatabaseJob]] completed with the following parameters: [{run.id=1, input.file.path=/path/to/batch-integration-example/target/files/isono_family.csv}] and the following status: [COMPLETED] in 668ms
2022-05-20 01:22:31.231  INFO 18890 --- [   scheduling-1] job execution result                     : GenericMessage [payload=JobExecution: id=1, version=2, startTime=Fri May 20 01:22:30 JST 2022, endTime=Fri May 20 01:22:31 JST 2022, lastUpdated=Fri May 20 01:22:31 JST 2022, status=COMPLETED, exitStatus=exitCode=COMPLETED;exitDescription=, job=[JobInstance: id=1, version=0, Job=[loadFileToDatabaseJob]], jobParameters=[{run.id=1, input.file.path=/path/to/batch-integration-example/target/files/isono_family.csv}], headers={file_originalFile=target/files/isono_family.csv, id=82978876-8ca8-12d7-4fe6-9faeaf0312ec, file_name=isono_family.csv, file_relativePath=isono_family.csv, timestamp=1652977351224}]

ćƒ­ć‚°ć‚’č¦‹ć‚‹ćØć€ćƒ•ć‚”ć‚¤ćƒ«ć‚’ę¤œå‡ŗ恗恦

2022-05-20 01:22:30.249  INFO 18890 --- [   scheduling-1] polling file                             : GenericMessage [payload=target/files/isono_family.csv, headers={file_originalFile=target/files/isono_family.csv, id=ddfff35a-5d0c-89ed-905e-f00d2fc00a6c, file_name=isono_family.csv, file_relativePath=isono_family.csv, timestamp=1652977350247}]

Jobć«å¤‰ę›ć€‚

2022-05-20 01:22:30.307  INFO 18890 --- [   scheduling-1] transform job request                    : GenericMessage [payload=JobLaunchRequest: loadFileToDatabaseJob, parameters={run.id=1, input.file.path=/path/to/batch-integration-example/target/files/isono_family.csv}, headers={file_originalFile=target/files/isono_family.csv, id=37b5abff-b5b8-61ab-e0c1-d73813bf4c2f, file_name=isono_family.csv, file_relativePath=isono_family.csv, timestamp=1652977350307}]

Spring Batch恮Jobć‚’å®Ÿč”Œć€‚

2022-05-20 01:22:30.668  INFO 18890 --- [   scheduling-1] o.s.batch.core.job.SimpleStepHandler     : Executing step: [loadFileToDatabaseStep]
2022-05-20 01:22:30.975  INFO 18890 --- [   scheduling-1] o.s.batch.core.step.AbstractStep         : Step: [loadFileToDatabaseStep] executed in 306ms
2022-05-20 01:22:31.061  INFO 18890 --- [   scheduling-1] o.s.batch.core.job.SimpleStepHandler     : Executing step: [deleteFileStep]
2022-05-20 01:22:31.169  INFO 18890 --- [   scheduling-1] o.s.batch.core.step.AbstractStep         : Step: [deleteFileStep] executed in 108ms
2022-05-20 01:22:31.224  INFO 18890 --- [   scheduling-1] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=loadFileToDatabaseJob]] completed with the following parameters: [{run.id=1, input.file.path=/path/to/batch-integration-example/target/files/isono_family.csv}] and the following status: [COMPLETED] in 668ms

ēµ‚äŗ†ć€ćØć„ć†ę§˜å­ćŒć‚ć‹ć‚Šć¾ć™ć€‚

2022-05-20 01:22:31.231  INFO 18890 --- [   scheduling-1] job execution result                     : GenericMessage [payload=JobExecution: id=1, version=2, startTime=Fri May 20 01:22:30 JST 2022, endTime=Fri May 20 01:22:31 JST 2022, lastUpdated=Fri May 20 01:22:31 JST 2022, status=COMPLETED, exitStatus=exitCode=COMPLETED;exitDescription=, job=[JobInstance: id=1, version=0, Job=[loadFileToDatabaseJob]], jobParameters=[{run.id=1, input.file.path=/path/to/batch-integration-example/target/files/isono_family.csv}], headers={file_originalFile=target/files/isono_family.csv, id=82978876-8ca8-12d7-4fe6-9faeaf0312ec, file_name=isono_family.csv, file_relativePath=isono_family.csv, timestamp=1652977351224}]

ćƒ‡ćƒ¼ć‚æ悒ē¢ŗčŖć—恦ćæć¾ć—ć‚‡ć†ć€‚

mysql> select * from person;
+----+-----------+------------+------+---------------------+
| id | last_name | first_name | age  | updated_time        |
+----+-----------+------------+------+---------------------+
|  1 | ē£Æ野      | ć‚«ćƒ„ć‚Ŗ     |   11 | 2022-05-20 01:22:31 |
|  2 | ē£Æ野      | ćƒÆć‚«ćƒ”     |    9 | 2022-05-20 01:22:31 |
|  3 | 惕悰ē”°    | 悵悶ć‚Ø     |   23 | 2022-05-20 01:22:31 |
|  4 | 惕悰ē”°    | ćƒžć‚¹ć‚Ŗ     |   32 | 2022-05-20 01:22:31 |
|  5 | 惕悰ē”°    | ć‚æ惩ć‚Ŗ     |    3 | 2022-05-20 01:22:31 |
+----+-----------+------------+------+---------------------+
5 rows in set (0.00 sec)

ć”ć‚ƒć‚“ćØå–ć‚Šč¾¼ć¾ć‚Œć¦ć„ć¾ć™ć­ć€‚

ē›£č¦–åÆ¾č±”ć®ćƒ‡ć‚£ćƒ¬ć‚Æ惈ćƒŖć«é…ē½®ć—ćŸćƒ•ć‚”ć‚¤ćƒ«ćÆ态Spring Batch恮Jobć«å«ć¾ć‚Œć¦ć„ćŸTaskletć«ć‚ˆć‚Šå‰Šé™¤ć•ć‚Œć¦ć„ć¾ć™ć€‚

$ tree target/files
target/files

0 directories, 0 files

ć‚‚ć†ć²ćØć¤ć®ćƒ•ć‚”ć‚¤ćƒ«ć‚’ć€ē›£č¦–åÆ¾č±”ć®ćƒ‡ć‚£ćƒ¬ć‚Æ惈ćƒŖć«ć‚³ćƒ”ćƒ¼ć—ć¦ćæć¾ć™ć€‚

$ cp src/main/resources/namino_family.csv target/files

å…ˆć»ć©ćØåŒę§˜ć€é…ē½®ć•ć‚ŒćŸćƒ•ć‚”ć‚¤ćƒ«ć‚’ę¤œå‡ŗ恗恦Spring BatchćŒčµ·å‹•ć—ć¾ć—ćŸć€‚

2022-05-20 01:31:28.237  INFO 18890 --- [   scheduling-1] polling file                             : GenericMessage [payload=target/files/namino_family.csv, headers={file_originalFile=target/files/namino_family.csv, id=2e9cac33-a5be-8eeb-a471-d47f579906cc, file_name=namino_family.csv, file_relativePath=namino_family.csv, timestamp=1652977888237}]
2022-05-20 01:31:28.278  INFO 18890 --- [   scheduling-1] transform job request                    : GenericMessage [payload=JobLaunchRequest: loadFileToDatabaseJob, parameters={run.id=2, input.file.path=/path/to/batch-integration-example/target/files/namino_family.csv}, headers={file_originalFile=target/files/namino_family.csv, id=c404ecd5-4b5a-2593-dc12-13b0621b3b54, file_name=namino_family.csv, file_relativePath=namino_family.csv, timestamp=1652977888278}]
2022-05-20 01:31:28.365  INFO 18890 --- [   scheduling-1] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=loadFileToDatabaseJob]] launched with the following parameters: [{run.id=2, input.file.path=/path/to/batch-integration-example/target/files/namino_family.csv}]
2022-05-20 01:31:28.448  INFO 18890 --- [   scheduling-1] o.s.batch.core.job.SimpleStepHandler     : Executing step: [loadFileToDatabaseStep]
2022-05-20 01:31:28.581  INFO 18890 --- [   scheduling-1] o.s.batch.core.step.AbstractStep         : Step: [loadFileToDatabaseStep] executed in 132ms
2022-05-20 01:31:28.658  INFO 18890 --- [   scheduling-1] o.s.batch.core.job.SimpleStepHandler     : Executing step: [deleteFileStep]
2022-05-20 01:31:28.834  INFO 18890 --- [   scheduling-1] o.s.batch.core.step.AbstractStep         : Step: [deleteFileStep] executed in 176ms
2022-05-20 01:31:28.939  INFO 18890 --- [   scheduling-1] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=loadFileToDatabaseJob]] completed with the following parameters: [{run.id=2, input.file.path=/path/to/batch-integration-example/target/files/namino_family.csv}] and the following status: [COMPLETED] in 538ms
2022-05-20 01:31:28.940  INFO 18890 --- [   scheduling-1] job execution result                     : GenericMessage [payload=JobExecution: id=2, version=2, startTime=Fri May 20 01:31:28 JST 2022, endTime=Fri May 20 01:31:28 JST 2022, lastUpdated=Fri May 20 01:31:28 JST 2022, status=COMPLETED, exitStatus=exitCode=COMPLETED;exitDescription=, job=[JobInstance: id=2, version=0, Job=[loadFileToDatabaseJob]], jobParameters=[{run.id=2, input.file.path=/path/to/batch-integration-example/target/files/namino_family.csv}], headers={file_originalFile=target/files/namino_family.csv, id=f50295b0-dd2b-5d49-6196-d61c5507b3d5, file_name=namino_family.csv, file_relativePath=namino_family.csv, timestamp=1652977888940}]

å–ć‚Šč¾¼ć¾ć‚ŒćŸćƒ‡ćƒ¼ć‚æ怂

mysql> select * from person;
+----+-----------+--------------+------+---------------------+
| id | last_name | first_name   | age  | updated_time        |
+----+-----------+--------------+------+---------------------+
|  1 | ē£Æ野      | ć‚«ćƒ„ć‚Ŗ       |   11 | 2022-05-20 01:22:31 |
|  2 | ē£Æ野      | ćƒÆć‚«ćƒ”       |    9 | 2022-05-20 01:22:31 |
|  3 | 惕悰ē”°    | 悵悶ć‚Ø       |   23 | 2022-05-20 01:22:31 |
|  4 | 惕悰ē”°    | ćƒžć‚¹ć‚Ŗ       |   32 | 2022-05-20 01:22:31 |
|  5 | 惕悰ē”°    | ć‚æ惩ć‚Ŗ       |    3 | 2022-05-20 01:22:31 |
|  6 | ę³¢é‡Ž      | 惎ćƒŖć‚¹ć‚±     |   24 | 2022-05-20 01:31:29 |
|  7 | ę³¢é‡Ž      | ć‚æć‚¤ć‚³       |   22 | 2022-05-20 01:31:29 |
|  8 | ę³¢é‡Ž      | 悤ć‚Æ惩       |    1 | 2022-05-20 01:31:29 |
+----+-----------+--------------+------+---------------------+
8 rows in set (0.00 sec)

恓恓恧态悂恆1åŗ¦ęœ€åˆć«å–ć‚Šč¾¼ć‚“ć ćƒ•ć‚”ć‚¤ćƒ«ć‚’ć‚³ćƒ”ćƒ¼ć—ć¦ćæć¾ć™ć€‚

$ cp src/main/resources/isono_family.csv target/files

ęœ€åˆćØåŒć˜ć‚ˆć†ć«ć€Spring BatchćŒčµ·å‹•ć—ć¾ć™ć€‚

2022-05-20 01:33:40.237  INFO 18890 --- [   scheduling-1] polling file                             : GenericMessage [payload=target/files/isono_family.csv, headers={file_originalFile=target/files/isono_family.csv, id=ef090587-8d4d-0932-7b89-62d2418d8391, file_name=isono_family.csv, file_relativePath=isono_family.csv, timestamp=1652978020237}]
2022-05-20 01:33:40.252  INFO 18890 --- [   scheduling-1] transform job request                    : GenericMessage [payload=JobLaunchRequest: loadFileToDatabaseJob, parameters={run.id=3, input.file.path=/path/to/batch-integration-example/target/files/isono_family.csv}, headers={file_originalFile=target/files/isono_family.csv, id=f2603171-64a4-9093-0621-0125698264db, file_name=isono_family.csv, file_relativePath=isono_family.csv, timestamp=1652978020252}]
2022-05-20 01:33:40.320  INFO 18890 --- [   scheduling-1] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=loadFileToDatabaseJob]] launched with the following parameters: [{run.id=3, input.file.path=/path/to/batch-integration-example/target/files/isono_family.csv}]
2022-05-20 01:33:40.397  INFO 18890 --- [   scheduling-1] o.s.batch.core.job.SimpleStepHandler     : Executing step: [loadFileToDatabaseStep]
2022-05-20 01:33:40.504  INFO 18890 --- [   scheduling-1] o.s.batch.core.step.AbstractStep         : Step: [loadFileToDatabaseStep] executed in 107ms
2022-05-20 01:33:40.571  INFO 18890 --- [   scheduling-1] o.s.batch.core.job.SimpleStepHandler     : Executing step: [deleteFileStep]
2022-05-20 01:33:40.631  INFO 18890 --- [   scheduling-1] o.s.batch.core.step.AbstractStep         : Step: [deleteFileStep] executed in 60ms
2022-05-20 01:33:40.691  INFO 18890 --- [   scheduling-1] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=loadFileToDatabaseJob]] completed with the following parameters: [{run.id=3, input.file.path=/path/to/batch-integration-example/target/files/isono_family.csv}] and the following status: [COMPLETED] in 348ms
2022-05-20 01:33:40.692  INFO 18890 --- [   scheduling-1] job execution result                     : GenericMessage [payload=JobExecution: id=3, version=2, startTime=Fri May 20 01:33:40 JST 2022, endTime=Fri May 20 01:33:40 JST 2022, lastUpdated=Fri May 20 01:33:40 JST 2022, status=COMPLETED, exitStatus=exitCode=COMPLETED;exitDescription=, job=[JobInstance: id=3, version=0, Job=[loadFileToDatabaseJob]], jobParameters=[{run.id=3, input.file.path=/path/to/batch-integration-example/target/files/isono_family.csv}], headers={file_originalFile=target/files/isono_family.csv, id=23cb8db4-88cd-5bfb-6557-aca9275a8b1f, file_name=isono_family.csv, file_relativePath=isono_family.csv, timestamp=1652978020691}]

Jobć®ę§‹ęˆć«RunIdIncrementerć‚’å«ć‚ć¦ć„ć‚‹ć®ć§ć€run.idćŒć‚¤ćƒ³ć‚ÆćƒŖćƒ”ćƒ³ćƒˆć•ć‚Œć¦ć„ć‚‹ć®ć§äøŽćˆć‚‹ćƒ•ć‚”ć‚¤ćƒ«ćƒ‘ć‚¹ćŒåŒć˜ć§ć‚‚å•é”ŒćŖ恏
čµ·å‹•ć—ć¾ć™ć€‚

2022-05-20 01:33:40.252  INFO 18890 --- [   scheduling-1] transform job request                    : GenericMessage [payload=JobLaunchRequest: loadFileToDatabaseJob, parameters={run.id=3, input.file.path=/path/to/batch-integration-example/target/files/isono_family.csv}, headers={file_originalFile=target/files/isono_family.csv, id=f2603171-64a4-9093-0621-0125698264db, file_name=isono_family.csv, file_relativePath=isono_family.csv, timestamp=1652978020252}]

RunIdIncrementerć‚’å«ć‚ćšć«åŒć˜JobParameters恧Jobć‚’čµ·å‹•ć—ćŸå “åˆćÆ态Spring Batchć®å®Ÿč”Œć«å¤±ę•—ć™ć‚‹ć“ćØ恫ćŖć‚Šć¾ć™ć€‚

åŒć˜ćƒ‡ćƒ¼ć‚æ恮updated_timećŒę›“ę–°ć•ć‚Œć¦ć„ć‚‹ć“ćØ悂ē¢ŗčŖć—ć¦ćŠćć¾ć™ć€‚

mysql> select * from person;
+----+-----------+--------------+------+---------------------+
| id | last_name | first_name   | age  | updated_time        |
+----+-----------+--------------+------+---------------------+
|  1 | ē£Æ野      | ć‚«ćƒ„ć‚Ŗ       |   11 | 2022-05-20 01:33:40 |
|  2 | ē£Æ野      | ćƒÆć‚«ćƒ”       |    9 | 2022-05-20 01:33:40 |
|  3 | 惕悰ē”°    | 悵悶ć‚Ø       |   23 | 2022-05-20 01:33:40 |
|  4 | 惕悰ē”°    | ćƒžć‚¹ć‚Ŗ       |   32 | 2022-05-20 01:33:40 |
|  5 | 惕悰ē”°    | ć‚æ惩ć‚Ŗ       |    3 | 2022-05-20 01:33:40 |
|  6 | ę³¢é‡Ž      | 惎ćƒŖć‚¹ć‚±     |   24 | 2022-05-20 01:31:29 |
|  7 | ę³¢é‡Ž      | ć‚æć‚¤ć‚³       |   22 | 2022-05-20 01:31:29 |
|  8 | ę³¢é‡Ž      | 悤ć‚Æ惩       |    1 | 2022-05-20 01:31:29 |
+----+-----------+--------------+------+---------------------+
8 rows in set (0.00 sec)

OK恧恙恭怂

恓悌恧态Spring BatchćØSpring Integration恮ēµ„ćæåˆć‚ć›ć§å‹•ä½œē¢ŗčŖć§ćć¾ć—ćŸć€‚

ć‚Ŗćƒžć‚±ļ¼šRunIdIncrementerć®ä»£ć‚ć‚Šć«JsrJobParametersConverter悒ä½æć£ćŸå “åˆćÆļ¼Ÿ

ä»Šå›žć€RunIdIncrementer悒ä½æē”Øć—ć¾ć—ćŸćŒJsrJobParametersConverter悒ä½æć£ć¦ć‚‚ć‚ˆć„ć®ć§ćÆļ¼ŸćØ恄恆ē–‘å•ć‚’ęŒć£ćŸć‚ŠćÆć—ć¾ć™ć€‚

RunIdIncrementerć‚’ć‚³ćƒ”ćƒ³ćƒˆć‚¢ć‚¦ćƒˆć—ć¦

    @Bean
    public Job loadFileToDatabaseJob(JobBuilderFactory jobBuilderFactory) {
        return jobBuilderFactory
                .get("loadFileToDatabaseJob")
                // .incrementer(new RunIdIncrementer())
                .start(loadFileToDatabaseStep(null))
                .next(deleteFileStep(null))
                .build();
    }

JsrJobParametersConverter悒BeanćØć—ć¦å®šē¾©ć—ć¦ć‚‚ć‚ˆć„ć®ć§ćÆćŖ恄恮恧恗悇恆恋怂

    @Bean
    public JsrJobParametersConverter jsrJobParametersConverter(DataSource dataSource) {
        return new JsrJobParametersConverter(dataSource);
    }

恓悌ćÆć€ć†ć¾ćć„ćć¾ć›ć‚“ć€‚

ć“ć®å®šē¾©ę–¹ę³•ć§JsrJobParametersConverter恌ä½æć‚ć‚Œć‚‹ć®ćÆ态Spring Batch悒JobLauncherApplicationRunner悒ä½æć£ć¦čµ·å‹•ć—ćŸå “åˆć§ć™ć­ć€‚

https://github.com/spring-projects/spring-boot/blob/v2.6.7/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/JobLauncherApplicationRunner.java

悂恗恏ćÆ态JsrJobOperator悒ä½æć£ć¦ć‚‚ć‚ˆć•ćć†ć§ćÆć‚ć‚Šć¾ć™ć€‚

https://github.com/spring-projects/spring-batch/blob/4.3.5/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/launch/JsrJobOperator.java

Spring Batch Integrationć‚’ä»‹ć—ć¦å®Ÿč”Œć§ććŖ恕恝恆恧ćÆć‚ć‚Šć¾ć™ćŒā€¦ć€‚

ć¾ćØ悁

Spring BatchćØSpring Integration悒ēµ„ćæåˆć‚ć›ćŸć€Spring Batch Integration悒試恗恦ćæć¾ć—ćŸć€‚

ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆćŒå°‘ćŖ恄恮恧Spring BatchćØSpring Integration恮恤ćŖćŽč¾¼ćæćÆć‚„ć‚„č‹¦åŠ“ć—ć¾ć—ćŸćŒć€ćć“ć‚’ć‚ÆćƒŖć‚¢ć™ć‚Œć°Spring BatchćØ
Spring Integrationćć‚Œćžć‚Œć«ēŸ„識恌恂悌恰ē°”å˜ć«ä½æćˆć‚‹ę„Ÿć˜ćŒć—ć¾ć—ćŸć­ć€‚

Spring Integration恮Inbound Channel Adapterć‚’ä»‹ć—ć¦å–å¾—ć™ć‚‹Messageć®å˜ä½ćŒSpring Batch恮Jobć®čµ·å‹•å˜ä½ć«ćŖ悋恮恧态
ćć®ē²’åŗ¦ć§å¤§äøˆå¤«ćŖ悉Spring Integrationć®ę©Ÿčƒ½ćŒä½æćˆć‚‹ć®ć§Spring Batch悒ä½æć£ćŸč”Øē¾ēÆ„å›²ćŒå¤§ćććŖ悋恮ćÆć‚ˆćć‚ć‹ć‚Šć¾ć—ćŸć€‚

č¦šćˆć¦ćŠććØ悈恕恝恆恧恙恭怂