myBatis源码解析-类型转换篇

开始分析Type包前,说明下使用场景。数据构建语句使用PreparedStatement,需要输入的是jdbc类型,但我们一般写的是java类型。同理,数据库结果集返回的是jdbc类型,而我们需要java类型。这就涉及到一个类型转换问题,Type包就是解决这个问题。下面是Type包类图所在结构:

源码解析

  1. BaseTypeHandle - 类型处理器实现的基类

mybatis中的默认类型处理器,自定义类型处理器都继承自BaseTypeHandle。是分析类型处理器的关键,查看其类图如下:

分析BaseTypeHandle前,分析其接口TypeHandle。

复制代码
// 类型处理器接口,查询参数时将java类型转为jdbc类型。获取结果时将jdbc类型转问java类型。
public interface TypeHandler {
// 设置查询参数,java类型转为jdbc类型
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
// 获取结果参数,jdbc转为java类型
T getResult(ResultSet rs, String columnName) throws SQLException;

T getResult(ResultSet rs, int columnIndex) throws SQLException;

T getResult(CallableStatement cs, int columnIndex) throws SQLException;

}
复制代码
TypeHandler接口定义了参数转换的方法。查询时将java类型转为jdbc类型,获取结果时将jdbc类型转为java类型。

分析继承的抽象类,TypeReference,主要是获取泛型T的原生类型。

复制代码
public abstract class TypeReference {

private final Type rawType; // 保存所处理的java原生类型、个人理解即T泛型的类型。

protected TypeReference() {
rawType = getSuperclassTypeParameter(getClass());
}
// rawType的获取过程。任务类型处理器都需要继承BaseTypeHandle,而BaseTypeHandle继承TypeReference,此处为了获取T的java类型。
// 至于为什么使用这一变量,因为我们自定义类型处理器可以不指定java类型,只指定jdbc类型,这样java类型默认就是T类型。
Type getSuperclassTypeParameter(Class<?> clazz) {
Type genericSuperclass = clazz.getGenericSuperclass(); // 获取分类,包括T。此处和getSuperClass的区别是,getSuperClass只返回直接父类,并不包括父类带的泛型T
if (genericSuperclass instanceof Class) { // 任何类型处理器都有泛型T,一直循环找,如果没找到,直接报错。
// try to climb up the hierarchy until meet something useful
if (TypeReference.class != genericSuperclass) {
return getSuperclassTypeParameter(clazz.getSuperclass());
}
https://www.iqiyi.com/manhua/search-keyword=乐昌哪儿有捐卵地方?微信939761500
https://www.iqiyi.com/manhua/search-keyword=乐昌公司高薪直招捐卵女孩微信939761500
https://www.iqiyi.com/manhua/search-keyword=乐昌公司高薪直招捐卵志愿者微信939761500
https://www.iqiyi.com/manhua/search-keyword=乐昌招聘捐卵微信微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄招捐卵女孩微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄我要捐卵微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵中介微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄靠谱捐卵中介微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵机构微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄高补偿捐卵微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄哪里可以捐卵微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵服务微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵拿多少钱微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵子微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵的价格微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵营养费微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵中心微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄卵子库微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵补偿微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄有偿捐卵微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄爱心捐卵微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵联系方式微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵能拿多少钱微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵一般多少钱微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄大学生捐卵微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄欠网贷捐卵微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵要求微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄高薪招聘捐卵中心微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵志愿者微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄哪里捐卵补偿高微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵需要多长时间微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄怎么捐卵 微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄哪里有捐卵的 微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄我要捐卵 微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄爱心捐卵 微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄哪里有捐卵 微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄怎样捐卵 微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄个人捐卵 微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵多少钱 微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵群 微信939761500
throw new TypeException("'" + getClass() + "' extends TypeReference but misses the type parameter. "
+ "Remove the extension or add a type parameter to it.");
}

Type rawType = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0]; // 获取泛型T的java类型
// TODO remove this when Reflector is fixed to return Types
if (rawType instanceof ParameterizedType) {  // ?自己debug没有进入到这一步,保留。个人理解是万一还是类型嵌套模式如user<T>,还有泛型T。就再次获取T的java类型。
  rawType = ((ParameterizedType) rawType).getRawType();
}

return rawType;

}

public final Type getRawType() {
return rawType; // 返回解析的rawType
}
.....
}
复制代码
TypeReference类提供了获取rawType的方法。对于我们自定义类型转换器,可以只输入要转换的jdbc类型,那默认的待转换java类型就是rawType类型。

接下来分析BaseTypeHandle,也是一个抽象类,查看其源码,主要实现了对输入参数,结果参数为空的处理,若不为空,则交给子类具体实现。

