web-dev-qa-db-ja.com

MyBatis列挙型の使用

これは以前に尋ねられたことは知っていますが、これまでに見つけた情報に基づいてソリューションを実装することはできませんでした。多分誰かがそれを私に説明できるでしょう。

「ステータス」という表があります。 IDと名前の2つの列があります。 idはPKです。

POJOステータスを使用する代わりに、列挙型を使用したいと思います。このような列挙型を次のように作成しました。

public enum Status {
    NEW(1), READY(2), CLOSED(3);

    private int id;

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return this.id;
    }

    Status(int id) {
        this.id = id;
    }
}

ここに私のマッパーがあります

     <select id="getStatusByName" resultType="Status" parameterType="String">       
        SELECT  ls.id, ls.name
        FROM status AS ls
        WHERE ls.name = #{name}
    </select>

しかし、何らかの理由で、列挙型を取得しようとすると、何かが壊れますが、例外はスローされません。

16
R.V.

私はいくつかの角度からこの質問に取り組みました、そしてここに私の発見があります。警告:これらすべての調査はMyBatis-3.1.1を使用して行ったため、以前のバージョンでは動作が異なる可能性がありました。

まず、MyBatisにはEnumTypeHandlerが組み込まれています。デフォルトでは、Java enumを指定すると、これがそのタイプを処理するものになります。クエリの場合、データベースレコードをJava列挙型、EnumTypeHandlerは引数を1つだけ取り、その値に対応するJava enum値を検索しようとします。

例で説明します。上記のクエリが「Ready」を引数として渡すと、2および"Ready"が返されるとします。その場合、エラーメッセージNo enum constant com.foo.Status.2が表示されます。 SELECTステートメントの順序を逆にすると

SELECT ls.name, ls.id

エラーメッセージはNo enum constant com.foo.Status.Readyです。 MyBatisが何をしているか推測できると思います。 EnumTypeHandlerはクエリから返された2番目の値を無視していることに注意してください。

クエリを

SELECT UPPER(ls.name)

機能させます:Status.READY列挙が返されます。

次に、Status列挙型に独自のTypeHandlerを定義しようとしました。残念ながら、デフォルトのEnumTypeHandlerと同様に、正しいEnumを参照するために、値(idまたはname)の1つだけを取得できました。両方を取得することはできませんでした。したがって、データベースIDが上記でハードコーディングした値と一致しない場合は、不一致になります。データベースIDが常に列挙型で指定したIDと一致することを確認した場合、データベースから必要なのは名前(大文字に変換されたもの)だけです。

次に、私は巧妙になってMyBatis ObjectFactoryを実装し、int idとString名の両方を取得して、Java enumで一致することを確認しますが、うまくいきませんでした。 MyBatisは、Java列挙型のObjectFactoryを呼び出さないため(少なくとも、機能させることができませんでした)。

したがって、私の結論は、Java MyBatisのenumは、データベースの名前をenum定数名に一致させる必要がある限り簡単です-組み込みのEnumTypeHandlerを使用するか、 SQLでUPPER(name)を実行してもJava enum名と一致するのに十分でない場合は所有します。多くの場合、列挙値は単に列と単一の値のみがあり、IDも含まれていません。intIDと名前も一致させる必要がある場合は、Java =列挙型またはデータベースエントリ、あるいはその両方。

最後に、これの実際の例を見たい場合は、MyBatisのkoansのkoan 23を参照してください: https://github.com/midpeter444/mybatis-koans 。私のソリューションを確認するだけの場合は、completed-koans/koan23ディレクトリを確認してください。また、Java列挙型を介してデータベースにレコードを挿入する例もあります。

18
quux00

カスタムTypeHandlerを使用して結果を直接ENUMに変換できるため、データベース内のすべての値を大文字のENUM名として置く必要はありません。

これは、ステータス列挙カスタムハンドラーがどのようになるかです。

public class StatusTypeHandler implements TypeHandler<Status> {

public Status getResult(ResultSet rs, String param) throws SQLException {
    return Status.getEnum(rs.getInt(param));
}

public Status getResult(CallableStatement cs, int col) throws SQLException {
    return Status.getEnum(cs.getInt(col));
}

public void setParameter(PreparedStatement ps, int paramInt, Status paramType, JdbcType jdbctype)
        throws SQLException {
    ps.setInt(paramInt, paramType.getId());
}
}

このコードを追加して、mybatis-config.xmlでデフォルトでステータスを処理するようにTypeHandlerを定義します。

    <typeHandlers> 
            <typeHandler javaType='Status' handler='StatusTypeHandler' /> 
    </typeHandlers>

Daoに次の2つの関数がある例を考えてみましょう。

Status getStatusById(int code);
Status getStatusByName(String name);

マッパーは次のようになります

<select id="getStatusById" resultType="Status" parameterType="int">       
    SELECT  ls.id
    FROM status AS ls
    WHERE ls.id = #{id}
</select>

<select id="getStatusByName" resultType="Status" parameterType="String">       
    SELECT  ls.id
    FROM status AS ls
    WHERE ls.name = #{name}
</select>

これで、両方のマッパーのresultTypeがStatusであるため、myBatisはこのタイプのCustomTypeHandlerを使用します。つまり、デフォルトでEnumの処理に使用するEnumTypeHandlerの代わりにStatusTypeHandlerを使用するため、データベースで適切なEnum名を維持する必要はありません。

9
faizan