Ktor - asynchronous Web framework for KotlinでDoma2を使ってデータベースアクセスしてみました。
Ktorの公式サイト上にはデータベースアクセスする方法などが全く無いので、これが正解かどうかはわかりませんが…
build.gradle
- データベースアクセスに必要となるライブラリを追加します
dependencies {
kapt 'org.seasar.doma:doma:2.22.0'
implementation 'org.seasar.doma:doma:2.22.0'
runtime 'org.postgresql:postgresql:42.2.5'
implementation 'com.zaxxer:HikariCP:3.3.1'
}
- Doma2でkaptが必要になるので、kaptプラグインを追加します
apply plugin: 'kotlin-kapt'
データベースの接続先を定義する
resources/application.conf
にデータベースの接続先などを定義します
database {
driverClass = org.postgresql.Driver
url = "jdbc:postgresql://localhost:5432/siosio"
user = siosio
password = siosio
}
DomaのConfigクラスを追加する
- トランザクション — Doma 2.0 ドキュメントを参考にシングルトンなコンフィグを作ります
Kotlinなので、object
で作ってsingleton
は@JvmStatic
にしています
init
で、HikariCPのDataSourceを作成します
接続先などの情報は、resources/application.conf
からとってきます
@SingletonConfig
object DomaConfig : Config {
private lateinit var ds: LocalTransactionDataSource
fun init(environment: ApplicationEnvironment) {
val hikariConfig = HikariConfig()
hikariConfig.driverClassName = environment.config.property("database.driverClass").getString()
hikariConfig.jdbcUrl = environment.config.property("database.url").getString()
hikariConfig.username = environment.config.property("database.user").getString()
hikariConfig.password = environment.config.property("database.password").getString()
ds = LocalTransactionDataSource(HikariDataSource(hikariConfig))
}
override fun getDialect(): Dialect = PostgresDialect()
override fun getDataSource(): DataSource {
if (::ds.isInitialized.not()) {
throw IllegalStateException("database setting is not initialized")
}
return ds
}
override fun getTransactionManager(): TransactionManager {
return LocalTransactionManager(ds.getLocalTransaction(jdbcLogger))
}
@JvmStatic
fun singleton(): DomaConfig = DomaConfig
}
Moduleの定義タイミングでDataSourceを作成する
DomaConfig#init
を呼び出してDataSourceを作成します
fun Application.module(testing: Boolean = false) {
DomaConfig.init(environment)
routing {
user()
}
}
Domaを使いやすくするヘルパーメソッドを追加する
- Daoの実装クラス(DaoImpl)を取得するメソッドを追加します
transaction
を実行するためのメソッドを追加します
inline fun <reified T> dao(): T {
return Thread.currentThread().contextClassLoader
.loadClass("${T::class.qualifiedName}Impl")
.newInstance() as T
}
fun <T> PipelineContext<out Any, out Any>.transaction(block: () -> T): T {
return DomaConfig.transactionManager.required(block)
}
データベースアクセス処理を実装する
- 先程定義した
transaction
メソッドに渡したブロック内でデータベースアクセスを行います
- 確認用に登録処理と、登録したデータを一括取得する処理を実装しています
fun Route.user(): Unit {
get("/users") {
val userList = transaction {
dao<UserDao>().findAll()
}
call.respond(userList)
}
post("/users") {
val user = call.receive(UserEntity::class)
transaction {
dao<UserDao>().insert(user)
}
call.respond(HttpStatusCode.Created)
}
}
アプリケーションを実行して確認してみる
データの登録処理
登録処理を呼び出して、2件データを登録してみます
~ ❮❮❮ curl -H 'Content-Type: application/json' -d '{"name": "hoge"}' -D - http://localhost:8080/users
HTTP/1.1 201 Created
Content-Length: 0
~ ❯❯❯ curl -H 'Content-Type: application/json' -d '{"name": "fuga"}' -D - http://localhost:8080/users
HTTP/1.1 201 Created
Content-Length: 0
データの取得処理
一括取得を呼び出すと登録したデータが返されることが確認できます
~ ❯❯❯ curl -D - http://localhost:8080/users
HTTP/1.1 200 OK
Content-Length: 72
Content-Type: application/json; charset=UTF-8
[ {
"id" : 2,
"name" : "hoge"
}, {
"id" : 3,
"name" : "fuga"
} ]
データベースの確認
登録した2件のデータが確認できます。ちゃんと動いていますね。
siosio=# select * from users;
id | name
----+------
2 | hoge
3 | fuga
(2 rows)