かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Gradle で Multi-project を作成する ( その9 )( doma2lib+cmdapp+webapp編、sample-cmdapp プロジェクトを作成する )

概要

記事一覧はこちらです。

Gradle で Multi-project を作成する ( その8 )( doma2lib+cmdapp+webapp編、doma2-lib プロジェクトを作成する ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • Doma 2 の Entity、Dao を提供するライブラリ+Spring Boot ベースのコマンドラインアプリケーション+Spring Boot ベースの Web アプリケーションの Multi-project を作成します。
    • 今回は sample-cmdapp プロジェクトを作成します。

参照したサイト・書籍

  1. univocity-parsers/src/test/java/com/univocity/parsers/examples/RoutineExamples.java
    https://github.com/uniVocity/univocity-parsers/blob/master/src/test/java/com/univocity/parsers/examples/RoutineExamples.java

目次

  1. Spring Initializr で sample-cmdapp プロジェクトを作成する
  2. sample-cmdapp の build.gradle を変更する
  3. gradle-multiprj-doma2lib-cmdwebapp の settings.gradle に include 'sample-cmdapp' を追加する
  4. gradle-multiprj-doma2lib-cmdwebapp の build.gradle の configure の適用対象に sample-cmdapp を追加する
  5. CSV ファイルを読み込んで DB のテーブルにデータを登録する機能を実装する
  6. 続く。。。

手順

Spring Initializr で sample-cmdapp プロジェクトを作成する

IntelliJ IDEA から Spring Initializr を利用して sample-cmdapp プロジェクトを作成します。

f:id:ksby:20190430184635p:plainf:id:ksby:20190430184741p:plain
f:id:ksby:20190430184829p:plainf:id:ksby:20190430184904p:plain

※dependencies のダイアログでは何もチェックしません。

f:id:ksby:20190430185000p:plain

作成後 IntelliJ IDEA のウィンドウが開きますが、何もせずに閉じます。

src ディレクトリと build.gradle だけ残して、それ以外のディレクトリファイルは全て削除します。

また sample-cmdapp/src/test/java/ksbysample/app/samplecmdapp/SampleCmdappApplicationTests.java も削除し、sample-cmdapp/src/test/java/ の下はクリアします。

sample-cmdapp の build.gradle を変更する

sample-cmdapp の build.gradle を以下のように変更します。

dependencies {
    implementation("org.springframework.boot:spring-boot-starter")

    implementation project(":doma2-lib")
    implementation("com.univocity:univocity-parsers:2.8.1")
    implementation("args4j:args4j:2.33")
    implementation("com.github.rozidan:modelmapper-spring-boot-starter:1.0.0")
}
  • doma2-lib プロジェクトへの依存関係と、CSV ファイルを処理するためのライブラリである univocity-parsers、コマンドラインオプションを処理するためのライブラリ args4j、ModelMapper を利用するためのライブラリ com.github.rozidan:modelmapper-spring-boot-starter への依存関係を追加します。

gradle-multiprj-doma2lib-cmdwebapp の settings.gradle に include 'sample-cmdapp' を追加する

settings.gradle に include 'sample-cmdapp' を追加します。

rootProject.name = 'gradle-multiprj-doma2lib-cmdwebapp'
include 'doma2-lib'
include 'sample-cmdapp'

gradle-multiprj-doma2lib-cmdwebapp の build.gradle の configure の適用対象に sample-cmdapp を追加する

gradle-multiprj-doma2lib-cmdwebapp の build.gradle の configure の適用対象に sample-cmdapp を追加します。

configure(subprojects.findAll { it.name ==~ /^(doma2-lib|sample-cmdapp)$/ }) {
    apply plugin: "org.springframework.boot"

    dependencyManagement {
        imports {
            mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)
        }
    }

    ext {
        jdbcDriver = "mysql:mysql-connector-java:8.0.15"
        domaVersion = "2.24.0"
    }
    
    dependencies {
        testImplementation("org.springframework.boot:spring-boot-starter-test")

        runtimeOnly(jdbcDriver)
        implementation("org.seasar.doma.boot:doma-spring-boot-starter:1.1.1")
        implementation("org.seasar.doma:doma:${domaVersion}")
        implementation("org.flywaydb:flyway-core:5.2.4")
    }

}
  • /^(doma2-lib)$//^(doma2-lib|sample-cmdapp)$/ に変更します。

変更後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。Gradle Tool Window に sample-cmdapp が表示されます。

f:id:ksby:20190430190713p:plain

また Run/Debug Configuration の Spring Boot の所に doma2-lib と sample-cmdapp の2つの設定が追加されるので、IntelliJ IDEA の画面右下に「Run Dashboard」のダイアログが表示されます。「Show run configurations in Run Dashboard」リンクをクリックして Run Dashboard Tool Window を表示します。

f:id:ksby:20190430190448p:plain f:id:ksby:20190430190559p:plain

Doma2LibApplication の Run/Debug Configuration は不要なので削除します。

clean タスク実行 → Rebuild Project 実行 → build タスク実行を行い、警告・エラーが出ずに BUILD SUCCESSFUL が出力されることを確認します。

f:id:ksby:20190430192039p:plain

CSV ファイルを読み込んで DB のテーブルにデータを登録する機能を実装する

以下の仕様で実装します。

  • コマンドラインから java -jar -Dspring.profiles.active=<develop|product> -jar sample-cmdapp-1.0.0-RELEASE.jar -csvfile=<CSVファイルのパス> で実行します。
  • CSV ファイルは、
    • 文字コードUTF-8
    • 改行コードは CRLF
    • 1行目はヘッダ行。"name","age","sex"
    • 2行目以降はデータ行。
    • データは必ずダブルクォーテーションで囲む。
  • CSV ファイルのデータチェックは行いません。

doma2-lib プロジェクト内の ksbysample.lib パッケージの下も Component Scan の対象にするために sample-cmdapp/src/main/java/ksbysample/app/samplecmdapp/SampleCmdappApplication.java に @ComponentScan アノテーションを追加し、ksbysample.libksbysample.app の2つの package を指定します(ksbysample だけでも良いのですが複数指定するサンプルを作成したかったので2つ指定しています)。

package ksbysample.app.samplecmdapp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;

@SpringBootApplication
@ComponentScan(
        basePackages = {"ksbysample.lib", "ksbysample.app"},
        excludeFilters = {
                @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
                @ComponentScan.Filter(type = FilterType.CUSTOM,
                        classes = AutoConfigurationExcludeFilter.class)})
public class SampleCmdappApplication {

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

}

Lombok を使用したいので gradle-multiprj-doma2lib-cmdwebapp の build.gradle の configure の dependencies に追加します。

..........

configure(subprojects.findAll { it.name ==~ /^(doma2-lib|sample-cmdapp)$/ }) {
    apply plugin: "org.springframework.boot"

    dependencyManagement {
        imports {
            mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)
        }
    }

    ext {
        jdbcDriver = "mysql:mysql-connector-java:8.0.15"
        domaVersion = "2.24.0"
    }
    
    dependencies {
        def lombokVersion = "1.18.6"

        testImplementation("org.springframework.boot:spring-boot-starter-test")

        runtimeOnly(jdbcDriver)
        implementation("org.seasar.doma.boot:doma-spring-boot-starter:1.1.1")
        implementation("org.seasar.doma:doma:${domaVersion}")
        implementation("org.flywaydb:flyway-core:5.2.4")

        // for lombok
        // testAnnotationProcessor、testCompileOnly を併記しなくてよいよう configurations で設定している
        annotationProcessor("org.projectlombok:lombok:${lombokVersion}")
        compileOnly("org.projectlombok:lombok:${lombokVersion}")
    }

}
  • configure の dependencies に以下の3行を追加します。
    • def lombokVersion = "1.18.6"
    • annotationProcessor("org.projectlombok:lombok:${lombokVersion}")
    • compileOnly("org.projectlombok:lombok:${lombokVersion}")

変更後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。

CSV ファイルの1レコードのデータをセットするための POJO クラスを実装します。Univocity Parsers で利用します。sample-cmdapp/src/main/java/ksbysample/app/samplecmdapp/EmployeeCsvRecord.java を新規作成し、以下の内容を記述します。

