Kotlin×Spring Boot×Doma2をGradle Kotlin DSLで環境構築!

こんにちは。虎の穴ラボ所属のH.Kです。
虎の穴ラボではKotlinを使っているのですが、SQLをソース上にあまり書きたくないエンジニアもいて、"Doma2なら"と思い、このたび環境構築しました。
現在Spring InitializrにてKotlin、Gradleでひな形プロジェクトを作成すると、Gradle Kotlin DSLを使用したプロジェクトとなります。

Gradle Kotlin DSL(build.gradle.kts)での構築方法について言及が少なかったため、備忘録も兼ねて手順を公開します!

そもそもDoma2とは

Java(Kotlin対応)のORMです。
公式リファレンスはこちらです。

doma.readthedocs.io

特徴としては以下のようなものになります。(公式リファレンスより引用)

doma.readthedocs.io

・注釈処理を使用して コンパイル時 にコードの生成やコードの検証を行う
・データベース上のカラムの値を振る舞いを持った Java オブジェクトにマッピングできる
・2-way SQL と呼ばれる SQL テンプレートを利用できる
・Java 8 の java.time.LocalDatejava.util.Optionaljava.util.stream.Stream を利用できる
・JRE 以外のライブラリへの依存が一切ない

2-way SQLのため発行されるクエリがわかりやすくプログラムを実行しなくてもどのようなSQLが発行されるか確認できます。
またKotlinのソースとSQLがファイルとして分離されるため、見通しの良いソースを書くことができます。

作業環境

  • macOS
  • IntelliJ

この記事で作成するプロジェクトの構成

  • Kotlin
  • Spring Boot
  • Gradle(Gradle Kotlin DSL)
  • Doma2
  • Java8
  • H2DB

ひな形プロジェクトの作成

Spring InitializrにてKotlin、Gradleのひな形プロジェクトを作成します。

  1. Project:Gradle Project
  2. Language:Kotlin
  3. Spring Boot:2.1.7
  4. Project Metadata:お好みで(今回はoptionsにてPackaging:War、Java:8を選択)

今回、依存関係は以下を追加します。
DBのDriver関係は構築したい環境に合わせて変更してください。

  • Spring Web Starter(RESTで画面表示する用)
  • H2 Database(サクッとDB接続を確認する用)

f:id:toranoana-lab:20190819112341p:plain
Spring Initializr(Doma2)

ここまで設定したらGenerate the projectでひな形プロジェクトを作成します。
作成したひな形プロジェクトを解凍し、intelliJで開きます。

IntelliJの設定追加

Annotation Processingの設定を行います。
Preferences>Build, Excution, Deployment>Compiler>Annotation Processors

  • Enable annotation processingをチェックする
  • Store generated sources relative toをModule content rootに設定する

f:id:toranoana-lab:20190819125545p:plain
intelliJ setting

build.gradle.ktsの設定

公式サンプルプロジェクトのbuild.gradleの記載に合わせbuild.gradle.ktsを修正していきます。 github.com

最終的なbuild.gradle.kts

最終的には以下のようになります。(記載が完了したら依存関係をImportしてください。)

build.gradle.kts build.gradle.kts

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    id("org.springframework.boot") version "2.1.7.RELEASE"
    id("io.spring.dependency-management") version "1.0.7.RELEASE"
    war
    kotlin("jvm") version "1.2.71"
    kotlin("plugin.spring") version "1.2.71"
    id("org.jetbrains.kotlin.kapt") version "1.2.71"
}

group = "com.example"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_1_8

configurations {
    compileOnly {
        extendsFrom(configurations.annotationProcessor.get())
    }
}

repositories {
    mavenCentral()
    maven("https://oss.sonatype.org/content/repositories/snapshots/")
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    compile("com.h2database:h2:1.4.191")
    compile("org.seasar.doma.boot:doma-spring-boot-starter:1.1.1")
    kapt("org.seasar.doma:doma:2.24.0")
    implementation("org.seasar.doma:doma:2.24.0")
}

val compileKotlin: KotlinCompile by tasks

kapt {
    arguments {
        arg("doma.resources.dir", compileKotlin.destinationDir)
    }
}

tasks.register("copyDomaResources",Sync::class){
    from("src/main/resources")
    into(compileKotlin.destinationDir)
    include("doma.compile.config")
    include("META-INF/**/*.sql")
    include("META-INF/**/*.script")
}

tasks.withType<KotlinCompile> {
    dependsOn(tasks.getByName("copyDomaResources"))
    kotlinOptions {
        freeCompilerArgs = listOf("-Xjsr305=strict")
        jvmTarget = "1.8"
    }
}

部分ごとに少し細かく見ていきます。

1.pluginsにkaptを追加

kaptとはDoma2で使用するPluggable Annotation Processing API (アノテーションを使ってソースコードを生成する仕組み)をKotlinで行うためのツールです。
pluginsにkaptを追加します。

plugins{
  .
  .
  .
  id("org.jetbrains.kotlin.kapt") version "1.2.71"
}

Annotation Processingの設定を取得します。

configurations {
  compileOnly {
    extendsFrom(configurations.annotationProcessor.get())
  }
}

2.Doma2の依存関係を追加

Doma2をリポジトリーから取得する設定を追加します。
まずrepositoriesにスナップショットのリポジトリーを追加します。

