🐧

MyBatisでJavaのenumをPostgreSQLの列挙型(enum)にマッピングする

2023/07/16に公開

MyBatisには、Java上のenumをハンドリングするためのTypeHandlerとして、下記の2つが用意されています。

  • org.apache.ibatis.type.EnumTypeHandler
    →列挙子の名前(name()で取得できる)を利用してマッピング ※MyBatisのデフォルトの設定では、このTypeHandlerが利用される
  • org.apache.ibatis.type.EnumOrdinalTypeHandler
    →列挙子の順序(ordinal()で取得できる)を利用してマッピング

上記のどちらも、PostgreSQLの列挙型(enum)にマッピングすることはできません。
(EnumTypeHandlervarchartextにはマッピングできるが、enumには直接マッピングできない)

Javaのenumを保存するならば、PostgreSQLのenumとして保存したい、、ということで、PostgreSQLのenumとして保存する方法を書いていきます。

なお、SQLアンチパターンでenum型を使うことをアンチパターンとしてあげているのもあってか、一律enumは使うべきではないようなことを書かれている記事を見かけたりもしますが、プログラム上でenumとして定義されるような項目であれば、アンチパターンで上げているようなパターンとはならないと思いますので、積極的に使っていって良いと思っています。
DB上でもenumとすることで、どんな値が入るのかDB上の型からわかるようになります。

確認した際の各バージョンは下記の通りです。

  • PostgreSQL 15.3
  • Java 17
  • Spring Boot 3.1.1
  • MyBatis 3.5.13

テーブル定義

今回は下記のようなenumとテーブルを定義します。

CREATE TYPE day AS ENUM ('SUNDAY', 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY');

CREATE TABLE pgenum_records (
  id serial,
  day day
);

エンティティ

DB上のenumにあわせて、Javaでも下記のようなenumを定義します。

package com.github.onozaty.mybatis.pg.example.domain;

public enum Day {

    SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
}

テーブルにあわせて、下記のようなエンティティクラスを定義します。

package com.github.onozaty.mybatis.pg.example.domain;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class PgEnumRecord {

    private int id;

    private Day day;
}

リポジトリ

リポジトリです。

package com.github.onozaty.mybatis.pg.example.repository;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import com.github.onozaty.mybatis.pg.example.domain.PgEnumRecord;

@Mapper
public interface PgEnumRepository {

    @Select("""
INSERT INTO pgenum_records (
  day
)
VALUES (
  #{day}
)
RETURNING *
    """)
    PgEnumRecord insert(PgEnumRecord record);

    @Select("""
SELECT * FROM pgenum_records
    """)
    List<PgEnumRecord> selectAll();

    @Select("""
SELECT * FROM pgenum_records WHERE id = #{id}
    """)
    PgEnumRecord select(int id);

    @Update("""
UPDATE pgenum_records
SET
  day = #{day}
WHERE
  id = #{id}
    """)
    void update(PgEnumRecord record);
}

TypeHandlerの設定

最初に書いた通り、MyBatisの標準の機能ではPostgreSQLのenum用のTypeHandlerは無いので、下記のライブラリを利用します。

依存関係を追加します。(Gradleを利用しているのでbuild.gradleに追加)

    implementation 'com.github.onozaty:mybatis-postgresql-typehandlers:1.0.2'

MyBatisでJavaのenumをマッピングするTypeHandlerを上記ライブラリのcom.github.onozaty.mybatis.pg.type.pgenum.PgEnumTypeHandlerに変更します。

Spring Bootのapplication.propertiesで、mybatis.configuration.default-enum-type-handlerにて指定できます。

mybatis.configuration.default-enum-type-handler=com.github.onozaty.mybatis.pg.type.pgenum.PgEnumTypeHandler

これでJavaのenumがPostgreSQLのenumとしてマッピングされるようになります。

コード全体

コード全体は下記プロジェクトになります。
enum以外にも、JavaのオブジェクトとPostgreSQLのJSONB、JavaのListとPostgreSQLのARRAYをマッピングするためのコードが含まれています。

参考

Discussion