2021/9/4
こんにちは。今回は前回まで作成していたWEBアプリの補足としてバッチによるユーザレコードの挿入を実装してみます。
バッチ用に新たなプロジェクトを作成します。
eclipse、ファイル -> 新規 -> その他の順に押下。
Spring BootのSpring スターター・プロジェクトを押下。
パッケージの名前のみ"demobatch"にして次へを押下。
Spring Batch、Lombok、Mybatis Framework、PostgresSQL Driverを選択します。
pom.xmlに先ほど選択したライブラリが追加されましたが、
MAVEN 構成問題のエラーが発生したので、<properties>のタグに下記を追加します。
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
他にもビルド時の自動テストの解除を追加しました。
<maven.test.skip>true</maven.test.skip>
修正後のpom.xmlを記載します。
/demobatch/pom.xml
Spring Bootの設定
/demobatch/src/main/resources/application.properties
Spring Boot起動時に実行されるSQL
/demobatch/src/main/resources/schema.sql
/demobatch/src/main/resources/data.sql
ログの設定
/demobatch/src/main/resources/logback-spring.xml
これまでに作成したWEBアプリは、ユーザ登録する場合にログイン処理を行い、一人ずつ一覧画面 -> 入力画面 -> 完了画面に遷移し、処理をする必要があります。
通常業務ではこれで良いのですが、一度に1000人分のユーザを手入力するときはきつくなります。
そこで、夜中などに一括でユーザを登録することを想定してバッチ処理を作成します。
Spring Batchは、バッチ処理のおおきな単位としてジョブがあり、そのジョブに対して実際の処理を定義したステップという単位を定めることで構成されます。
ステップは大きく分けると、指定された件数ごとにバッチ処理を行うchunkと一括でバッチ処理を行うtaskletがあります。
今回は一度に処理するtaskletの方で実装をすすめていきます。
こんにちは。今回は前回まで作成していたWEBアプリの補足としてバッチによるユーザレコードの挿入を実装してみます。
前準備
バッチ用に新たなプロジェクトを作成します。
eclipse、ファイル -> 新規 -> その他の順に押下。
Spring BootのSpring スターター・プロジェクトを押下。
パッケージの名前のみ"demobatch"にして次へを押下。
Spring Batch、Lombok、Mybatis Framework、PostgresSQL Driverを選択します。
pom.xmlに先ほど選択したライブラリが追加されましたが、
MAVEN 構成問題のエラーが発生したので、<properties>のタグに下記を追加します。
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<maven.test.skip>true</maven.test.skip>
修正後のpom.xmlを記載します。
/demobatch/pom.xml
<?xml version="1.0" encoding="UTF-8"?> <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 https://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> <!-- 2.2.6 --> <version>2.2.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demobatch</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demobatch</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <!-- 不明なエラー解消 --> <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version> <!-- ビルド時、自動テストしない --> <maven.test.skip>true</maven.test.skip> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-batch</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.0</version> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.batch</groupId> <artifactId>spring-batch-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Spring Bootの設定
/demobatch/src/main/resources/application.properties
#DB接続の設定 spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.url=jdbc:postgresql://localhost:5432/web?web_schema spring.datasource.username=dev spring.datasource.password=xxxx #起動するごとにDBを初期化する spring.datasource.initialization-mode=always # Spring Batchの実行結果をSpring指定のDBスキーマに出力する spring.batch.initialize-schema: always
Spring Boot起動時に実行されるSQL
/demobatch/src/main/resources/schema.sql
--DDL --usersテーブルが存在しなければ作成 CREATE TABLE IF NOT EXISTS web_schema.users ( id serial PRIMARY KEY, name VARCHAR(30), email VARCHAR(50), address VARCHAR(255), sex VARCHAR(1), remark VARCHAR(255) );
/demobatch/src/main/resources/data.sql
--DML --usersテーブルの削除 DELETE FROM web_schema.users;
ログの設定
/demobatch/src/main/resources/logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE logback> <configuration> <!--デフォルト設定読み込み --> <include resource="org/springframework/boot/logging/logback/base.xml" /> <!--変数の設定 --> <property name="logFilePath" value="C:/log/" /> <property name="logFileName" value="batch" /> <!--標準出力 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <target>System.out</target> <encoder> <charset>UTF-8</charset> <!--level ロギングイベントのレベル --> <!--message ロギングイベントに関連付けられたメッセージ --> <pattern>%d{yyyy/MM/dd HH:mm:ss} %-5level [%thread] - %message%n</pattern> </encoder> </appender> <!-- アプリケーションログ --> <appender name="BATCH_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 出力先ファイルパス --> <file>${logFilePath}${logFileName}.log</file> <!-- ログのローテーション設定 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!--日付けが変わったら年月フォルダ内にzipファイル作成 --> <fileNamePattern>${logFilePath}/%d{yyyyMM,aux}/${logFileName}-%d{yyyy-MM-dd}.log.zip</fileNamePattern> <!--最大30日間保存 --> <maxHistory>30</maxHistory> </rollingPolicy> <encoder> <charset>UTF-8</charset> <pattern>%d{yyyy/MM/dd HH:mm:ss} %-5level [%thread] - %message%n</pattern> </encoder> </appender> <!--rootロガー、コンソールとバッチログに出力する --> <root level="INFO"> <appender-ref ref="STDOUT" /> <appender-ref ref="BATCH_LOG" /> </root> </configuration>
バッチ処理
シナリオ
これまでに作成したWEBアプリは、ユーザ登録する場合にログイン処理を行い、一人ずつ一覧画面 -> 入力画面 -> 完了画面に遷移し、処理をする必要があります。
通常業務ではこれで良いのですが、一度に1000人分のユーザを手入力するときはきつくなります。
そこで、夜中などに一括でユーザを登録することを想定してバッチ処理を作成します。
バッチ構成
Spring Batchは、バッチ処理のおおきな単位としてジョブがあり、そのジョブに対して実際の処理を定義したステップという単位を定めることで構成されます。
ステップは大きく分けると、指定された件数ごとにバッチ処理を行うchunkと一括でバッチ処理を行うtaskletがあります。
今回は一度に処理するtaskletの方で実装をすすめていきます。
引用:ジョブの構成と実行
web_schema.usersに登録するための入力情報になります。
/demobatch/src/main/resources/users_ins.xml
taskletを実行するための設定クラスを作成します。
ここではjob1というジョブがstep1のステップを呼び出し、UserInsertTaskletを実行させています。
/demobatch/src/main/java/com/example/demo/configuration/UserInsertTaskletConfiguration.java
taskletの実装クラスになります。
Taskletのインターフェースを実装し、入力情報のxmlをUsersオブジェクトに変換。データベースへの登録処理を行います。
/demobatch/src/main/java/com/example/demo/tasklet/UserInsertTasklet.java
ジョブの処理前後にログを出力するリスナークラスです。
/demobatch/src/main/java/com/example/demo/listener/JobListener.java
xmlからデータを取得するusersクラスです。@XmlRootElement、@XmlElementを付けて取得するタグを設定します。
/demobatch/src/main/java/com/example/demo/parse/Users.java
UsersテーブルへのデータをマッピングするためのEntityです。
/demobatch/src/main/java/com/example/demo/domain/User.java
Mybatisを設定するためのMapperです。実装はMybatisの方で行ってくれます。
/demobatch/src/main/java/com/example/demo/mapper/UserMapper.java
Mybatisで実行されるSQLを記載したXMLです。今回はeclipseからでなく、Jarからバッチ実行しますのでMapperと同じ場所に置いてません。
/demobatch/src/main/resources/com/example/demo/mapper/UserMapper.xml
それでは実行してみましょう。
プロジェクトを右クリックし、実行からMaven installをクリック。バッチをJarにコンパイルします。
/demobatch/target/demobatch-0.0.1-SNAPSHOT.jarができますので、targetフォルダに移動して下記のコマンドを実行します。
※今回はeclipseのターミナルでなく、Windowsのターミナルから実行します。
chcp 65001(UTF-8表示)
java -jar demobatch-0.0.1-SNAPSHOT.jar --spring.batch.job.names=jobjava -jar demobatch-0.0.1-SNAPSHOT.jar --spring.batch.job.names=job1
参考
Spring Batch - リファレンスドキュメント
Spring Boot Batchの作成方法
Classpath resource not found when running as jar
お借りした素材
NYN姉貴.png
ブチ切れNYN姉貴BB
SZKのPCの切り抜き
よかったら使ってください 2
JAVA おすすめの本
タスクレットの実装
web_schema.usersに登録するための入力情報になります。
/demobatch/src/main/resources/users_ins.xml
<?xml version="1.0" encoding="UTF-8" ?> <users> <user> <name>武田信玄</name> <email>takeda@xxxxx.co.jp</email> <address>山梨県</address> <sex>1</sex> <remark>-</remark> </user> <user> <name>源頼朝</name> <email>minamoto@xxxxx.co.jp</email> <address>神奈川県</address> <sex>1</sex> <remark>-</remark> </user> <user> <name>足利尊氏</name> <email>asikaga@xxxxx.co.jp</email> <address>栃木県</address> <sex>1</sex> <remark>-</remark> </user> </users>
taskletを実行するための設定クラスを作成します。
ここではjob1というジョブがstep1のステップを呼び出し、UserInsertTaskletを実行させています。
/demobatch/src/main/java/com/example/demo/configuration/UserInsertTaskletConfiguration.java
package com.example.demo.configuration; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobExecutionListener; import org.springframework.batch.core.Step; 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.launch.support.RunIdIncrementer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.example.demo.listener.JobListener; import com.example.demo.tasklet.UserInsertTasklet; /** * * Spring Batch 勉強用 Taskletを動かす設定 * * @version 1.0.0 */ @Configuration // バッチ有効化 @EnableBatchProcessing public class UserInsertTaskletConfiguration { @Autowired private JobBuilderFactory jobBuilderFactory; @Autowired private StepBuilderFactory stepBuilderFactory; @Autowired private UserInsertTasklet tasklet; // @Configulationアノテーション下、@Componentでなく、起動時に設定される@Beanを設定 @Bean public Job job1(Step step1) { return jobBuilderFactory.get("job1") .incrementer(new RunIdIncrementer()) .listener(listener()) .start(step1) .build(); } @Bean public Step step1() { return stepBuilderFactory.get("step1").tasklet(tasklet).build(); } @Bean public JobExecutionListener listener() { // 処理前後にメッセージ出力 return new JobListener(); } }
taskletの実装クラスになります。
Taskletのインターフェースを実装し、入力情報のxmlをUsersオブジェクトに変換。データベースへの登録処理を行います。
/demobatch/src/main/java/com/example/demo/tasklet/UserInsertTasklet.java
package com.example.demo.tasklet; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.List; import javax.xml.bind.JAXB; import javax.xml.bind.Unmarshaller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.repeat.RepeatStatus; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import com.example.demo.domain.User; import com.example.demo.mapper.UserMapper; import com.example.demo.parse.Users; /** * * Spring Batch 勉強用 タスク * * @version 1.0.0 */ //DI コンテナに登録 @Component public class UserInsertTasklet implements Tasklet { private static final String FILE_PATH = "users_ins.xml"; private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private UserMapper userMapper; private Unmarshaller unmarshaller; public Unmarshaller getUnmarshaller() { return unmarshaller; } public void setUnmarshaller(Unmarshaller unmarshaller) { this.unmarshaller = unmarshaller; } /* * users_ins.xmlからusersテーブルへデータを登録する * * {@inheritDoc} */ @Override @Transactional public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { Resource resource = new ClassPathResource(FILE_PATH); BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream())); // xmlをオブジェクトに変換 Users users = JAXB.unmarshal(reader, Users.class); List<User> userList = users.getUsers(); for (User user : userList) { int result = userMapper.insert(user); // 登録結果をログに出力 logger.info("[登録結果] " + String.valueOf(result) + "件登録"); } return RepeatStatus.FINISHED; } }
ジョブの処理前後にログを出力するリスナークラスです。
/demobatch/src/main/java/com/example/demo/listener/JobListener.java
package com.example.demo.listener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.listener.JobExecutionListenerSupport; /** * * Spring Boot 勉強用 ジョブリスナー * * @version 1.0.0 */ public class JobListener extends JobExecutionListenerSupport { private final Logger logger = LoggerFactory.getLogger(this.getClass()); /* * {@inheritDoc} */ @Override public void beforeJob(JobExecution jobExecution) { super.beforeJob(jobExecution); logger.info("ジョブ開始"); } /* * {@inheritDoc} */ @Override public void afterJob(JobExecution jobExecution) { super.afterJob(jobExecution); logger.info("ジョブ終了"); } }
xmlからデータを取得するusersクラスです。@XmlRootElement、@XmlElementを付けて取得するタグを設定します。
/demobatch/src/main/java/com/example/demo/parse/Users.java
package com.example.demo.parse; import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import com.example.demo.domain.User; /** * * Spring Batch 勉強用 * xmlをパースしたUserクラスを格納する * * @version 1.0.0 */ // 取得するxmlのルートタグ @XmlRootElement(name = "users") public class Users { private List<User> users; // 取得するxmlのタグ @XmlElement(name = "user") public List<User> getUsers() { return users; } public void setUsers(List<User> users) { this.users = users; } @Override public String toString() { if (users == null) { return "users=null"; } return "Users [users=" + users.toString() + "]"; } }
UsersテーブルへのデータをマッピングするためのEntityです。
/demobatch/src/main/java/com/example/demo/domain/User.java
package com.example.demo.domain; import lombok.Data; /** * * Spring Batch 勉強用 エンティティ * UsersテーブルのデータをマッピングするためのEntity * * @version 1.0.0 */ @Data public class User { private String id; private String name; private String email; private String address; private String sex; private String remark; }
Mybatisを設定するためのMapperです。実装はMybatisの方で行ってくれます。
/demobatch/src/main/java/com/example/demo/mapper/UserMapper.java
package com.example.demo.mapper; import org.apache.ibatis.annotations.Mapper; import com.example.demo.domain.User; /** * * Spring Boot 勉強用 モデル * * @version 1.0.0 */ @Mapper public interface UserMapper { /* * usersテーブルにデータを登録する * * @param user 登録用データ * @return 処理結果 */ int insert(User user); }
Mybatisで実行されるSQLを記載したXMLです。今回はeclipseからでなく、Jarからバッチ実行しますのでMapperと同じ場所に置いてません。
/demobatch/src/main/resources/com/example/demo/mapper/UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.demo.mapper.UserMapper"> <!--usersテーブルにデータを登録する --> <insert id="insert" useGeneratedKeys="true" keyProperty="id" parameterType="com.example.demo.domain.User"> INSERT INTO web_schema.users (name, email, address, sex, remark) VALUES (#{name}, #{email}, #{address}, #{sex}, #{remark}); </insert> </mapper>
Jarにしてからバッチを実行
それでは実行してみましょう。
プロジェクトを右クリックし、実行からMaven installをクリック。バッチをJarにコンパイルします。
/demobatch/target/demobatch-0.0.1-SNAPSHOT.jarができますので、targetフォルダに移動して下記のコマンドを実行します。
※今回はeclipseのターミナルでなく、Windowsのターミナルから実行します。
chcp 65001(UTF-8表示)
java -jar demobatch-0.0.1-SNAPSHOT.jar --spring.batch.job.names=jobjava -jar demobatch-0.0.1-SNAPSHOT.jar --spring.batch.job.names=job1
参考
Spring Batch - リファレンスドキュメント
Spring Boot Batchの作成方法
Classpath resource not found when running as jar
お借りした素材
NYN姉貴.png
ブチ切れNYN姉貴BB
SZKのPCの切り抜き
よかったら使ってください 2
JAVA おすすめの本
0 件のコメント:
コメントを投稿