repositories {
  mavenCentral()
  maven("https://oss.sonatype.org/content/repositories/snapshots/")
}

次にdependenciesにimplementationとkaptにdomaの依存関係を示します。

dependencies {
  .
  .
  .
  compile("org.seasar.doma.boot:doma-spring-boot-starter:1.1.1")
  kapt("org.seasar.doma:doma:2.24.0")
  implementation("org.seasar.doma:doma:2.24.0")
}

3.コンパイル時のタスクを設定

まずSQLファイル等をコピーするタスクを作成します。

val compileKotlin: KotlinCompile by tasks

kapt {
    arguments {
        arg("doma.resources.dir", compileKotlin.destinationDir)
    }
}

tasks.register("copyDomaResources",Sync::class){
    from("src/main/resources")
    into(compileKotlin.destinationDir)
    include("doma.compile.config")
    include("META-INF/**/*.sql")
    include("META-INF/**/*.script")
}

Kotlinのコンパイルタスクに作成したタスクを依存させます。

tasks.withType<KotlinCompile> {
    dependsOn(tasks.getByName("copyDomaResources"))
    kotlinOptions {
        freeCompilerArgs = listOf("-Xjsr305=strict")
        jvmTarget = "1.8"
    }
}

DB設定

Gradleの設定が完了したので、DBの設定になります。

H2DBの接続設定

H2DBの接続設定を行います。 実際に使用するDBの設定に合わせて適宜変更してください。 application.properties

spring.datasource.url= jdbc:h2:mem:test;
spring.datasource.driverClassName= org.h2.Driver
spring.datasource.username= sa
spring.datasource.password=
spring.h2.console.enabled= true

環境確認用ソースの実装

DBからデータを取得して画面に表示する簡単な処理を作ってみます。

最終的なプロジェクトの構成はこのようになります。

f:id:toranoana-lab:20190819142226p:plain
プロジェクト構成

Entityの作成

DBのテーブルに対応するEntityを作成します。
DemoTable.kt

package com.example.doma.domademo.entity

import org.seasar.doma.Entity
import org.seasar.doma.Id

@Entity(immutable = true)
data class DemoTable(@Id val id: Int,val name: String)

Dao(インターフェイス)の作成

Daoインターフェイスを作成します。
Doma2ではDao実装クラスの作成は不要です(インターフェイスから自動で作成されるため)
DIコンテナの管理対象とするためDaoクラスには@ConfigAutowireableを設定しています。
DemoTableDao.kt

package com.example.doma.domademo.dao

import com.example.doma.domademo.entity.DemoTable
import org.seasar.doma.Dao
import org.seasar.doma.Insert
import org.seasar.doma.Select
import org.seasar.doma.boot.ConfigAutowireable
import org.seasar.doma.jdbc.Result

@Dao
@ConfigAutowireable
interface DemoTableDao {
    @Insert
    fun insert(entity : DemoTable):Result<DemoTable>

    @Select
    fun selectAll():List<DemoTable>
}

RESTコントローラの作成

画面に表示して確認するためにDaoから取得した値をそのまま返却するコントローラを作成します。
DemoController.kt

package com.example.doma.domademo.controller

import com.example.doma.domademo.dao.DemoTableDao
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class DemoController {
    @Autowired
    lateinit var demoTableDao : DemoTableDao

    @GetMapping(value = "/")
    fun index() : String=demoTableDao.selectAll().toString()
}

Daoに対応するSQLファイルの追加

DaoのselectAllメソッドに対応するSQLファイルを作成します。
今回、Daoはcom.example.doma.domademo.daoに作成しているため、
SQLファイルはresouces/META-INF/com/example/doma/domademo/dao/DemoTableDaoに作成します。 selectAll.sql

select * from DEMO_TABLE;

確認用SQLの作成

起動時にH2DBに自動でテーブルを作成し、データを追加できるように以下のSQLファイルをresoucesに配置します。
schema.sql

CREATE TABLE IF NOT EXISTS demo_table (
  id    INTEGER      NOT NULL ,
  name VARCHAR(255) NOT NULL,
  PRIMARY KEY (id)
);

data.sql

insert into demo_table(id,name) values(1,'test1');
insert into demo_table(id,name) values(2,'test2');

いざ実行!

gradleでclean buildします。
そしてbootRun

http://localhost:8080/にアクセスして以下のように表示されれば成功です!!

f:id:toranoana-lab:20190819132857p:plain
bootRun Result

まとめ

今回はKotlin、Spring-boot、Doma2をGradle Kotlin DSLで環境構築してみました。
Gradle Kotlin DSLに関してはまだまだ日本語の情報が少ないように思えます。
Doma2の環境に関わらずGradle Kotlin DSL記載のサンプルとして役に立てばと考えています。

またDoma2は最初に説明した通り、Kotlin(Java)のソースとSQLがファイルとして分離されます。
メンテナンスしやすく、いいライブラリだと個人的には感じていますので、Kotlinと組み合わせて便利なツールを開発していきたいです。

P.S.

虎の穴では一緒に働く仲間を絶賛募集中です! この記事を読んで、興味を持っていただけた方はぜひ弊社の採用情報をご覧下さい。 yumenosora.co.jp

また、今月8/28には渋谷にて会社説明会を開催いたします。ご興味のある方は是非ご応募ください! yumenosora.connpass.com