Anarchy In the 1K

CommandLineJobRunnerを用いたSpring Batchの実行

目次

背景

 前回こちらの記事でSpring Boot Batchを用いたバッチ実行を行いました。今回は、コマンドラインからのバッチ実行を試みます。
 加えて、今回はSpring Bootを用いずに実装を行うことで、前回自動で設定されていた部分を手動で設定します。

ハンズオン

事前準備

DBの用意

 こちらの記事を元に、PostgreSQLを用意します。その後、Spring Batchで用いる以下テーブルを以下の通り作成します。

CREATE TABLE BATCH_JOB_INSTANCE  (
    JOB_INSTANCE_ID BIGINT  NOT NULL PRIMARY KEY ,
    VERSION BIGINT ,
    JOB_NAME VARCHAR(100) NOT NULL,
    JOB_KEY VARCHAR(32) NOT NULL,
    constraint JOB_INST_UN unique (JOB_NAME, JOB_KEY)
) ;

CREATE TABLE BATCH_JOB_EXECUTION  (
    JOB_EXECUTION_ID BIGINT  NOT NULL PRIMARY KEY ,
    VERSION BIGINT  ,
    JOB_INSTANCE_ID BIGINT NOT NULL,
    CREATE_TIME TIMESTAMP NOT NULL,
    START_TIME TIMESTAMP DEFAULT NULL ,
    END_TIME TIMESTAMP DEFAULT NULL ,
    STATUS VARCHAR(10) ,
    EXIT_CODE VARCHAR(2500) ,
    EXIT_MESSAGE VARCHAR(2500) ,
    LAST_UPDATED TIMESTAMP,
    JOB_CONFIGURATION_LOCATION VARCHAR(2500) NULL,
    constraint JOB_INST_EXEC_FK foreign key (JOB_INSTANCE_ID)
    references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID)
) ;

