周回レースの集計データの処理を想定した「どんどんレコード(行)が追加されていく複数のテキストデータを一定間隔(1秒)で読んで、追加されたレコードをDBに登録していく」バッチ処理(Spring
Batch 6)の話のつづき。
ちなみに(1)で「仕様」に書き忘れてたけど、データベースへのアクセスは JDBC ではなく JPA を使っています。
俺的には全然 SQL は苦手ではないし、もっと言えば複雑な SQL を書くのは好きな方ですが(笑)、一昨年から昨年にかけて C#.NET
で「DB操作は LINQ」って案件やって、ああ、糞っ、直接生の SQL 書けば一発なのにと苦労した記憶があるので(^^;;;、敢えて JPA
を選んでみたわけです。勉強のために。
※LINQ でも直接 SQL 書けるじゃんってツッコミは無しで願います。言語仕様の話ではなく、コーディングルールで禁止されていたということなのよ。
ま、とういうわけで、今回はプロパティファイルや、テーブルの設定などを。
■Apache Maven プロジェクト設定ファイル(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.0https://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>4.1.0</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>BatchTest5</artifactId><version>0.0.1-SNAPSHOT</version><name>BatchTest5</name><description/><url/><licenses><license/></licenses><developers><developer/></developers><scm><connection/><developerConnection/><tag/><url/></scm><properties><java.version>21</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-h2console</artifactId></dependency><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-webmvc</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>com.h2database</groupId><artifactId>h2</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-batch-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webmvc-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><executions><execution><id>default-compile</id><phase>compile</phase><goals><goal>compile</goal></goals><configuration><annotationProcessorPaths><path><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></path></annotationProcessorPaths></configuration></execution><execution><id>default-testCompile</id><phase>test-compile</phase><goals><goal>testCompile</goal></goals><configuration><annotationProcessorPaths><path><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></path></annotationProcessorPaths></configuration></execution></executions></plugin></plugins></build></project>
他のエントリーでも書いたけど、バッチ処理だけど H2 コンソールを動かすために tomcat
が必要なので、spring-boot-starter-webmvc モジュールが依存関係(dependency)として登録されていること。
■プロパティ(resource/application.properties)
spring.application.name=BatchTest5# ファイル一覧app.files-list-path=C:\\work\\files.txt# H2 を PostgreSQL 互換モードで使用(テーブル名等は小文字で)spring.datasource.url=jdbc:h2:./.data/h2/db;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUEspring.datasource.driver-class-name=org.h2.Driverspring.datasource.username=saspring.datasource.password=# H2 Console を有効化spring.h2.console.enabled=truespring.h2.console.path=/h2-console# JPA/Hibernate設定spring.jpa.database-platform=org.hibernate.dialect.H2Dialectspring.jpa.hibernate.ddl-auto=updatespring.jpa.show-sql=truespring.batch.job.enabled=false# Webアプリケーションとして常駐spring.main.web-application-type=servlet
特に注意するのは
spring.batch.job.enabled=false
かな。
これは起動時に自動でバッチジョブを実行しないようにしている。
Spring Boot はバッチ用のライブラリ(spring-boot-starter-batch)を検知すると、定義されている @Bean
のジョブを起動時にすべて片っ端から実行しようとしてしまう。今回はスケジューラでパラメータ(読み込むファイル名とか)を与えてジョブを起動する形にしているから、勝手にジョブを起動されちゃうと「パラメータが無い状態」の実行となりエラーが発生する。なので「自動で起動すんなよ」と抑制しているわけやね。
あと、app.files-list-path は「'app.files-list-path' is an unknown property.
[PROP_UNKNOWN_PROPERTY]」という警告が出るので、プロパティを認識させるためのメタデータを作ってやる。
■メタデータファイル(resource/META-INF/additional-spring-configuration-metadata.json)
{"properties": [{"name": "app.files-list-path","type": "java.lang.String","description": "A description for 'app.files-list-path'"}]}
■Spring Boot メインクラス(BatchTest5Application.java)
package com.netandfield.test;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.scheduling.annotation.EnableScheduling;@SpringBootApplication@EnableSchedulingpublic class BatchTest5Application {public static void main(String[] args) {SpringApplication.run(BatchTest5Application.class, args);}}
Eclipse で Spring Batch のバッチを実行するときは、このファイルを Java アプリケーションとして実行する。
■DBマッピングクラス(FileProgress.java)
※各ファイルが何行目まで読まれているかを保持するテーブル(file_progress)
package com.netandfield.test;import jakarta.persistence.Entity;import jakarta.persistence.Id;import jakarta.persistence.Table;import lombok.Data;@Entity@Table(name = "file_progress")@Data// 各ファイルの読み込み行数を管理するエンティティpublic class FileProgress {@Idprivate String filePath; // ファイルのフルパスprivate int lastReadLines; // 前回までに読み込み完了した行数}
■DBマッピングクラス(ProcessedData.java)
※読み込まれたデータが保存されるテーブル(processed_data)
package com.netandfield.test;import jakarta.persistence.Column;import jakarta.persistence.Entity;import jakarta.persistence.GeneratedValue;import jakarta.persistence.GenerationType;import jakarta.persistence.Id;import jakarta.persistence.Table;import lombok.Data;@Entity@Table(name = "processed_data")@Data// データを保存するエンティティpublic class ProcessedData {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String code;private String name;// データベース上の列名を「data_value」に退避させる(value は予約語なので)@Column(name = "data_value")private Integer value;}
id は、@Id アノテーションで主キーに設定され、データベースの Auto Increment機能を利用して自動採番されている。
id が 1~5までは続いて、急に 33に飛んでいるのは、DBに採番を依頼するとき、ある程度まとめて採番してもらっているから。
今回は、一度に 32個の ID を発行し、読み込んだデータに順に割り当てている。足らなければ再度採番依頼をするが、今回は最初の処理では
5件しかデータが無かったので、5番まで使って 6~32は捨てている。
そして、ファイルに新しい行が追加されたので次の処理が実行されるが、この時に前回の続きで、「33~64までの32個」のIDを採番し先頭から新しいレコードの
id に割り当てたというわけである。
せっかく採番した ID が捨てられるのはもったいないということなら、この同時採番される数を 1 にしても良いが、当然
1レコードずつ採番要求が発生するので処理スピードは落ちる。
■FileProgressクラスのインタフェース(FileProgressRepository.java)
package com.netandfield.test;import org.springframework.data.jpa.repository.JpaRepository;public interface FileProgressRepository extendsJpaRepository<FileProgress, String> {}
org.springframework.data.jpa.repository.JpaRepository を継承しており、findById
や save といったメソッドが実行可能に。

コメントする