1. 程式人生 > >【MyBatis原始碼分析】TypeHandler解析屬性配置元素詳述及相關列舉使用高階進階

【MyBatis原始碼分析】TypeHandler解析屬性配置元素詳述及相關列舉使用高階進階

TypeHandler解析

接著看一下typeHandlerElement(root.evalNode("typeHandlers"));方法,這句讀取的是<configuration>下的<typeHandlers>節點,程式碼實現為:

private void typeHandlerElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          String typeHandlerPackage = child.getStringAttribute("name");
          typeHandlerRegistry.register(typeHandlerPackage);
        } else {
          String javaTypeName = child.getStringAttribute("javaType");
          String jdbcTypeName = child.getStringAttribute("jdbcType");
          String handlerTypeName = child.getStringAttribute("handler");
          Class<?> javaTypeClass = resolveClass(javaTypeName);
          JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
          Class<?> typeHandlerClass = resolveClass(handlerTypeName);
          if (javaTypeClass != null) {
            if (jdbcType == null) {
              typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
            } else {
              typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
            }
          } else {
            typeHandlerRegistry.register(typeHandlerClass);
          }
        }
      }
    }
  }

Mybatis中的TypeHandler是什麼?

  無論是 MyBatis 在預處理語句(PreparedStatement)中設定一個引數時,還是從結果集中取出一個值時,都會用型別處理器將獲取的值以合適的方式轉換成 Java 型別。Mybatis預設為我們實現了許多TypeHandler, 當我們沒有配置指定TypeHandler時,Mybatis會根據引數或者返回結果的不同,預設為我們選擇合適的TypeHandler處理。

那麼,Mybatis為我們實現了哪些TypeHandler呢?  我們怎麼自定義實現一個TypeHandler ?  這些都會在接下來的mybatis的原始碼中看到。

Mybatis的初始化TypeHandler初始化註冊物件為如下程式碼:
public TypeHandlerRegistry() {
    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    register(JdbcType.BOOLEAN, new BooleanTypeHandler());
    register(JdbcType.BIT, new BooleanTypeHandler());

    register(Byte.class, new ByteTypeHandler());
    register(byte.class, new ByteTypeHandler());
    register(JdbcType.TINYINT, new ByteTypeHandler());

    register(Short.class, new ShortTypeHandler());
    register(short.class, new ShortTypeHandler());
    register(JdbcType.SMALLINT, new ShortTypeHandler());

    register(Integer.class, new IntegerTypeHandler());
    register(int.class, new IntegerTypeHandler());
    register(JdbcType.INTEGER, new IntegerTypeHandler());

    register(Long.class, new LongTypeHandler());
    register(long.class, new LongTypeHandler());

    register(Float.class, new FloatTypeHandler());
    register(float.class, new FloatTypeHandler());
    register(JdbcType.FLOAT, new FloatTypeHandler());

    register(Double.class, new DoubleTypeHandler());
    register(double.class, new DoubleTypeHandler());
    register(JdbcType.DOUBLE, new DoubleTypeHandler());

    register(Reader.class, new ClobReaderTypeHandler());
    register(String.class, new StringTypeHandler());
    register(String.class, JdbcType.CHAR, new StringTypeHandler());
    register(String.class, JdbcType.CLOB, new ClobTypeHandler());
    register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
    register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());
    register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
    register(JdbcType.CHAR, new StringTypeHandler());
    register(JdbcType.VARCHAR, new StringTypeHandler());
    register(JdbcType.CLOB, new ClobTypeHandler());
    register(JdbcType.LONGVARCHAR, new ClobTypeHandler());
    register(JdbcType.NVARCHAR, new NStringTypeHandler());
    register(JdbcType.NCHAR, new NStringTypeHandler());
    register(JdbcType.NCLOB, new NClobTypeHandler());

    register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
    register(JdbcType.ARRAY, new ArrayTypeHandler());

    register(BigInteger.class, new BigIntegerTypeHandler());
    register(JdbcType.BIGINT, new LongTypeHandler());

    register(BigDecimal.class, new BigDecimalTypeHandler());
    register(JdbcType.REAL, new BigDecimalTypeHandler());
    register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
    register(JdbcType.NUMERIC, new BigDecimalTypeHandler());

    register(InputStream.class, new BlobInputStreamTypeHandler());
    register(Byte[].class, new ByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
    register(byte[].class, new ByteArrayTypeHandler());
    register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
    register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.BLOB, new BlobTypeHandler());

    register(Object.class, UNKNOWN_TYPE_HANDLER);
    register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
    register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);

    register(Date.class, new DateTypeHandler());
    register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
    register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
    register(JdbcType.TIMESTAMP, new DateTypeHandler());
    register(JdbcType.DATE, new DateOnlyTypeHandler());
    register(JdbcType.TIME, new TimeOnlyTypeHandler());

    register(java.sql.Date.class, new SqlDateTypeHandler());
    register(java.sql.Time.class, new SqlTimeTypeHandler());
    register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());

    // mybatis-typehandlers-jsr310
    try {
      // since 1.0.0
      register("java.time.Instant", "org.apache.ibatis.type.InstantTypeHandler");
      register("java.time.LocalDateTime", "org.apache.ibatis.type.LocalDateTimeTypeHandler");
      register("java.time.LocalDate", "org.apache.ibatis.type.LocalDateTypeHandler");
      register("java.time.LocalTime", "org.apache.ibatis.type.LocalTimeTypeHandler");
      register("java.time.OffsetDateTime", "org.apache.ibatis.type.OffsetDateTimeTypeHandler");
      register("java.time.OffsetTime", "org.apache.ibatis.type.OffsetTimeTypeHandler");
      register("java.time.ZonedDateTime", "org.apache.ibatis.type.ZonedDateTimeTypeHandler");
      // since 1.0.1
      register("java.time.Month", "org.apache.ibatis.type.MonthTypeHandler");
      register("java.time.Year", "org.apache.ibatis.type.YearTypeHandler");

    } catch (ClassNotFoundException e) {
      // no JSR-310 handlers
    }

    // issue #273
    register(Character.class, new CharacterTypeHandler());
    register(char.class, new CharacterTypeHandler());
  }
