jOOQ:カスタムタイプの実現方法

もちろん、データベース上ではカスタムタイプを定義しないものとする。可能なDBもあるが、話がややこしくなるのでここでは行わない。

ここで行いたいことは、例えば単純な32ビット整数値(DB上ではINTとかINTEGER)を、Java上ではIntegerとしてアクセスするのではなく、独自の別の型としてアクセスできないかと言うことだ。

問題

例えば、usr_levelという32ビット整数のテーブル列があるとして、Javaプログラム上ではこれを必ずUserLevelというenumで扱うものとした場合、列の出し入れでは32ビット整数ではなく、UserLevelで行えた方が便利だ。つまり、以下のような定義があり、

public enum UserLevel {
  NONE, USER, ADMIN, DEVEL;
}

例えば、jOOQの生成するレコード定義のコードとしては、

    public TblUserRecord setUsrLevel(Integer value) {
        set(3, value);
        return this;
    }

ではなく、

    public TblUserRecord setUsrLevel(UserLevel value) {
        set(3, value);
        return this;
    }

となっていて欲しい。もちろん値の取得の場合も、

    public Integer getUsrLevel() {
        return (Integer) get(3);
    }

ではなく、

    public UserLevel getUsrLevel() {
        return (UserLevel) get(3);
    }

となっていて欲しいのである。

これを行わせる方法がある。

解決方法

以下に解決方法がある。順を追って説明していく

準備

先に示したUserLevel定義とInteger型との相互変換を行うクラスを定義する。

  public class UserLevelConverter implements org.jooq.Converter<Integer, UserLevel> {
    @Override
    public UserLevel from(Integer databaseObject) {
      return UserLevel.values()[databaseObject];
    }

    @Override
    public Integer to(UserLevel userObject) {
      return userObject.ordinal();
    }

    @Override
    public Class<Integer> fromType() {
      return Integer.class;
    }

    @Override
    public Class<UserLevel> toType() {
      return UserLevel.class;
    }    
  }

jOOQの生成定義

これらをコードジェネレーションxmlに設定する。

<database>
  <forcedTypes>
    <forcedType>
      <userType>foo.bar.UserLevel</userType>
      <converter>foo.bar.UserLevelConverter</converter>
      <includeExpression>USR_LEVEL</includeExpression>
    </forcedType>
  </forcedTypes>
</database>

includeExpressionには、列名を指定するのだが、例のように列名そのものでもよいし、「TBL_SAMPLE\.USR_LEVEL」のようにテーブル名を指定してもよい。複数ある場合は”|”で区切ればよい。要するにJavaの正規表現が使用できる。

以上でjOOQのコードジェネレーション時に、そもそもデータベース型としては存在しないUserLevelというカスタム型に対応させることができる。