目录
一、类型处理器
1、typeHandler类型处理器具体用处:
2、typeHandler结构:
3、默认的类型处理器
二、自定义类型处理器处理枚举
1、MyBatis提供的两个枚举处理器
2、定义枚举类型处理器
MyBatis在与数据库交互的时候,Executor对象会调用ParameterHandler对象进行设置参数和ResultSetHandler对象处理数据库查询返回的数据。而这两个主要的操作都需要经过typeHandler来处理:
//ParameterHandler
typeHandler.setParameter(ps, i + 1, value, jdbcType);
//ResultSetHandler
typeHandler.getResult(rs, column);
类型处理器一般为实现org.apache.ibatis.type.TypeHandler 接口,或继承 org.apache.ibatis.type.BaseTypeHandler抽象类。
(1)org.apache.ibatis.type.TypeHandler 接口
public interface TypeHandler {
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
接口中有四个方法,只有两个功能,设置参数setParameter和获取结果getResult。getResult有三种不同的获取方式,根据列名称获取、根据列的索引获取、从存储过程中获取。
(2) org.apache.ibatis.type.BaseTypeHandler抽象类
public abstract class BaseTypeHandler extends TypeReference implements TypeHandler {
//...
}
BaseTypeHandler同样实现了TypeHandler 接口,重写了接口中的方法,并且为我们实现了其中的逻辑。其中多出来的四个方法:setNonNullParameter、getNullableResult为精细化的处理器提供进一步逻辑处理接口。
MyBatis框架为我们提供的BaseTypeHandler完成了大部分的功能,因此不同类型处理器只需要继承这个BaseTypeHandler就可以了。
例如下方的处理String的StringTypeHandler:
(3)示例:StringTypeHandler
StringTypeHandler继承了BaseTypeHandler,只要重写BaseTypeHandler种额外提供的抽象方法就可以。底层依然是jdbc的基本操作。
public class StringTypeHandler extends BaseTypeHandler {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
throws SQLException {
ps.setString(i, parameter);
}
@Override
public String getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return rs.getString(columnName);
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return rs.getString(columnIndex);
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return cs.getString(columnIndex);
}
}
类型处理器 | Java 类型 | JDBC 类型 |
---|---|---|
BooleanTypeHandler | java.lang.Boolean, boolean | 数据库兼容的 BOOLEAN |
ByteTypeHandler | java.lang.Byte, byte | 数据库兼容的 NUMERIC 或 BYTE |
ShortTypeHandler | java.lang.Short, short | 数据库兼容的 NUMERIC 或 SHORT INTEGER |
IntegerTypeHandler | java.lang.Integer, int | 数据库兼容的 NUMERIC 或 INTEGER |
LongTypeHandler | java.lang.Long, long | 数据库兼容的 NUMERIC 或 LONG INTEGER |
FloatTypeHandler | java.lang.Float, float | 数据库兼容的 NUMERIC 或 FLOAT |
DoubleTypeHandler | java.lang.Double, double | 数据库兼容的 NUMERIC 或 DOUBLE |
BigDecimalTypeHandler | java.math.BigDecimal | 数据库兼容的 NUMERIC 或 DECIMAL |
StringTypeHandler | java.lang.String | CHAR, VARCHAR |
ClobReaderTypeHandler | java.io.Reader | - |
ClobTypeHandler | java.lang.String | CLOB, LONGVARCHAR |
NStringTypeHandler | java.lang.String | NVARCHAR, NCHAR |
NClobTypeHandler | java.lang.String | NCLOB |
BlobInputStreamTypeHandler | java.io.InputStream | - |
ByteArrayTypeHandler | byte[] | 数据库兼容的字节流类型 |
BlobTypeHandler | byte[] | BLOB, LONGVARBINARY |
DateTypeHandler | java.util.Date | TIMESTAMP |
DateOnlyTypeHandler | java.util.Date | DATE |
TimeOnlyTypeHandler | java.util.Date | TIME |
SqlTimestampTypeHandler | java.sql.Timestamp | TIMESTAMP |
SqlDateTypeHandler | java.sql.Date | DATE |
SqlTimeTypeHandler | java.sql.Time | TIME |
ObjectTypeHandler | Any | OTHER 或未指定类型 |
EnumTypeHandler | Enumeration Type | VARCHAR-任何兼容的字符串类型,存储枚举的名称(而不是索引) |
EnumOrdinalTypeHandler | Enumeration Type | 任何兼容的 NUMERIC 或 DOUBLE 类型,存储枚举的索引(而不是名称)。 |
InstantTypeHandler | java.time.Instant | TIMESTAMP |
LocalDateTimeTypeHandler | java.time.LocalDateTime | TIMESTAMP |
LocalDateTypeHandler | java.time.LocalDate | DATE |
LocalTimeTypeHandler | java.time.LocalTime | TIME |
OffsetDateTimeTypeHandler | java.time.OffsetDateTime | TIMESTAMP |
OffsetTimeTypeHandler | java.time.OffsetTime | TIME |
ZonedDateTimeTypeHandler | java.time.ZonedDateTime | TIMESTAMP |
YearTypeHandler | java.time.Year | INTEGER |
MonthTypeHandler | java.time.Month | INTEGER |
YearMonthTypeHandler | java.time.YearMonth | VARCHAR or LONGVARCHAR |
JapaneseDateTypeHandler | java.time.chrono.JapaneseDate | DATE |
枚举的知识复习参考:https://blog.csdn.net/shaohe18362202126/article/details/86766999
EnumTypeHandler与EnumOrdinalTypeHandler
EnumTypeHandler与EnumOrdinalTypeHandler同样继承了BaseTypeHandler,具体的逻辑在BaseTypeHandler提供的四个方法中。
(1)EnumTypeHandler
EnumTypeHandler接收枚举类型,但是存储在数据库中是枚举的名称即name属性,从数据库中获取值后根据name获取枚举的对象。
setNonNullParameter方法:parameter.name()获取枚举参数的name
@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
if (jdbcType == null) {
ps.setString(i, parameter.name());
} else {
ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE); // see r3589
}
}
getNullableResult方法:Enum.valueOf(type, s)根据枚举类型和枚举名称获取枚举对象,type为枚举的class。
private Class type;
@Override
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
String s = rs.getString(columnName);
return s == null ? null : Enum.valueOf(type, s);
}
(2)EnumOrdinalTypeHandler
EnumOrdinalTypeHandler是将枚举对象转换为枚举对象的索引值,接收结果时候同样根据索引值转换成枚举对象。
setNonNullParameter方法:parameter.ordinal()获取枚举参数的ordinal
@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
ps.setInt(i, parameter.ordinal());
}
getNullableResult方法:根据索引从枚举数组enums中获取枚举对象。
private final E[] enums;
@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);
}
}
}
(3)两者切换
示例:
增加employee的枚举对象:
public enum StatusEnum{
LOGIN, LOGOUT, UMKOWN;
}
employee对象中包含此属性:
public class Employee implements Serializable {
//其他属性...
private StatusEnum status;
public StatusEnum getStatus() {
return status;
}
public void setStatus(StatusEnum status) {
this.status = status;
}
}
首先,MyBatis默认对枚举的处理器为EnumTypeHandler,数据库中的枚举保存为其名称name的值。:
从EnumTypeHandler到EnumOrdinalTypeHandler的切换:
方法一:全局配置
在mybatis全局配置文件中增加配置:
javaType属性表示该类型处理器只在com.starfall.mybaits.typehandler.StatusEnum时候生效,对于其他的枚举还是使用默认的EnumTypeHandler。
或者在类型处理器的类上(TypeHandler class)增加一个 @MappedTypes 注解来指定与其关联的 Java 类型列表
方法二:精细化配置
在具体的SQL中的具体的字段上配置需要调用的typeHandler
insert和update语句中:
INSERT INTO employee (first_name, last_name, email, status)
VALUES
(
#{firstName,jdbcType=VARCHAR},
#{lastName,jdbcType=VARCHAR},
#{email,jdbcType=VARCHAR},
#{status,typeHandler=org.apache.ibatis.type.EnumOrdinalTypeHandler}
)
select中:在对应的resultMap中配置typeHandler
升级枚举StatusEnum的功能:
增加两个属性code和msg。
public enum StatusEnum {
LOGIN(100, "登录"), LOGOUT(200, "登出"), UMKOWN(300, "未知");
// 自定义的属性code
private final Integer code;
// 自定义的属性msg
private final String msg;
// 自定义的构造函数,传入属性
private StatusEnum(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
/**
* 根据code获取枚举对象
*
* @param code
* @return
*/
public static StatusEnum getEnumByCode(Integer code) {
StatusEnum status = null;
Class> clasz = StatusEnum.class;
if (clasz.isEnum()) {
// 返回该枚举类型的所有元素,如果Class对象不是枚举类型,则返回null。
StatusEnum[] values = (StatusEnum[]) clasz.getEnumConstants();
for (StatusEnum statusEnum : values) {
if (statusEnum.getCode() == code) {
status = statusEnum;
}
}
}
return status;
}
}
需求:数据库中保存StatusEnum的code,而不是枚举的name和ordinal了
(1)创建上述的枚举对象,并提供根据code获取枚举对象的方法。
(2)自定义类型处理器,继承BaseTypeHandler类。
实现四个方法中的逻辑,设置参数为枚举的code、根据code获取枚举对象。
public class StatusEnumTypeHandler extends BaseTypeHandler {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, StatusEnum parameter, JdbcType jdbcType)
throws SQLException {
ps.setString(i, parameter.getCode().toString());
}
@Override
public StatusEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {
int code = rs.getInt(columnName);
return StatusEnum.getEnumByCode(code);
}
@Override
public StatusEnum getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
int code = rs.getInt(columnIndex);
return StatusEnum.getEnumByCode(code);
}
@Override
public StatusEnum getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
int code = cs.getInt(columnIndex);
return StatusEnum.getEnumByCode(code);
}
}
(3)配置自定义的类型处理器。可以参考上方的全局配置和具体到字段的配置:
INSERT INTO employee (first_name, last_name, email, status)
VALUES
(
#{firstName,jdbcType=VARCHAR},
#{lastName,jdbcType=VARCHAR},
#{email,jdbcType=VARCHAR},
#{status,typeHandler=com.starfall.mybaits.typehandler.StatusEnumTypeHandler}
)
(5)测试:
新增的数据:
查询的数据: