SpringBoot+Doma2+Gradleで環境構築したら色々ハマった件

## 環境
### 今回のメイン
- Windows10 (※Windows Server 2016 Datacenter)
- eclipse 4.8 Photon (pleiades-2021-03-java-win-64bit-jre_20210328)
- Java11 (OpenJDK11U-jdk_x64_windows_hotspot_11.0.10_9)
- Gradle 7.0.2
- Gradle-Wrapper (=gradlew)
- SpringBoot 2.5.0
- Doma2 2.46.1
- Doma-CodeGen 1.4.0
- ↑2021.06.05 時点の最新バージョンで実施

※`AWS Workspaces` を使っているため、その実態はWindowsServerでありUIとしてWindows10となっている

### メインの他に必要or使用した環境
- PostgreSQL 13.3-2　（自身の環境に合わせて変更してください）

環境構築方法を知りたい方は[構築手順](#構築手順)へ
ハマった内容を確認したい方は[ハマった点](#ハマった点)へ

## 構築手順
### 本筋でないところは割愛
- Postgresのインストール&設定
- eclipseのインストール

### SpringBootプロジェクトを作成する
- [Spring Initializr](https://start.spring.io/) で作成し、ダウンロードする
    - Project: Gradle Project
    - Language: Java
    - Project Metadata
        - java: 11
        - それ以外は任意
    - ADD Dependencies...
        - Spring Web
        - PostgreSQL Driver
        - Lombok
        - ↑は必要最低限の構成

### Gradle-Wrapperの構築
- ダウンロードしたプロジェクト内のGradle-Wrapperを更新する
    - `/path/to/project/gradle/wrapper/gradle-wrapper.properties` を編集する

    ```properties:gradle-wrapper.properties
    
    distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
    ```

    - プロジェクトディレクトリに移動し、更新コマンドを実行する

    ```
    > cd /path/to/project
    > gradlew wrapper
    ```

    - これにより`gradle-wrapper.jar`や`gradlew`などのコマンドファイルも更新される

### Gradleビルドの設定

<details><summary>`build.gradle`を編集する</summary><div>

```gradle:build.gradle

    import org.seasar.doma.gradle.codegen.desc.NamingType

    buildscript {
        repositories {
            mavenCentral()
        }
        dependencies {
            // Doma-CodeGenの実行時に必要となる設定
            classpath 'org.postgresql:postgresql:42.2.20'
        }
    }

    plugins {
        id 'org.springframework.boot' version '2.5.0'
        id 'io.spring.dependency-management' version '1.0.11.RELEASE'
        id 'java'
        // Doma-Boilerplate プロジェクトの設定を参考にする
        id 'com.diffplug.eclipse.apt' version "3.29.1"
        id 'org.seasar.doma.compile' version '1.1.1-beta1'

        // @see https://github.com/domaframework/doma-codegen-plugin
        // @see https://plugins.gradle.org/plugin/org.seasar.doma.codegen
        id 'org.seasar.doma.codegen' version '1.4.0'
    }

    // ヒミツ
    group = '***.*****.*******'
    version = '0.0.1-SNAPSHOT'
    sourceCompatibility = '11'
    targetCompatibility = '11'

    configurations {
        domaGenRuntime
    }

    test {
        useJUnitPlatform()
    }

    compileJava {
        options.encoding = 'UTF-8'
    }

    compileTestJava {
        options.encoding = 'UTF-8'
    }

    repositories {
        mavenCentral()
    }

    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-security'
        implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
        implementation 'org.springframework.boot:spring-boot-starter-web'
        implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'

        // https://mvnrepository.com/artifact/org.postgresql/postgresql
        runtimeOnly 'org.postgresql:postgresql'

        // Lombok
        compileOnly 'org.projectlombok:lombok'
        annotationProcessor 'org.projectlombok:lombok'

        // https://mvnrepository.com/artifact/org.seasar.doma.boot/doma-spring-boot-starter
        implementation group: 'org.seasar.doma.boot', name: 'doma-spring-boot-starter', version: '1.5.0'

        // https://mvnrepository.com/artifact/org.seasar.doma/doma-core
        implementation group: 'org.seasar.doma', name: 'doma-core', version: '2.46.1'

        // https://mvnrepository.com/artifact/org.seasar.doma/doma-processor
        compileOnly group: 'org.seasar.doma', name: 'doma-processor', version: '2.46.1'
        annotationProcessor group: 'org.seasar.doma', name: 'doma-processor', version: '2.46.1'

        testImplementation 'org.springframework.boot:spring-boot-starter-test'
        testImplementation 'org.springframework.security:spring-security-test'
    }

    // 筆者の環境ではDoma-CodeGenで使用するDatasource情報をプロパティファイルに外出ししてます
    // user definitions.
    /**
     * Reads properties from the specified .properties file.
     */
    def loadProperties(filename) {
        def props = new Properties()
        file(filename).withInputStream {
            props.load(it)
        }
        return props
    }

    def loadDatasourceProps() {
        return loadProperties("$rootDir/domaCodeGen/property/datasource.properties")
    }

    // doma-codegen-plugin
    domaCodeGen {
        // load datasource info.
        def dsProps = loadDatasourceProps()
        def _url = dsProps.getProperty("origin.url")
        def _user = dsProps.getProperty("origin.username")
        def _password = dsProps.getProperty("origin.password")

        // make an arbitrary named block
        dev {
            url = _url
            user = _user
            password = _password

            // custom template files
            templateDir = file("$projectDir/domaCodeGen/template")
            // configuration for generated entity source files
            entity {
                overwrite = true
                packageName = "***.*****.*******.entity"  // 自身の環境に合わせてください
                namingType = NamingType.SNAKE_LOWER_CASE
                showColumnName = false
                useAccessor = false
                useMappedSuperclass = false
            }
            // configuration for generated DAO source files
            dao {
                overwrite = true
                packageName = "***.*****.*******.dao"  // 自身の環境に合わせてください
            }
            sql {
                overwrite = true
            }
        }
    }


```

</div></details>



### eclipseプロジェクトに変更し、eclipseにインポートする
- 以下のコマンドを実行すると、eclipseに必要なファイルが生成される（例: `.project`, `.classpath`など）

    ```
    > cd /path/to/project
    > gradlew eclipse
    ```

- （重要！）:thinking: 一般プロジェクトとしてインポートする
    - ファイル > インポート > 一般 > 既存プロジェクトをワークスペースへ

### ビルド実行
- インポート時にeclipseが自動的にインポートすると思いますが念のため
    - プロジェクト > クリーン
    - （重要！）:thinking: eclipseが実行するGradle環境の設定
        - ウィンドウ > 設定 > Gradle > Gradle ディストリビューション
            - Gradle ラッパーにチェックを付ける
    - Gradleビルド
        - 実行 > 実行構成 > Gradle タスク
            - 作業ディレクトリ: `${workspace_loc:/プロジェクト名}`
            - タスク: build
    - Doma-CodeGenでDao+Entityソースファイルを生成する
        - 実行 > 実行構成 > Gradle タスク
            - 作業ディレクトリ: `${workspace_loc:/プロジェクト名}`
            - タスク: domaCodeGenDevAll


## ハマった点
- Doma2とGradle-Plugin(BuildShip)との関係
- Doma-CodeGenのカスタマイズ

### Doma2とGradle-Plugin(BuildShip)との関係
#### Doma2についての基礎知識
- Doma2ではJavaの`Pluggable Annotation Processing API`という仕組みを利用している
- これによりコンパイル時にソースの自動生成や検証を行ってくれる
    - ソースの自動生成: XxxDaoImp.javaなど
    - 検証: Daoで定義したメソッドに対応するSQLファイルの存在チェックなど
- Doma2では自動生成ファイルを `.apt_generated` に格納する
- 当然、ビルドパスに `.apt_generated` が存在しないと実行（orコンパイル）できない
    - Doma2では[doma-compile-plugin](https://github.com/domaframework/doma-compile-plugin)により自動的にビルドパスの設定を行ってくれる
    - 本件では `gradlew eclipse` を実行したタイミング
- また、Doma2ではDaoとsqlのパッケージ構成が同一である必要がある
    - 詳細は[本家のドキュメント](https://doma.readthedocs.io/en/latest/sql/#format-of-file-path)を参照
- サンプルプロジェクトが充実しており、以下の`build.gradle`を参考にした
    - Spring+Doma2の構成だと[ココ](https://github.com/domaframework/spring-boot-jpetstore)
    - シンプルなDomaプロジェクトは[ココ](https://github.com/domaframework/simple-boilerplate)

#### [BuildShip](https://projects.eclipse.org/projects/tools.buildship)について
- 知っていることはeclipse公式のGradle-Pluginということだけ
- 今回はハマったから使わないからね！！！

#### ハマったポイント
- `Gradle refresh` でビルドパスが上書きされる！
    - 実際、`.classpath`ファイルのタイムスタンプが更新されている
    - しかも、`.apt_generated` の出力先が`default`に勝手に変更されてしまう！
    - なので、aptの検証時にDaoとSQLファイルの整合性チェックでコンパイルエラーになる
- 試したこと
    - 公式での[Q&A](https://github.com/eclipse/buildship/issues/575)
        - 回答の通り、`dependencies { files(".apt_generated") }` など試してもうまくいかなかった
    - ビルドパスの設定で`ソースフォルダごとに出力フォルダの指定を可能にする`のチェックを外せば出力フォルダを統一できるが、テストソースが作れなくなるので却下。
    - 結局。BuildShipを使用することを断念。

#### BuildShipを使用しない方法
- eclipseのSpringスタータープロジェクトによるプロジェクト作成はしない
    - ↑強制的にBuildShipが利用される
    - 作成されたプロジェクトからBuildShipに関係のありそうな設定を削除してもeclipseでビルドができなくなるだけ
        - `.project`の`<nature>org.eclipse.buildship.core.gradleprojectnature</nature>`や
        - `.settings/org.eclipse.buildship.core.prefs`ファイル全体など
- Spring Initializrでプロジェクト作成する
- `gradlew eclipse`してeclipseプロジェクトに変換する
- eclipseに「一般プロジェクト」としてインポートする

### Doma-CodeGenのカスタマイズ
#### Doma-CodeGenとは？
- Doma-Genの後継プロジェクトか？⇒[GitHub](https://github.com/domaframework/doma-codegen-plugin)
- 以前からDomaのソース生成といえばDoma-Genであったが`deprecated`になっている！
- 記事が長くなったので、Doma-CodeGenでの試行錯誤やその動機を次回に回します。