17

これは以前に尋ねられたことは知っていますが、これまでに見つけた情報に基づいて解決策を実装できませんでした. おそらく誰かが私にそれを説明できます。

テーブル「ステータス」があります。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>

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

4

2 に答える 2

17

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

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

例はよりよく説明します。上記のクエリが返さ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 を定義しようとしました。残念ながら、 default と同様EnumTypeHandlerに、適切な Enum を参照するために値 (id または name) の 1 つしか取得できず、両方は取得できませんでした。したがって、データベース ID が上記でハードコーディングした値と一致しない場合、不一致が発生します。データベース ID が列挙型で指定した ID と常に一致することを確認した場合、データベースから必要なのは名前 (大文字に変換されたもの) だけです。

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

したがって、私の結論は、MyBatis の Java 列挙型は、データベースからの名前を列挙型定数名に一致させる必要がある限り、簡単です。組み込みの EnumTypeHandler を使用するか、または UPPER(name) をSQL は、Java 列挙型名と一致するには十分ではありません。多くの場合、これで十分です。列挙された値は列のチェック制約である可能性があり、id ではなく単一の値しかないためです。名前だけでなく int id も一致させる必要がある場合は、Java 列挙型やデータベース エントリを設定するときに手動で ID を一致させます。

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

于 2012-06-08T00:53:03.370 に答える
13

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

これは、Status 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 でデフォルトで Status を処理する 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 名を維持する必要はありません。

于 2013-09-03T16:20:33.907 に答える