2012-04-18 78 views
13

我知道這已經被問過,但是我沒有能夠實現一個基於我目前發現的信息的解決方案。所以也許有人可以向我解釋。MyBatis枚舉用法

我有一個表「狀態」。它有兩列:id和name。 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> 

但由於某些原因,當我嘗試檢索枚舉,東西壞了,但也不例外被拋出。

+1

ISTM setId()在一個枚舉是一個可怕的想法... :) – 2017-11-02 10:29:20

回答

18

我從幾個角度研究過這個問題,這裏是我的發現。警告:我使用MyBatis-3.1.1完成了所有這些調查,因此在早期版本中可能會有不同的表現。

首先,MyBatis有一個內置的EnumTypeHandler。默認情況下,只要您將Java枚舉指定爲resultType或parameterType,就會處理該類型。對於查詢,當試圖將數據庫記錄轉換爲Java枚舉時,EnumTypeHandler只接受一個參數並嘗試查找與該值相對應的Java枚舉值。

一個例子會更好地說明。假設當我傳遞「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忽略了查詢返回的第二個值。

更改您的查詢

SELECT UPPER(ls.name) 

導致它的工作:返回Status.READY枚舉。

所以接下來我嘗試爲狀態枚舉定義我自己的TypeHandler。不幸的是,與默認EnumTypeHandler一樣,我只能得到其中一個值(id或name),以便引用正確的Enum,而不是兩者。因此,如果數據庫ID與您上面硬編碼的值不匹配,那麼您將會遇到不匹配。如果確保數據庫ID始終與您在枚舉中指定的ID匹配,那麼您只需從數據庫中獲取名稱(轉換爲大寫)即可。

然後我想我會變聰明並實現一個MyBatis ObjectFactory,同時獲取int id和String名稱,並確保這些匹配在Java枚舉中,我傳回,但這不起作用,因爲MyBatis不會調用Java枚舉類型的ObjectFactory(至少我無法讓它工作)。

所以我的結論是,只要您需要將數據庫中的名稱與枚舉常量名稱相匹配,就可以輕鬆使用MyBatis中的Java枚舉 - 既可以使用內置的EnumTypeHandler,也可以定義自己的如果執行UPPER名稱)不足以匹配Java枚舉名稱。在很多情況下,這就足夠了,因爲枚舉值可能只是列上的一個檢查約束,它只有一個值,而不是一個id。如果您還需要匹配int id和名稱,則在設置Java枚舉和/或數據庫條目時手動設置ID匹配。

最後,如果你想看到一個這樣的工作示例,請參閱MyBatis koan的koan 23:https://github.com/midpeter444/mybatis-koans。如果您只想查看我的解決方案,請查看completed-koans/koan23目錄。我還有一個通過Java枚舉將記錄插入數據庫的例子。

6

您可以使用Custom TypeHandler將結果直接轉換爲ENUM,這樣就不需要將所有值都作爲UPPER CASE ENUM Names放入數據庫中。

這是你的狀態枚舉自定義處理程序將如何看起來像

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中加入此代碼來處理狀態。

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

現在讓我們來考慮,你必須在你的道以下兩個函數的例子,

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兩個映射器是狀態,myBatis將使用這種類型的CustomTypeHandler,即StatusTypeHandler而不是EnumTypeHandler,它默認使用處理枚舉,所以不需要維護正確的Enum數據庫中的名稱。

+0

如果我需要返回枚舉而不傳遞參數(id,名稱等)? – axcdnt 2015-04-15 13:22:26

+2

這就是提供乾淨解決方案的答案。依靠大寫字母很難看。 – Eduardo 2016-09-02 15:04:25