其實大部分需求上面的註冊已經滿足!接下來我們先看typeHandler相關Xml配置資訊:
<typeHandlers>
         <!--javaType 配置java型別,例如String, 如果配上javaType, 那麼指定的typeHandler就只作用於指定的型別 -->
        <!--<typeHandler handler="Handler.AddressTypeHandler"  javaType="model.Address" jdbcType="VARCHAR" />-->
        <!--<typeHandler handler="org.apache.ibatis.type.EnumTypeHandler"  /> 使用列舉名稱作為引數傳遞-->
        <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler"  javaType="enums.web" /><!-- 使用整數下標作為引數傳遞-->
        <!--<typeHandler handler="org.apache.ibatis.type.EnumTypeHandler"  javaType="enums.web" /><!– 使用字串作為引數傳遞–>-->
        <!-- 配置package的時候,mybatis會去配置的package掃描TypeHandler -->
        <package name="Handler"/>

    </typeHandlers>

我們來自定義實現一個自定義Handler

假設郵箱可用拼接的方式儲存

定義一個郵箱類:

/**
 * Copyright (C), 2015-2018, XXX有限公司
 * FileName: AssembledMail
 * Author:   temp
 * Date:     2018/3/29 13:51
 * Description:
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改時間           版本號              描述
 */
package model;

/**
 * 郵箱類
 *  組裝郵箱<br>
 * 〈〉
 *
 * @author temp
 * @create 2018/3/29
 * @since 1.0.0
 */
public class AssembledMail {

    private String head;

    private String tail;

    public AssembledMail(){

    }

    public AssembledMail(String head,String tail){

        this.head = head;
        this.tail = tail;
    }

    public String getHead() {
        return head;
    }

    public String getTail() {
        return tail;
    }

    public void setHead(String head) {
        this.head = head;
    }

    public void setTail(String tail) {
        this.tail = tail;
    }

    @Override
    public String toString() {
        return head + "@" + tail;
    }
}

繼承並實現BaseTypeHandler可實現註冊TypeHandler(可配置JavaType及JdbcType)

/**
 * Copyright (C), 2015-2018, XXX有限公司
 * FileName: AddressTypeHandler
 * Author:   temp
 * Date:     2018/3/29 9:58
 * Description:
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改時間           版本號              描述
 */
package Handler;

import model.AssembledMail;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 〈一句話功能簡述〉<br> 
 * 〈〉
 *
 * @author temp
 * @create 2018/3/29
 * @since 1.0.0
 */