CREATE TABLE BATCH_JOB_EXECUTION_PARAMS  (
    JOB_EXECUTION_ID BIGINT NOT NULL ,
    TYPE_CD VARCHAR(6) NOT NULL ,
    KEY_NAME VARCHAR(100) NOT NULL ,
    STRING_VAL VARCHAR(250) ,
    DATE_VAL TIMESTAMP DEFAULT NULL ,
    LONG_VAL BIGINT ,
    DOUBLE_VAL DOUBLE PRECISION ,
    IDENTIFYING CHAR(1) NOT NULL ,
    constraint JOB_EXEC_PARAMS_FK foreign key (JOB_EXECUTION_ID)
    references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ;

CREATE TABLE BATCH_STEP_EXECUTION  (
    STEP_EXECUTION_ID BIGINT  NOT NULL PRIMARY KEY ,
    VERSION BIGINT NOT NULL,
    STEP_NAME VARCHAR(100) NOT NULL,
    JOB_EXECUTION_ID BIGINT NOT NULL,
    START_TIME TIMESTAMP NOT NULL ,
    END_TIME TIMESTAMP DEFAULT NULL ,
    STATUS VARCHAR(10) ,
    COMMIT_COUNT BIGINT ,
    READ_COUNT BIGINT ,
    FILTER_COUNT BIGINT ,
    WRITE_COUNT BIGINT ,
    READ_SKIP_COUNT BIGINT ,
    WRITE_SKIP_COUNT BIGINT ,
    PROCESS_SKIP_COUNT BIGINT ,
    ROLLBACK_COUNT BIGINT ,
    EXIT_CODE VARCHAR(2500) ,
    EXIT_MESSAGE VARCHAR(2500) ,
    LAST_UPDATED TIMESTAMP,
    constraint JOB_EXEC_STEP_FK foreign key (JOB_EXECUTION_ID)
    references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ;

CREATE TABLE BATCH_STEP_EXECUTION_CONTEXT  (
    STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
    SHORT_CONTEXT VARCHAR(2500) NOT NULL,
    SERIALIZED_CONTEXT TEXT ,
    constraint STEP_EXEC_CTX_FK foreign key (STEP_EXECUTION_ID)
    references BATCH_STEP_EXECUTION(STEP_EXECUTION_ID)
) ;

CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT  (
    JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
    SHORT_CONTEXT VARCHAR(2500) NOT NULL,
    SERIALIZED_CONTEXT TEXT ,
    constraint JOB_EXEC_CTX_FK foreign key (JOB_EXECUTION_ID)
    references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ;

CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ MAXVALUE 9223372036854775807 NO CYCLE;
CREATE SEQUENCE BATCH_JOB_EXECUTION_SEQ MAXVALUE 9223372036854775807 NO CYCLE;
CREATE SEQUENCE BATCH_JOB_SEQ MAXVALUE 9223372036854775807 NO CYCLE;

プロジェクトの作成

 以下コマンドを発行し、プロジェクトを作成します。

mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4

作成されたpom.xmlに以下を追記します。

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!-- 追記箇所↓ -->
    <!-- https://mvnrepository.com/artifact/org.springframework.batch/spring-batch-core -->
    <dependency>
      <groupId>org.springframework.batch</groupId>
      <artifactId>spring-batch-core</artifactId>
      <version>4.2.4.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.8.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/commons-dbcp/commons-dbcp -->
    <dependency>
      <groupId>commons-dbcp</groupId>
      <artifactId>commons-dbcp</artifactId>
      <version>1.2.2</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
    <dependency>
      <groupId>org.postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <version>42.2.14</version>
      <scope>runtime</scope>
    </dependency>
    <!-- https:/
    /mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.12</version>
      <scope>provided</scope>
    </dependency>
    <!-- 追記箇所↑ -->
  </dependencies>

コーディング

 こちらのサイトのサンプルを実装します。以下では、変更点や重要な箇所を解説します。また、作成したプロジェクトに関して、こちらに配置しています。

設定ファイル

Spring Batchの設定

 src/main/resources/common-batch-context.xml上で、Spring Batchの設定を行います。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       xsi:schemaLocation="http://www.springframework.org/schema/batch
  http://www.springframework.org/schema/batch/spring-batch-3.0.xsd
  http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    <!-- Spring Batchで必要になるBeanの定義 -->
    <bean id="jobRepository"
          class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean"
          p:dataSource-ref="dataSource" p:transactionManager-ref="transactionManager"
          p:maxVarCharLength="2000" p:isolationLevelForCreate="ISOLATION_SERIALIZABLE" />
    <bean id="jobExplorer" class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean"
          p:dataSource-ref="dataSource" />
    <bean id="jobLauncher"
          class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
        <property name="jobRepository" ref="jobRepository" />
    </bean>
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager" lazy-init="true">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- DBの設定 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="org.postgresql.Driver" />
        <property name="url" value="jdbc:postgresql://localhost:5432/postgres" />
        <property name="username" value="postgres" />
        <property name="password" value="password" />
    </bean>
</beans>

各Beanの説明はこちらをご覧ください。

ジョブ個別の設定

 src/main/resources/job-setting.xml上で、ジョブ個別の設定を行う。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/batch
    http://www.springframework.org/schema/batch/spring-batch-3.0.xsd
     http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    <!--  SpringBatch共通の設定  -->
    <import resource="classpath:/common-batch-context.xml"/>

    <!--  ジョブの設定  -->
    <job id="job1" xmlns="http://www.springframework.org/schema/batch"
         incrementer="jobParametersIncrementer">
        <step id="step1" parent="simpleStep">
            <tasklet>
                <chunk reader="fileItemReader" writer="fileItemWriter"/>
            </tasklet>
        </step>
    </job>

    <bean id="jobParametersIncrementer"
          class="org.springframework.batch.core.launch.support.RunIdIncrementer" />
    <bean id="simpleStep"
          class="org.springframework.batch.core.step.item.SimpleStepFactoryBean" abstract="true">
        <property name="jobRepository" ref="jobRepository" />
        <property name="commitInterval" value="1" />
    </bean>

    <bean id="fileItemReader"
          class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
        <property name="resource" value="classpath:./input.csv" />
        <property name="lineMapper">
            <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
                <property name="lineTokenizer">
                    <bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
                        <property name="names" value="num,name" />
                    </bean>
                </property>
                <property name="fieldSetMapper">
                    <bean class="work.garaku.code.example.MemberFieldSetMapper" />
                </property>
            </bean>
        </property>
    </bean>

    <bean id="fileItemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
        <property name="resource" value="file:./src/main/resources/output.txt" />
        <property name="lineAggregator">
            <bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
                <property name="fieldExtractor">
                    <bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor" >
                        <property name="names" value="name,num" />
                    </bean>
                </property>
            </bean>
        </property>
    </bean>
</beans>
  1. 前述の「Spring Batchの設定」で記載した設定ファイルの読み込みを行う。
  2. ジョブの設定を以下の通り設定を行う。
    • incrementer: JobInstanceはジョブ名とジョブパラメータ毎に生成される為、incrementerが一意なパラメータを渡すことで、実行の都度一意なJobInstanceが生成されます。*1
    • parent: parentに指定したStepを継承し、プロパティを書き換えることが可能です。詳しくはこちらをご覧ください。
    • chunk: 後続で設定している、reader, writerの設定を行います。

Javaソース

 サンプルをまんま拝借しました。

ファイル

 src/main/resources配下に以下の通りinput.csvを配置します。

1,山田
2,田中

実行

 以下の通りコマンドを発行します。

# libフォルダにライブラリを出力します。
mvn dependency:copy-dependencies -DoutputDirectory=lib
# ビルドします。
mvn clean package
# 実行します。
java -cp 'target/spring_batch_commandline_sample-1.0-SNAPSHOT.jar:lib/*' org.springframework.batch.core.launch.support.CommandLineJobRunner -next job-setting.xml job1

 org.springframework.batch.core.launch.support.CommandLineJobRunnerを用いた実行に関して、詳しくはこちらをご覧ください。また、引数で指定した-nextに関して、incrementerを用いた実行の為に必要になります。詳しくはこちらもご覧ください。

*1:合わせて次もご覧ください。https://fujiu.hatenablog.com/entry/2020/08/01/174618#2回目