-
Notifications
You must be signed in to change notification settings - Fork 4
Description
Current project I am using postgres for the first time.
Having used Mysql Enums in the past, and being a fan of typed data -- I ended up with a postgres style enum. Turns out its not quite so easy to use -- in the db, jdbc, sql2o or vok-orm
But not impossible --
This is what I did to make it work for me -- there may well be much better ways.
First off -- the naive thing to assume is "It just works" -- the error one gets attemping to put a Enum value into a query or insert is not obvious -- it is a side effect of postgress exposing somewhat the fact that enums are user defined types but not enough to use them :)
So you get an error about the expression expecting a user defined type but being given a varying.
( google saved the day again )
To work around -- one needs to explicitly add a cast to the SQL. I used a custom SQL (thank you for assuming that one needs to do that sometimes !!!) like this:
This is a simple generic function that updates 1 property of a kotlin class into 1 field in a DB table.
Called like
field = MyEnumValue
update( ::field ) // for normal fields
update(::field, "::my_user_defined_type" ) // for enums and other convertable user types
fun update(property : KMutableProperty0<*>,cast:String="" /* for status enum */) = db {
con.createQuery("UPDATE devices SET ${property.name} = :value${cast} WHERE id = :id")
.addParameter( "value" , property.getter.call() )
.addParameter("id", id )
.executeUpdate()
The relevant bit is the "${cast}" which will insert the "::my_user_defined_type" to create ":value::my_user_defined_type"
I wasn't sure how well vok or sql2o would handle this expressing given that it includes a ":" -- but it did.
To READ the DB with the enum I made a custom converter -- I didnt put the effort into making it fully generic -- the reified types and kotlin enum reflection is a bit subtle and under-implemented.
SO I hard coded the single enum "ConversionState" -- a creative author should be able to extend this genericly
VokOrm.dataSourceConfig.apply {
... configuration code ...
... then this call
registerEnumConverter( object : EnumConverterFactory {
override fun <E : Enum<out Enum<*>>?> newConverter(enumClass : Class<E>?) : Converter<E> =
object : Converter<E> {
override fun toDatabaseParam(v : E) : Any = (v as ConversionState).name
override fun convert(v : Any?) : E = ConversionState.valueOf(v.toString()) as E
}
})
}