@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes(value = {AssembledMail.class})
public class AddressTypeHandler  extends BaseTypeHandler<AssembledMail> {


    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, AssembledMail address, JdbcType jdbcType) throws SQLException {
        preparedStatement.setString(i,address.toString());

    }

    @Override
    public AssembledMail getNullableResult(ResultSet resultSet, String s) throws SQLException {

        return get(resultSet.getString(s));
    }

    @Override
    public AssembledMail getNullableResult(ResultSet resultSet, int i) throws SQLException {

        return get(resultSet.getString(i));
    }

    @Override
    public AssembledMail getNullableResult(CallableStatement callableStatement, int i) throws SQLException {

        return get(callableStatement.getString(i));
    }

    private AssembledMail get(String sta){
        AssembledMail assembledMail = new AssembledMail();
        assembledMail.setHead(sta.substring(0, sta.indexOf("@")));
        assembledMail.setTail(sta.substring(sta.indexOf("@")+1,sta.length()));
        return assembledMail;
    }
}

開起xml掃描這個類

xml對映配置

<resultMap type="Mail" id="MailResultMap">
         <result column="id" property="id" />
         <result column="create_time" property="createTime" />
         <result column="modify_time" property="modifyTime" />
         <result column="web_id" property="webId" />
         <result column="mail" property="mail" typeHandler="Handler.AddressTypeHandler"/>
         <result column="use_for" property="useFor" />
     </resultMap>

接下來修改Mail的mail的型別為AssemblerMail型別

接下來我們測試一下

 @Test
	 public void testInsert() {
	    Mail mail1 = new Mail(1, new AssembledMail("ztp","qq.com"), "個人使用");
	    System.out.println(mailDao.insertMail(mail1));
	 }

結果mail這個單個欄位已經對映進去

接下來我們查詢一下

@Test
	 public void testDelete() {
	     System.out.println(mailDao.deleteMail(12));
	 }

結果符合我們的要求.

實現簡單實現自定義Handler後我們按照業務場景實現比較多的列舉對映

前面的xml配置檔案已經簡單說明了Mybatis自帶的針對列舉的對映類區別

我們來實現一下列舉Handler:

定義一個列舉類

/**
 * Copyright (C), 2015-2018, XXX有限公司
 * FileName: web
 * Author:   temp
 * Date:     2018/3/29 11:03
 * Description:
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改時間           版本號              描述
 */
package enums;

import org.apache.ibatis.type.MappedTypes;

/**
 * 〈一句話功能簡述〉<br> 
 * 〈〉
 *
 * @author temp
 * @create 2018/3/29
 * @since 1.0.0
 */
/*@MappedTypes(enums.web.class)*/
public enum  web {

    SING(1,"新浪"),
    QQ(2,"QQ"),
    SOHU(3,"搜狐"),
    FIREFOX(4,"火狐");

    private int id;

    private String name;

    private web(int id,String name){

        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

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

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return super.toString();
    }

    public static web getweb(int id){
        if(id == 1){

            return SING;
        }else if(id == 2){

            return QQ;
        }else if(id == 3){

            return SOHU;
        }else if(id == 4){

            return FIREFOX;
        }
        return null;
    }
}

配置屬性對映

<resultMap type="Mail" id="MailResultMap">
         <result column="id" property="id" />
         <result column="create_time" property="createTime" />
         <result column="modify_time" property="modifyTime" />
         <result column="web_id" property="webId" typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler" />
         <result column="mail" property="mail" typeHandler="Handler.AddressTypeHandler"/>
         <result column="use_for" property="useFor" />
     </resultMap>

接下來修改Mail的webId的型別為列舉web

接下來我們看實際執行效果:

@Test
	 public void testInsert() {
	    Mail mail1 = new Mail(web.FIREFOX, new AssembledMail("ztp","qq.com"), "個人使用");
	    System.out.println(mailDao.insertMail(mail1));
	 }

效果

查詢結果

從原始碼實現中我們可以知道兩點,<typeHandlers>標籤下可以定義<package>和<typeHandler>兩種標籤,但是看第4行和第7行的判斷,這是一段if...else...,因此可以知道<package>標籤和<typeHandler>標籤只能定義其中的一種

接下來我們繼續看到6行程式碼:

public void register(String packageName) {
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
    resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);
    Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses();
    for (Class<?> type : handlerSet) {
      //Ignore inner classes and interfaces (including package-info.java) and abstract classes
      if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
        register(type);
      }
    }
  }
