CSS調整

2018年8月25日

JPAのConverterをkotlinのdelegateで代用する

Spring BootでJPA機能のひとつConverterというのがあります。
DB値 -> EntityとEntity -> DBで値を変換する為の例のあれです。

よく例として上げられる(上げやすい)のがintとboolの変換とかですね。
Entityとしてはboolで扱っておいて、DB上はintで1/0で保存する、みたいな。

このConverter自体は深く掘り下げたりはしませんが、正直、なんかうまく動かないことが多いイメージが強いです。
まあ、Entityのインスタンス生成前に挟まれるであろう処理なので、利用しているデータストアの種類やHibernate等のアクセサに深く依存してそう、というか特定ケースでのHibernateでは動作しないようなissueも上がってたような気がします。

そんなこんなで、あまり信用していないのと、ちょっとレイヤーが変わるかもだけれどEntityのfieldにdelegate貼ったら代用できるよね、と思い、試してみました。

Converterをdelegateで代用してみる


Entityを用意


@Entity
@Table
class Memo {
    @Column("title")
    var title = ""
    @Column
    var value = ""
}

特に特筆する点はないかと。

Interfaceを用意してEntityに貼る


interface TitleHaving {
    var _title: String
}

class Memo: TitleHaving {
    @Column("title")
    override var _title = ""

タイトルを持ってるよー、のインターフェースです。
Entityへはtitleを_titleへ変更して実装。

delegateを用意


class TitleQuestionConverter: ReadWriteProperty<TitleHaving, String> {
    override fun setValue(thisRef: TitleHaving, property: KProperty<*>, value: String) { thisRef._title = value }
    override fun getValue(thisRef: TitleHaving, property: KProperty<*>): String = "${thisRef._title}?"
}

タイトル文言を疑問文にしてしまうというどうでもいいdelegateにしました。

Entityへ入れる


class Memo: TitleHaving {
    @Column("title")
    override var _title = ""
    @Column
    var value = ""

    @delegate:Transient
    var title by TitleQuestionConverter()
}

delegateするfield名をtitleとして、あたかも利用する側には@Columnがついてるfieldのように見せかけます。
その実は_titleの値を変換して取得、設定された場合にはそのまま_titleへ格納、@Columnが指定されている_title側はsaveでそのまま保存され、
対する自身は@delegate:Transientで、delegate先に@Transientを指定することで永続化対象ではなくします。

getValueで対象fieldへアクセスされてしまうので、LAZYが指定されたアソシエーション列等の場合はLAZYの意味が無くなったりしますが、簡単な変換なら十分作用すると思います。

いじょ