复制代码
// 类型处理器基类,增加了输入参数为null时的处理
public abstract class BaseTypeHandler extends TypeReference implements TypeHandler {

protected Configuration configuration; // 全局配置参数

public void setConfiguration(Configuration c) {
this.configuration = c;
}

public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) { // 对输入参数为null时的处理
if (jdbcType == null) { // jdbcType不能为空
throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
}
try {
ps.setNull(i, jdbcType.TYPE_CODE); // 将指定位置参数设置为空
} catch (SQLException e) {
throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
"Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
"Cause: " + e, e);
}
} else {
setNonNullParameter(ps, i, parameter, jdbcType); // 输入参数不为空的处理
}
}
.......
// 对非空参数需要子类实现。
public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;

public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;

public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;
复制代码
BaseTypeHandler其实只对空数据进行处理,非空数据交给子类实现,此处使用了模板设计模式。查看具体的类型处理器,如DateTypeHandle进行验证。

复制代码
public class DateTypeHandler extends BaseTypeHandler { // 日期转换处理器,泛型T为java.util.date

@Override
public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType)
throws SQLException {
ps.setTimestamp(i, new Timestamp((parameter).getTime())); // 设置PreparedStatement,其实就是java类型转为数据库识别的jdbc类型
}
https://www.iqiyi.com/manhua/search-keyword=南雄有偿招募捐卵 微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵吧 微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵志愿者 微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵流程 微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵一次多少钱微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄哪里可以捐卵高价微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵可以拿多少钱微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄哪里可以捐卵微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵一般多少钱微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵价格微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵多少钱微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄哪里能捐卵微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄正规捐卵得多少钱微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵联系方式微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄有偿捐卵中心微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄有偿捐卵联系招聘微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄哪里有正规捐卵微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄招聘捐卵女孩微信微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄招聘捐卵多少钱微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵补偿价格表微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄的捐卵中介靠谱吗?微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄哪里有捐卵公司微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄哪里捐卵价格高微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄卖卵子联系方式微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄正规医院捐卵子多少钱微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄招聘捐卵微信群微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄招代妈捐卵女孩捐卵群微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄高薪招聘捐卵女孩微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄有偿捐卵女孩联系方式微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄正规捐卵微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄高薪招聘捐卵女孩志愿者微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄有招捐卵的吗?微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵对身体有影响吗?微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵补偿多少?捐卵可以拿多少钱?微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵有多少钱?哪里可以捐?微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄有哪些正规医院可以捐卵?微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄捐卵需要几天微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄哪里可以找到捐卵志愿者?微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄可以捐卵的医院在哪里微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄有哪些正规医院可以捐卵微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄哪里可以捐卵?微信939761500
https://www.iqiyi.com/manhua/search-keyword=南雄要去哪里捐呢?微信939761500
@Override
public Date getNullableResult(ResultSet rs, String columnName)
throws SQLException {
Timestamp sqlTimestamp = rs.getTimestamp(columnName); // 获取指定列的结果
if (sqlTimestamp != null) {
return new Date(sqlTimestamp.getTime()); // 获取Date结果,jdbc类型转为java类型。
}
return null;
}
复制代码
类型处理器实现也很简单,是将java数据类型和数据库识别的jdbc类型的相互转换。其余的类型处理器太多了,暂且不分析了,大家感兴趣的可以自己看下。

  1. TypeHandleRegistry - 类型处理器注册类

类型转换处理器定义完毕后,需要有个仓库进行注册,后续使用直接去拿即可。TypeHandleRegistry就是处理器注册的地方。现在对TypeHandleRegistry源码进行分析。

复制代码
private static final Map<Class, Class> reversePrimitiveMap = new HashMap<Class, Class>() {
private static final long serialVersionUID = 1L;
{
put(Byte.class, byte.class);
put(Short.class, short.class);
put(Integer.class, int.class);
put(Long.class, long.class);
put(Float.class, float.class);
put(Double.class, double.class);
put(Boolean.class, boolean.class);
put(Character.class, char.class);
}
};
// 数据库类型处理器map集合,jdbc类型为key,TypeHandle为value
private final Map<JdbcType, TypeHandler> JDBC_TYPE_HANDLER_MAP = new EnumMap<jdbctype, typehandler<?="">>(JdbcType.class);
// java类型处理器map集合,由此可知,一个java类型可以对应对个 jdbc类型
private final Map<Type, Map<JdbcType, TypeHandler>> TYPE_HANDLER_MAP = new HashMap<type, map<jdbctype,="" typehandler<?="">>>();
// 默认的未知类型处理器
private final TypeHandler

上一篇:python beautifulsoup 爬虫实战--抓取acm队员atcoder比赛数据


下一篇:零基础搭建SpringBoot框架