🦜

Kotlin製ORMのexposedを使う準備をする

2023/08/31に公開

TL;DR

exposed を試しに動かすためのプレイグラウンドを作ります。

背景

  • exposed をいろいろ触るためのプレイグラウンドが欲しくなった
  • デバッグしながら exposed がどうやってクエリを投げてるのか解析したくなった

プロジェクトのベースを一応用意しているので、使ってみてください。

https://github.com/ysknsid25/kotlin_playground

メンバー募集中!

サーバーサイド Kotlin コミュニティを作りました、ぜひご参加ください!!

https://serverside-kt.connpass.com/

MySQL の準備

MySQL コンテナを使って DB を用意します。

docker-compose.yml
version: "3.9"
services:
  db:
    build: ./docker/mysql
    ports:
      - 13306:3306
    volumes:
      - db-store:/var/lib/mysql
volumes:
  db-store:
./docker/mysql/Dockerfile
FROM mysql/mysql-server:8.0

ENV MYSQL_DATABASE=exposed_local \
    MYSQL_USER=exposer \
    MYSQL_PASSWORD=secret \
    MYSQL_ROOT_PASSWORD=secret \
    TZ=Asia/Tokyo

COPY ./my.cnf /etc/my.cnf
# どうもwindowsでボリュームマウントすると権限が777になってしまうみたい
# 書き込み権限がついてるとMySQL起動時にエラーになるので644に変更している
RUN chmod 644 /etc/my.cnf

ただのローカル検証のためであれば、現時点で特に設定に意味はないので突っ込まないでください。

逆に本番とかで運用しようとしてるときに、これはコピーしないでください。

[mysqld]
# default
skip-host-cache
skip-name-resolve
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
secure-file-priv=/var/lib/mysql-files
user=mysql

pid-file=/var/run/mysqld/mysqld.pid

# character set / collation
character_set_server = utf8mb4
collation_server = utf8mb4_ja_0900_as_cs_ks

# timezone
default-time-zone = SYSTEM
log_timestamps = SYSTEM

# Error Log
log-error = mysql-error.log

# Slow Query Log
slow_query_log = 1
slow_query_log_file = mysql-slow.log
long_query_time = 1.0
log_queries_not_using_indexes = 0

# General Log
general_log = 1
general_log_file = mysql-general.log

[mysql]
default-character-set = utf8mb4

[client]
default-character-set = utf8mb4

これで起動する。

docker-compose up -d --build

テーブル定義の用意

検証用なので制約はなにもないです。

CREATE TABLE EMPLOYEE (
	EMPLOYEE_ID INT,
    DEPARTMENT_ID INT,
    ENROLLMENT_STATUS INT, -- 在籍状況 0:在籍中 1:休職中 2:退職済
    CREATED_AT DATETIME,
	UPDATED_AT DATETIME
);

CREATE TABLE DEPARTMENT (
	DEPARTMENT_ID INT,
    DEPARTMENT_NAME VARCHAR(32),
    CREATED_AT DATETIME,
	UPDATED_AT DATETIME
);

INSERT INTO DEPARTMENT VALUES (1, '総務', SYSDATE(), SYSDATE());
INSERT INTO DEPARTMENT VALUES (2, '経理', SYSDATE(), SYSDATE());
INSERT INTO DEPARTMENT VALUES (3, '営業', SYSDATE(), SYSDATE());

INSERT INTO EMPLOYEE VALUES (1, 1, 0, SYSDATE(), SYSDATE());
INSERT INTO EMPLOYEE VALUES (2, 2, 0, SYSDATE(), SYSDATE());
INSERT INTO EMPLOYEE VALUES (3, 2, 1, SYSDATE(), SYSDATE());
INSERT INTO EMPLOYEE VALUES (4, 3, 0, SYSDATE(), SYSDATE());
INSERT INTO EMPLOYEE VALUES (5, 3, 0, SYSDATE(), SYSDATE());
INSERT INTO EMPLOYEE VALUES (6, 3, 2, SYSDATE(), SYSDATE());

COMMIT;

exposed を利用するために必要なライブラリを準備する

build.gradle.kts に以下を記載。

build.gradle.kts
dependencies {
    // for exposed
    implementation("org.jetbrains.exposed", "exposed-core", "0.41.1")
    implementation("org.jetbrains.exposed", "exposed-dao", "0.41.1")
    implementation("org.jetbrains.exposed", "exposed-jdbc", "0.41.1")
    implementation("org.jetbrains.exposed:exposed-java-time:0.41.1")
    implementation("mysql:mysql-connector-java:8.0.33")
}

Model の作成

先ほど作成したテーブルについて、Kotlin の世界に Model を作ります。

Employee.kt
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.javatime.datetime

object Employee : Table("EMPLOYEE") {
    val employeeId = integer("EMPLOYEE_ID") //社員ID
    val departmentId = integer("DEPARTMENT_ID") //部署ID
    val enrollmentStatus = integer("ENROLLMENT_STATUS") //在籍状況 0:在籍中 1:休職中 2:退職済
    val createdAt = datetime("CREATED_AT") //作成日時
    val updatedAt = datetime("UPDATED_AT") //更新日時
}
Department.kt
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.javatime.datetime

object Department : Table("DEPARTMENT") {
    val departmentId = integer("DEPARTMENT_ID") //部署ID
    val departmentName = varchar("DEPARTMENT_NAME", 32) //部署名
    val createdAt = datetime("CREATED_AT") //作成日時
    val updatedAt = datetime("UPDATED_AT") //更新日時
}

SQL の実行

それでは Main.kt を作成し、exposed を使ってみます。

ここでは DB への接続情報をハードコードしていますが、本番運用や外部 DB へ接続する場合は必ず接続情報は設定ファイルに書き出して、Git などのバージョン管理対象外にしておいてください。

また、本番では DB への接続も常に接続と断を行うのではなくHikariCPなどを利用してください。

Main.kt
import example.koin.data.model.Department
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction

fun main(){
    Database.connect(
        url = "jdbc:mysql://127.0.0.1:13306/exposed_local",
        driver = "com.mysql.cj.jdbc.Driver",
        user = "exposer",
        password = "secret"
    )
    transaction {
        Department.slice(Department.departmentId, Department.departmentName).selectAll().map {
            println("${it[Department.departmentId]}: ${it[Department.departmentName]}")
        }
    }
}

以下のようにコンソールに表示されれば OK です。

2023-08-30 21:58:54.601 [main] DEBUG Exposed - SELECT DEPARTMENT.DEPARTMENT_ID, DEPARTMENT.DEPARTMENT_NAME FROM DEPARTMENT
1: 総務
2: 経理
3: 営業

おわりに

これで exposed で遊ぶ準備が整ったので、いろいろ検証したりライブラリの中身をみていけそうです。

メンバー募集中!

サーバーサイド Kotlin コミュニティを作りました、ぜひご参加ください!!

https://serverside-kt.connpass.com/

また関西在住のソフトウェア開発者を中心に、関西エンジニアコミュニティを一緒に盛り上げてくださる方を募集しています。

よろしければ Conpass からメンバー登録よろしくお願いいたします。

https://blessingsoftware.connpass.com/

Discussion