第3行根據路徑packageName尋找它下面的".class"檔案拿到所有的".class"檔案對應的類的Class,然後遍歷所有的Class,做了三層判斷
  • 必須不是匿名類
  • 必須不是介面
  • 必須不是抽象成員類
此時此Class對應的類符合條件,會進行註冊,通過register方法進行註冊,看一下方法實現:
public void register(Class<?> typeHandlerClass) {
    boolean mappedTypeFound = false;
    MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
    if (mappedTypes != null) {
      for (Class<?> javaTypeClass : mappedTypes.value()) {
        register(javaTypeClass, typeHandlerClass);
        mappedTypeFound = true;
      }
    }
    if (!mappedTypeFound) {
      register(getInstance(null, typeHandlerClass));
    }
  }
第3行獲取類上面的註解MappedTypes,如果MappedTypes註解中有定義value屬性且指定了物件的class,那麼第4行~第7行的判斷優先取這個Class。

我們繼續看第6行:

public void register(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
    register(javaTypeClass, getInstance(javaTypeClass, typeHandlerClass));
  }
方法過載,我們看一下getInstance方法:
public <T> TypeHandler<T> getInstance(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
    if (javaTypeClass != null) {
      try {
        Constructor<?> c = typeHandlerClass.getConstructor(Class.class);
        return (TypeHandler<T>) c.newInstance(javaTypeClass);
      } catch (NoSuchMethodException ignored) {
        // ignored
      } catch (Exception e) {
        throw new TypeException("Failed invoking constructor for handler " + typeHandlerClass, e);
      }
    }
    try {
      Constructor<?> c = typeHandlerClass.getConstructor();
      return (TypeHandler<T>) c.newInstance();
    } catch (Exception e) {
      throw new TypeException("Unable to find a usable constructor for " + typeHandlerClass, e);
    }
  }
通過反射它的泛型,來知道你想用這個TypeHandler處理的java型別,直接呼叫newInstance(),返回物件;
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
    MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
    if (mappedJdbcTypes != null) {
      for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
        register(javaType, handledJdbcType, typeHandler);
      }
      if (mappedJdbcTypes.includeNullJdbcType()) {
        register(javaType, null, typeHandler);
      }
    } else {
      register(javaType, null, typeHandler);
    }
  }
方法的過載,第2行獲取類上面的註解MappedJdbcTyoes,獲取MappedJdbcTyoes註解中有定義JdbcType的屬性;如果有jdbcType則用當前的jdbcTyoe;否則給null作為引數;

接下來看第5行程式碼:

private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
    if (javaType != null) {
      Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
      if (map == null) {
        map = new HashMap<JdbcType, TypeHandler<?>>();
        TYPE_HANDLER_MAP.put(javaType, map);
      }
      map.put(jdbcType, handler);
    }
    ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
  }

如果當前指定的typeHandler存在,則在當前的儲存map中取獲取;如果當前沒有儲存,則建立一個儲存map;將指定的jdbctype作為key,儲存typeHandler;

TYPE_HANDLER_MAP添加當前的 key:javaType value : map

用一個新增加的ALL_TYPE_HANDLERS_MAP儲存新增加的typeHandler;

接下來我們看不使用註解的方式:

public <T> void register(TypeHandler<T> typeHandler) {
    boolean mappedTypeFound = false;
    MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);
    if (mappedTypes != null) {
      for (Class<?> handledType : mappedTypes.value()) {
        register(handledType, typeHandler);
        mappedTypeFound = true;
      }
    }
    // @since 3.1.0 - try to auto-discover the mapped type
    if (!mappedTypeFound && typeHandler instanceof TypeReference) {
      try {
        TypeReference<T> typeReference = (TypeReference<T>) typeHandler;
        register(typeReference.getRawType(), typeHandler);
        mappedTypeFound = true;
      } catch (Throwable t) {
        // maybe users define the TypeReference with a different type and are not assignable, so just ignore it
      }
    }
    if (!mappedTypeFound) {
      register((Class<T>) null, typeHandler);
    }
  }
從1~9行程式碼還是進行對MappedTypes註解進行判斷是否存在,上面已經講述了;

接下來我們看到第11~18行獲取到泛型指定的類進行給到方法入參,接下來就是跟上面方法一樣了;

