はじめに
Kotlin 向け ORM ライブラリの ktorm でカラムの型に Blob を指定したらエラーが出たので原因を調べてみたら SQLite JDBC の型によるものだったということと、簡単な回避策をまとめました。
(事前情報: Java の byte[] は Kotlin だと ByteArray になります)
環境
Kotlin: 2.1.0
Java: 21
ktorm: ktorm-core 4.1.1
JDBC: 4.3
発生したこと
ktorm でカラムの型を以下のように指定して insert を実行したところ not implemented by SQLite JDBC driver
と言われてしまいました。
val items = blob("items").bindTo { it.items }
原因特定
以下の流れで原因を特定しました。
- ktorm の SqlType#doSetParameter でエラーが発生しているとスタックトレースに書かれる
- SqlType の実装を見に行く
- パラメータ指定で PreparedStatement#setBlob を実行している
- PreparedStatement が java.sql の型であることを特定する
- SQLite JDBC の setBlob を見に行く
- >> setBlob 実装されてないじゃん !!! <<
原因解説
ktorm でカラムの型に指定した blob はクエリの際に内部で java.sql.Blob を利用するようになっていますが、ktorm から呼び出した SQLite JDBC の PreparedStatement では Blob 用の実装がなされていなかったためエラーが発生していました。
そもそも SQLite データベースにおける Blob と Java における Blob は異なるものを指しています。
SQLite における Blob はバイナリオブジェクトそのものを指すのに対して Java での Blob はそのオブジェクトを生成したトランザクションが有効な間だけ有効な DB 内データへのポインタとなります。
Java で利用する際には byte[] / ByteArray として扱うので、ほとんどポインタとしか振る舞わない Blob のためにあれこれ整備はしないといったスタンスなのでしょうか。
回避策(簡易的)
byte[] / ByteArray を利用する時は ~Blob ではなく ~Bytes メソッドを利用する。
// ❌
preparedStatement.setBlob(1, ByteArray(0))
// ⭕
preparedStatemet.setBytes(1, ByteArray(0))
// 最初の ktorm のカラムを書き直すなら
val items = bytes("items").bindTo { it.items }
おわりに
ほぼ初めて DB を触ったのでエラーにビクビクしていましたが、調べてみたら大したこともなく簡単に回避できたので安心しました。