練習用のテーブル
create table articles( id integer primary key, title varchar(20) not null, author varchar(20), content text not null );
原始的な方法
Javaと同じ方法でゴリゴリ書く。
import java.sql.DriverManager
import java.sql.ResultSet
import java.sql.Statement
import java.sql.Connection
fun main(args: Array<String>) {
val articles = findAllArticles()
println(articles)
}
fun findAllArticles(): List<Article> {
var conn: Connection? = null
var statement: Statement? = null
var resultSet: ResultSet? = null
val articleListBuilder = ImmutableArrayListBuilder<Article>()
try {
conn = DriverManager.getConnection("jdbc:postgresql://localhost/sample")
statement = conn?.createStatement()
resultSet = statement?.executeQuery("select * from articles")
while(resultSet?.next() ?: false) {
articleListBuilder.add(Article(
resultSet!!.getLong(1),
resultSet!!.getString(2)!!,
resultSet!!.getString(3) ?: "",
resultSet!!.getString(4)!!
))
}
} finally {
resultSet?.close()
statement?.close()
conn?.close()
}
return articleListBuilder.build()
}
data class Article(
val id: Long,
val title: String,
val author: String,
val content: String
)
Javaよりは簡単に書ける。チェック例外がないだけでだいぶスッキリするなぁ。
Connectionとかのclose()はちょっと面倒臭いけど仕方ない。Kotlin標準ライブラリにはCloseableの拡張関数useがあって、これを使えば自分でクローズ書かなくてもいいんだけど、ConnectionとかはCloseableじゃない。
Kotlin標準ライブラリを使う方法
標準ライブラリかどうかはわからないけど、プロジェクトKotlinの一部として開発されてるパッケージkotlin.jdbcを使う方法。
使うにはリポジトリからクローンして、クラスパスを通す必要がある。
import kotlin.jdbc.getConnection
import kotlin.jdbc.use
import kotlin.jdbc.useSql
import kotlin.jdbc.iterator
fun main(args: Array<String>) {
val articles = findAllArticles()
println(articles)
}
fun findAllArticles(): List<Article> {
val articleListBuilder = ImmutableArrayListBuilder<Article>()
getConnection("jdbc:postgresql://localhost/sample").use {
// useSql -> use にリネーム予定
it.createStatement()?.useSql {
it.executeQuery("select * from articles").use {
it.iterator().forEach {
articleListBuilder.add(Article(
it.getLong(1),
it.getString(2)!!,
it.getString(3) ?: "",
it.getString(4)!!
))
}
}
}
}
return articleListBuilder.build()
}
data class Article(
val id: Long,
val title: String,
val author: String,
val content: String
)
try-catchもクローズもvarも消えてイイ感じ。でもそれだけ。
ちなみにStatementの拡張関数useSqlはuseにリネーム予定(ちょっと残念なバグがあるらしい)。
サードパーティ製ライブラリを使う方法
KotlinでSQLを意識させないためのサードパーティ製ライブラリがあるので使ってみた。 Exposedっていうライブラリで、一応プロトタイプという位置付け。 軽量さを売りにしてるのかな。もうちょっとカッコよくできそうな感じはする。 あとプロトタイプってことなので、まだまだ機能が充実してないです。
使うためにはリポジトリから引っ張って来て、クラスパスとかを上手いこと設定する。
import kotlin.sql.Database
import kotlin.sql.Table
import kotlin.sql.integer
import kotlin.sql.varchar
import kotlin.sql.template
fun main(args: Array<String>) {
val articles = findAllArticles()
println(articles)
}
fun findAllArticles(): List<Article> {
val articleListBuilder = ImmutableArrayListBuilder<Article>()
Database("jdbc:postgresql://localhost/sample", "org.postgresql.Driver").withSession {
Articles.all().forEach {
val (id, title, author) = it
articleListBuilder.add(Article(
id.toLong(),
title,
author,
""
))
}
}
return articleListBuilder.build()
}
object Articles: Table() {
val id = integer("id").primaryKey()
val title = varchar("title", length = 20)
val author = varchar("author", length = 20)
val content = varchar("content", length = 3000)
val all = template(id, title, author) // 注
}
data class Article(
val id: Long,
val title: String,
val author: String,
val content: String
)
「注」の箇所。ここではselect id, title, author相当のSQLを構築する元を作ってるんだけど、このtemplate関数は現状では最大3つの引数しか取れないっぽい。そのため今回の例ではcontentを取るのを諦めた。その他にはカラムのデータ型が整数値と文字列の2種類しかないなど、作り始めて間もない感じが伝わってきた。
Articles.all().forEachのブロックの中で、it(ここではTripleオブジェクト)の各値を多重宣言を使って各名前にバインドしている。
感想
カッコよくて便利なライブラリを作ろうと思いました。