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的源码中看到。
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,做了三层判断
- 必须不是匿名类
- 必须不是接口
- 必须不是抽象成员类
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>
以上是我们应用在实际项目中的一个对于Mybatis处理枚举类的方案。我看大多数人也都是这样在用。