package ksbysample.app.samplecmdapp;

import com.univocity.parsers.annotations.Parsed;
import lombok.Data;

@Data
public class EmployeeCsvRecord {

    @Parsed(field = "name")
    private String name;

    @Parsed(field = "age")
    private Integer age;

    @Parsed(field = "sex")
    private String sex;

}

null のフィールドを insert 文に出力しないようにしたいので、doma2-lib/src/main/java/ksbysample/lib/doma2lib/dao/EmployeeDao.java の insert メソッドの @Insert アノテーションexcludeNull = true を追加します。この設定を行わないと insert 文で update_time のカラムに null がセットされて MySQL 側で日時をセットしてくれません。

@Dao
@ConfigAutowireable
public interface EmployeeDao {

    /**
     * @param id
     * @return the Employee entity
     */
    @Select
    Employee selectById(Integer id);

    /**
     * @param entity
     * @return affected rows
     */
    @Insert(excludeNull = true)
    int insert(Employee entity);

    /**
     * @param entity
     * @return affected rows
     */
    @Update
    int update(Employee entity);

    /**
     * @param entity
     * @return affected rows
     */
    @Delete
    int delete(Employee entity);
}

CSV ファイルを読み込んで DB のテーブルにデータを登録する処理を実行するクラスを実装します。sample-cmdapp/src/main/java/ksbysample/app/samplecmdapp/EmployeeDataCsvToDbLoader.java を新規作成し、以下の内容を記述します。

package ksbysample.app.samplecmdapp;

import com.univocity.parsers.csv.CsvParserSettings;
import com.univocity.parsers.csv.CsvRoutines;
import ksbysample.lib.doma2lib.dao.EmployeeDao;
import ksbysample.lib.doma2lib.entity.Employee;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
import org.modelmapper.ModelMapper;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;

@Component
@ConditionalOnProperty(value = { "batch.execute" }, havingValue = "EmployeeDataCsvToDbLoader")
public class EmployeeDataCsvToDbLoader implements CommandLineRunner {

    @Option(name = "-csvfile", metaVar = "<path>", usage = "specifies a path to employee csv file")
    private String csvfile;

    private final EmployeeDao employeeDao;

    private final ModelMapper modelMapper;

    public EmployeeDataCsvToDbLoader(EmployeeDao employeeDao
            , ModelMapper modelMapper) {
        this.employeeDao = employeeDao;
        this.modelMapper = modelMapper;
    }

    @Override
    @Transactional
    public void run(String... args) throws Exception {
        // コマンドラインオプションを解析して @Option アノテーションを付加しているフィールドに値を設定する
        CmdLineParser cmdLineParser = new CmdLineParser(this);
        cmdLineParser.parseArgument(args);

        // Univocity Parses で CSV ファイルを読み込むための準備をする
        CsvParserSettings settings = new CsvParserSettings();
        settings.getFormat().setLineSeparator("\r\n");  // 改行コードは CRLF
        settings.setHeaderExtractionEnabled(true);      // ヘッダ行はスキップする
        CsvRoutines routines = new CsvRoutines(settings);

        // CSV ファイルを1行ずつ読み込み employee テーブルに insert する
        try (BufferedReader br = Files.newBufferedReader(Paths.get(this.csvfile), StandardCharsets.UTF_8)) {
            Employee employee = new Employee();
            for (EmployeeCsvRecord employeeCsvRecord : routines.iterate(EmployeeCsvRecord.class, br)) {
                modelMapper.map(employeeCsvRecord, employee);
                employeeDao.insert(employee);
            }
        }
    }

}

clean タスク実行 → Rebuild Project 実行 → build タスク実行を行い、警告・エラーが出ずに BUILD SUCCESSFUL が出力されることを確認します。

f:id:ksby:20190430221527p:plain

続く。。。

長くなったので一旦区切ります。次回は sample-cmdapp のテストの作成と、コマンドラインから実行した動作確認を行います。

履歴

2019/04/30
初版発行。