2012-04-18 17 views
13

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

私はテーブル「ステータス」を持っています。 idとnameという2つの列があります。 idはPKです。

POJOステータスを使用する代わりに、enumを使用したいと思います。私は列挙型、何か区切りを取得しようとすると、ここに

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> 
ですが、例外なく、次のように私はそのような列挙型を作成しましたスローされます。

+1

enumのsetId()はひどい考えです... :) –

答えて

18

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

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

例をよく説明します。 "Ready"を引数として渡すと上記のクエリが2"Ready"を返したとします。その場合、エラーメッセージNo enum constant com.foo.Status.2が表示されます。私は

SELECT ls.name, ls.id 

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

SELECT UPPER(ls.name) 

にクエリを変更するには、それが動作するために発生しますStatus.READY列挙が返されます。

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

次に、私は賢明になり、MyBatis ObjectFactoryを実装し、int idとStringの両方の名前を取得し、それらがJavaのenumで一致していることを確認していますが、MyBatisはコールしませんJavaのenum型のObjectFactory(少なくとも私はそれを動作させることができませんでした)。

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

最後に、この実例を見たい場合は、MyBatis koansのkoan 23:https://github.com/midpeter444/mybatis-koansを参照してください。私の解決策を見たいだけなら、completed-koans/koan23ディレクトリを見てください。また、Java enumを使用してデータベースにレコードを挿入する例もあります。

6

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

は、これはあなたのステータス列挙型のカスタムハンドラは、このコードを追加することで、MyBatisの-config.xmlのデフォルトステータスを処理するために、あなたのTypeHandlerを定義

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()); 
} 
} 

のようになります方法です。今

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

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

あなたのマッパーはマッパーの両方のためのresultTypeととして今

<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> 

のようになります、私たちはあなたがあなたのダオに二つの機能を以下している例を考えてみましょうStatusである場合、myBatisは、このタイプのCustomTypeHandler(つまり、EnumTypeHandlerの代わりにStatusTypeHandlerを使用します)を使用して、Enumを処理するためにデフォルトで使用するので、適切なEnumを維持する必要はありませんデータベース内の名前。

+0

引数(ID、名前など)を渡さずに列挙型を返す必要がある場合はどうなりますか? – axcdnt

+2

それはきれいな解決策を提供する答えです。大文字に頼るのは醜いです。 – Eduardo