接下來我們看使用(javaType,jdbcType,handler)的方式:

          String javaTypeName = child.getStringAttribute("javaType");
          String jdbcTypeName = child.getStringAttribute("jdbcType");
          String handlerTypeName = child.getStringAttribute("handler");
          Class<?> javaTypeClass = resolveClass(javaTypeName);
          JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
          Class<?> typeHandlerClass = resolveClass(handlerTypeName);
          if (javaTypeClass != null) {
            if (jdbcType == null) {
              typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
            } else {
              typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
            }
          } else {
            typeHandlerRegistry.register(typeHandlerClass);
          }
我們看到第4行程式碼:
protected Class<?> resolveClass(String alias) {
    if (alias == null) {
      return null;
    }
    try {
      return resolveAlias(alias);
    } catch (Exception e) {
      throw new BuilderException("Error resolving class. Cause: " + e, e);
    }
  }
根據配置的javaType值,如果為空則直接返回null;

繼續往下面看到resolveAlias方法:

public <T> Class<T> resolveAlias(String string) {
    try {
      if (string == null) {
        return null;
      }
      // issue #748
      String key = string.toLowerCase(Locale.ENGLISH);
      Class<T> value;
      if (TYPE_ALIASES.containsKey(key)) {
        value = (Class<T>) TYPE_ALIASES.get(key);
      } else {
        value = (Class<T>) Resources.classForName(string);
      }
      return value;
    } catch (ClassNotFoundException e) {
      throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
    }
  }

第七行將字串首字母小寫轉換

第九行判斷當前類名解析器中有沒有這個物件,如果有則直接返回;如果沒有則根據配置的物件路徑返回物件,路徑錯誤則異常;

JdbcType jdbcType = resolveJdbcType(jdbcTypeName);從JdbcType返回jdbcType物件;

handlerTypeName獲取和javaTypeName一樣,就不多說了;

解析完三個引數;接著就根據引數分別註冊不同的typeHandler;

接下來我們學習下自定義TypeHandler處理列舉;

在Mybatis中,處理列舉類的TypeHandler有兩個:

  1. EnumTypeHandler: 用於儲存列舉名
  2. EnumOrdinalTypeHandler: 用於儲存列舉的序號。

在實際專案中,以上往往不能滿足我們的需求。

需求分析

列舉需要包含兩個屬性,name(用於顯示), id(實際的列舉值)。資料庫儲存列舉值(id)。

這很明顯Mybatis提供的兩個列舉TypeHandler不能滿足我們的需求。此時,我們可以自定義一個通用的列舉TypeHandler來滿足我們的需求。

自定義列舉TypeHandler

通用列舉DisplayedEnum 

/**
 * Copyright (C), 2015-2018, XXX有限公司
 * FileName: DisplayedEnum
 * Author:   temp
 * Date:     2018/3/29 16:05
 * Description:
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改時間           版本號              描述
 */
package TypeHandler;

import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;

/**
 * 〈一句話功能簡述〉<br> 
 * 〈〉
 *
 * @author temp
 * @create 2018/3/29
 * @since 1.0.0
 */
public interface  DisplayedEnum {
    String DEFAULT_VALUE_NAME = "id";

    String DEFAULT_LABEL_NAME = "name";

