1. 程式人生 > >mybatis TypeHandler處理自定義列舉型別

mybatis TypeHandler處理自定義列舉型別

之前做一個專案一個表字段中有很多狀態,通過狀態碼來實現各個狀態,但是在java 實體類中用int型別表示狀態碼,就出現一堆魔鬼數字。如果這樣,那麼後期程式碼維護和可讀性比較困難,如果沒有完整註釋。
後來在官方參考 中發現mybatis提供兩種列舉型別轉換器:EnumTypeHandler(預設),EnumOrdinalTypeHandler.

EnumTypeHandler主要將欄位值轉換成對應列舉物件。通過Enum.valueOf(class,string) 方法實現。部分原始碼如下

 @Override
  public E getNullableResult(ResultSet rs, String columnName) throws
SQLException { String s = rs.getString(columnName); return s == null ? null : Enum.valueOf(type, s); }

EnumOrdinalTypeHandler 是將欄位值(必須數字型別)作為列舉下標搜尋對應列舉物件.部分原始碼如下

  @Override
  public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
    int i = rs.getInt(columnName);
    if
(rs.wasNull()) { return null; } else { try { return enums[i]; } catch (Exception ex) { throw new IllegalArgumentException("Cannot convert " + i + " to " + type.getSimpleName() + " by ordinal value.", ex); } } }

int i = rs.getInt(columnName); i是返回欄位值,然後通過return enums[i];

返回對應下標列舉物件。

但是很多時候碼是根據業務確定的不一定是按照列舉下標來表示的。沒辦法這個時候只能自己動手,但是可以參考EnumOrdinalTypeHandler 做法,替換return enums[i];程式碼
自定義列舉型別轉換器MyEnumTypeHandler完整程式碼如下:

    public class MyEnumTypeHandler<E extends Enum<E>&BaseEnum<E>> extends BaseTypeHandler<E> {

    private Class<E> type;
    private E[] enums;

    public MyEnumTypeHandler(Class<E> type) throws InstantiationException, IllegalAccessException{

        if (type == null) {
              throw new IllegalArgumentException("Type argument cannot be null");
         }

        this.type=type;
        this.enums=this.type.getEnumConstants();

        if(this.enums==null){
             throw new IllegalArgumentException(type.getSimpleName() + " does not represent an enum type.");
        }       
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
         ps.setInt(i, parameter.getCode());

    }

    @Override
    public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
         int i = rs.getInt(columnName);
            if (rs.wasNull()) {
              return null;
            } else {
              try {
                return getEnum(i);
              } catch (Exception ex) {
                throw new IllegalArgumentException("Cannot convert " + i + " to " + type.getSimpleName() + " by ordinal value.", ex);
              }
            }
    }

    @Override
    public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
         int i = rs.getInt(columnIndex);
            if (rs.wasNull()) {
              return null;
            } else {
              try {
                return getEnum(i);
              } catch (Exception ex) {
                throw new IllegalArgumentException("Cannot convert " + i + " to " + type.getSimpleName() + " by ordinal value.", ex);
              }
            }
    }

    @Override
    public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        int i = cs.getInt(columnIndex);
        if (cs.wasNull()) {
          return null;
        } else {
          try {
            return getEnum(i);
          } catch (Exception ex) {
            throw new IllegalArgumentException("Cannot convert " + i + " to " + type.getSimpleName() + " by ordinal value.", ex);
          }
        }
    }

    private E getEnum(int code){

        for(E tmpE:this.enums){

            if(tmpE.getCode()==code){
                return tmpE;
            }
        }
        return null;
    }
}

BaseEnum 程式碼:

public interface BaseEnum<T> {

    int getCode();

    String getName();   

    T getEnumByCode(int code);
}

BaseEnum 介面是所有列舉必須繼承,主要為了統一性。這樣一個TypeHandler可以實現很多自定義enum.
在完成typeHandler之後需要將其加入到mybatis 配置檔案中。

<typeHandlers>
        <typeHandler handler="com.test.mybatis.typeHandler.MyEnumTypeHandler" javaType="XXEnum" />
    </typeHandlers>

考慮到如果很多枚舉出現,不能都要修改一次配置檔案,而且變化僅僅是javaType。後來發現mybatis提供一個MappedTypes 註解。只要在自定義typeHandler上實現即可。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MappedTypes {
  Class<?>[] value();
}

這個註解value是陣列型別,所以可以更方便新增。這樣mybatis配置檔案中只要將javaType屬性去掉即可,後期再多列舉也不用改動mybatis配置檔案,只要在MappedTypes註解里加新列舉即可。