ExposedでEnumを使ってみる
PostgreSQLをデータベースと使用しているときに、一部のカラムをEnumとして表現したい。
そのためにはまず、Enumを型として定義する必要があるので作成してみる。これはテーブルの定義よりも前にやっておく。
こんな感じ。
class PGEnum<T : Enum<T>>(enumTypeName: String, enumValue: T?) : PGobject() { init { value = enumValue?.name type = enumTypeName } }
カラムの値として受け取りたい値をenum class
を使って定義する。
enum class Service { INSTAGRAM, TWITTER, }
この二つを使って、テーブルを定義するとこんな感じになる。
第三引数と第四引数では、いわゆるfrom/to変換関数を渡している。
object Posts : UUIDTable() { val service = customEnumeration("service", "ServiceEnum", { value -> Service.valueOf(value as String) }, { PGEnum("ServiceEnum", it) }) }
SchemaUtils.create()
を呼び出すときにexec()
メソッドを呼び出して、CREATE TYPE
を実行することで、新しいデータ型を定義する。
CREATE TYPE
で指定するのは、customEnumeration()
の呼び出し時にsql
引数として渡した引数と同じ値。ここではServiceEnum
を指定している。
AS ENUM
で指定するのは、enum class
で定義した内容と同じ値。
val database = Database.connect( url = jdbcURL, driver = driverClassName, user = user, password = password ) transaction(database) { exec( "DO $$ BEGIN " + "CREATE TYPE ServiceEnum AS ENUM ('INSTAGRAM', 'TWITTER'); " + "EXCEPTION " + "WHEN duplicate_object THEN null; " + "END $$;" ) SchemaUtils.create(Posts) }
ここで、注意しないといけないのが例外処理を行なっている点。
はじめて実行するときは、データ型がまだ定義されていない状態なので次のように書いたとしてもエラーは発生しない。
exec("CREATE TYPE ServiceEnum AS ENUM ('INSTAGRAM', 'TWITTER');")
ところが、2回目以降になると次のようなエラーが発生してしまう。
Exception in thread "main" org.jetbrains.exposed.exceptions.ExposedSQLException: org.postgresql.util.PSQLException: ERROR: type "serviceenum" already exists
これはすでに存在するデータ型を作成しようとしたために発生した例外。
なので、上記のコードではEXCEPTION
を使って例外処理を行いエラーの発生を制御している。
参考
DataTypes · JetBrains/Exposed Wiki · GitHub
Check if a user-defined type already exists in PostgreSQL - Stack Overflow
Exposed(DAO/DSL) で PostgreSQL からあれこれ SELECT するサンプル・覚書 - インドカレーファンクラブ