    default Integer getValue() {
        Field field = ReflectionUtils.findField(this.getClass(), DEFAULT_VALUE_NAME);
        if (field == null)
            return null;
        try {
            field.setAccessible(true);
            return Integer.parseInt(field.get(this).toString());
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }


    default String getLabel() {
        Field field = ReflectionUtils.findField(this.getClass(), DEFAULT_LABEL_NAME);
        if (field == null)
            return null;
        try {
            field.setAccessible(true);
            return field.get(this).toString();
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    static <T extends Enum<T>> T valueOfEnum(Class<T> enumClass, Integer value) {
        if (value == null)
            throw  new IllegalArgumentException("DisplayedEnum value should not be null");
        if (enumClass.isAssignableFrom(DisplayedEnum.class))
            throw new IllegalArgumentException("illegal DisplayedEnum type");
        T[] enums = enumClass.getEnumConstants();
        for (T t: enums) {
            DisplayedEnum displayedEnum = (DisplayedEnum)t;
            if (displayedEnum.getValue().equals(value))
                return (T) displayedEnum;
        }
        throw new IllegalArgumentException("cannot parse integer: " + value + " to " + enumClass.getName());
    }

}

修改之前的普通列舉類

/**
 * Copyright (C), 2015-2018, XXX有限公司
 * FileName: web
 * Author:   temp
 * Date:     2018/3/29 11:03
 * Description:
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改時間           版本號              描述
 */
package enums;

import TypeHandler.DisplayedEnum;
import org.apache.ibatis.type.MappedTypes;

/**
 * 〈一句話功能簡述〉<br> 
 * 〈〉
 *
 * @author temp
 * @create 2018/3/29
 * @since 1.0.0
 */
public enum  web implements DisplayedEnum {

    SING(1,"新浪"),
    QQ(2,"QQ"),
    SOHU(3,"搜狐"),
    FIREFOX(4,"火狐");

    private int id;

    private String name;

    private web(int id,String name){

        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

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

    public void setName(String name) {
        this.name = name;
    }


}

說明:
普通列舉類通過實現DisplayedEnum介面,就可以:
  1. 通過getId()獲取列舉值。
  2. 通過getNmae()獲取列舉的name屬性。
  3. 通過valueOfEnum()將Integer值轉換為指定的列舉型別。
以上就是一個普通列舉類的示例。

自定義列舉TypeHandler

/**
 * Copyright (C), 2015-2018, XXX有限公司
 * FileName: DefaultEnumTypeHandler
 * Author:   temp
 * Date:     2018/3/29 16:48
 * Description:
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改時間           版本號              描述
 */
package Handler;

import TypeHandler.DisplayedEnum;
import enums.web;
import model.AssembledMail;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 〈一句話功能簡述〉<br> 
 * 〈〉
 *
 * @author temp
 * @create 2018/3/29
 * @since 1.0.0
 */
@MappedJdbcTypes(value = JdbcType.TINYINT, includeNullJdbcType = true)
@MappedTypes(value = {web.class})
public class DefaultEnumTypeHandler extends BaseTypeHandler<DisplayedEnum> {

    private Class<DisplayedEnum> type;

    public DefaultEnumTypeHandler(){
        System.out.print(111);
    };

    public DefaultEnumTypeHandler(Class<DisplayedEnum> type) {
        if (type == null) throw new IllegalArgumentException("Type argument cannot be null");
        this.type = type;
    }

    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, DisplayedEnum displayedEnum, JdbcType jdbcType) throws SQLException {
        preparedStatement.setInt(i, displayedEnum.getValue());
    }

    @Override
    public DisplayedEnum getNullableResult(ResultSet resultSet, String s) throws SQLException {
        return convert(resultSet.getInt(s));
    }

    @Override
    public DisplayedEnum getNullableResult(ResultSet resultSet, int i) throws SQLException {
        return convert(resultSet.getInt(i));
    }

    @Override
    public DisplayedEnum getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        return convert(callableStatement.getInt(i));
    }

    private DisplayedEnum convert(int status){
        DisplayedEnum[] objs = type.getEnumConstants();
        for(DisplayedEnum em: objs){
            if(em.getValue() == status){
                return  em;
            }
        }
        return null;
    }
}

使用我們自定義的DefaultEnumTypeHandler

注意:不能同時和包掃描使用

<typeHandlers>
         <!--javaType 配置java型別,例如String, 如果配上javaType, 那麼指定的typeHandler就只作用於指定的型別 -->
        <typeHandler handler="Handler.AddressTypeHandler"/>
        <!--<typeHandler handler="org.apache.ibatis.type.EnumTypeHandler"  /> 使用列舉名稱作為引數傳遞-->
        <typeHandler handler="Handler.DefaultEnumTypeHandler" /><!-- 使用整數下標作為引數傳遞-->
    </typeHandlers>

由於Mybatis預設在處理列舉型別的時候會使用EnumTypeHandler(只儲存及轉換列舉型別的名字), 因此,我們需要手動指定使用DefaultEnumTypeHandler。示例如下:

<resultMap type="Mail" id="MailResultMap">
         <result column="id" property="id" />
         <result column="create_time" property="createTime" />
         <result column="modify_time" property="modifyTime" />
         <result column="web_id" property="webId"  typeHandler="Handler.DefaultEnumTypeHandler" />
         <result column="mail" property="mail"   typeHandler="Handler.AddressTypeHandler"/>
         <result column="use_for" property="useFor" />
     </resultMap>
測試結果正確即我們需要通過使用typeHandler來指定。
以上是我們應用在實際專案中的一個對於Mybatis處理列舉類的方案。我看大多數人也都是這樣在用。