【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的原始碼中看到。
其實大部分需求上面的註冊已經滿足!接下來我們先看typeHandler相關Xml配置資訊: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()); }
<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,做了三層判斷- 必須不是匿名類
- 必須不是介面
- 必須不是抽象成員類
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有兩個:
- EnumTypeHandler: 用於儲存列舉名
- 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介面,就可以:
- 通過getId()獲取列舉值。
- 通過getNmae()獲取列舉的name屬性。
- 通過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處理列舉類的方案。我看大多數人也都是這樣在用。