たかぎとねこの忘備録

プログラミングに関する忘備録を自分用に残しときます。マサカリ怖い。

KtorとExposedの環境でちょっとデータベースにシードさせたいときのTips

Ktorでデータベースを使用したいので、com.takagimeow.infrastructure.database.daoDatabaseFactoryを実装してみます。

package com.takagimeow.infrastructure.database.dao

import com.takagimeow.infrastructure.database.models.Users
import kotlinx.coroutines.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.*
import org.jetbrains.exposed.sql.transactions.experimental.*

object DatabaseFactory {
    fun init() {
        val driverClassName = "org.postgresql.Driver"
        val jdbcURL = "jdbc:postgresql://localhost:5432/serverside"
        val user = "takagimeow"
        val password = "takagimeow_pass"
        val database = Database.connect(
            url = jdbcURL,
            driver = driverClassName,
            user = user,
            password = password
        )
        transaction(database) {
            SchemaUtils.create(Users)
        }
    }

    suspend fun <T> dbQuery(block: suspend () -> T): T =
        newSuspendedTransaction(Dispatchers.IO) { block() }
}

Application.ktで、このオブジェクトのinit()メソッドを呼び出すことでデータベースへ接続を行います。

fun Application.module() {
    DatabaseFactory.init()
    ...
}

そしてUsersDSLで定義します。

object Users : Table() {
    val id = integer("id").autoIncrement()
    val email = varchar("email", 128)
    val password = varchar("password", 128)
    val name = varchar("name", 128)
    val roleType = varchar("role_type", 128)

    override val primaryKey = PrimaryKey(id)
}

このとき、ユーザーの作成に関するルートは公開したくないけども、初期データとしてユーザーレコードをテーブルに登録したい場合を考えてみました。

プロジェクトではKoinを使いたいのでUserDaoFacadeインターフェースを作成します。

package com.takagimeow.infrastructure.database.dao

import com.takagimeow.infrastructure.database.models.*

interface UserDAOFacade {
    suspend fun addNewUser(email: String, password: String, name: String): Unit
}

そして、その実装であるUserDaoFacadeImplクラスを実装します。

package com.takagimeow.infrastructure.database.dao.impl

import com.takagimeow.infrastructure.database.dao.UserDAOFacade
import com.takagimeow.infrastructure.database.dao.DatabaseFactory.dbQuery
import com.takagimeow.infrastructure.database.models.Users
import kotlinx.coroutines.runBlocking
import org.jetbrains.exposed.sql.*

class UserDAOFacadeImpl : UserDAOFacade {
    override suspend fun addNewUser(email: String, password: String, name: String): Unit = dbQuery {
        val insertStatement = Users.insert {
            it[Users.email] = email
            it[Users.password] = password
            it[Users.name] = name
            it[roleType] = "USER"
        }
        insertStatement.resultedValues?.singleOrNull()?.let(::resultRowToUser)
    }
}

実際は、各リポジトリでこのクラスを注入することでデータベースに対する操作を行なっていくのですが、今回はこのUserDAOFacadeImplを再利用してデータの挿入を行おうと思います。

com.takagimeow.infrastructure.databaseパッケージにseedパッケージを作成します。

このパッケージの中に、InserUser.ktを作成します。 そしてmain()を定義して、コードブロックの中でUserDaoFacadeImplクラスのaddNewUser()を呼び出します。

package com.takagimeow.infrastructure.database.seed

import com.takagimeow.infrastructure.database.dao.DatabaseFactory
import com.takagimeow.infrastructure.database.dao.impl.UserDAOFacadeImpl

suspend fun main() {
    DatabaseFactory.init()
    val dao = UserDAOFacadeImpl()
    dao.addNewUser(
        "takagimeow@example.com",
        "password",
        "takagi",
    )
}

あとは、IntelliJ IDEAで表示されているmain()の横の実行ボタンを押すだけ。

ターミナルには次の様に表示されました。

2022-11-16 02:15:03.836 [DefaultDispatcher-worker-3] DEBUG Exposed - INSERT INTO users (email, "name", "password", role_type) VALUES ('takagimeow@example.com', 'takagi', 'password', 'USER')

プロセスは終了コード 0 で終了しました

TablePlusを通してデータを確認してみました。ちゃんと登録されています。