1、StatementHandler概述
MyBatis是一个基于Jdbc的Dao层框架,MyBatis将所有与JDBC相关的操作全部都封装到了StatementHandler中。StatementHandler是真正访问数据库的地方,是MyBatis的数据库处理对象,用于执行SQL语句。
一个SQL请求会经过会话,然后是执行器,最后由StatementHandler执行jdbc最终到达数据库。其关系图如下:

要注意这里三者之间的比例是1:1:n;这里的N取决于通过会话调用了多少次sql,命中缓存除外(StatementHandler是真正与数据库交互的地方,只有执行数据库查询操作的才会创建StatementHandler对象)
2、StatementHandler定义
JDBC处理器,基于JDBC构建JDBC Statement,并设置参数,然后执行sql。每调用会话执行一次SQL,都会有与之相对应的且唯一的Statement实例。
3、StatementHandler结构
StatementHandler接口定义了JDBC相关操作的方法如下:

StatementHandler有三个子类SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler分别对应jdbc的Statement、PreparedStatement、CallableStatement

大部分情况下都是预处理器,所以接下来我们就针对PreparedStatementHandler来讲解其实现过程。
处理流程解析时序图:

总共执行过程分为三个阶段:
- 预处理:这里预处理不仅仅是通过Connection创建Statement,还包括设置参数。
- 执行:包括执行SQL和处理结果映射两部分
- 关闭:直接关闭Statement
参数处理和结果集封装,涉及数据库字段和JavaBean之间的相互映射,相对复杂。所以分别使用ParameterHandler与ResultSetHandler两个专门的组件实现。
4、参数处理
参数处理即将Java Bean转换成数据类型(JDBC参数)。总共要经历过三个步骤,参数转换、参数映射、参数赋值。

1)参数转换
即将JAVA方法中的普通参数,封装转换成Map,以便map中的key和sql参数引用相对应。
@Select({"select * from users where name=#{name} or age=#{user.age}"})
@Options
User selectByNameOrAge(@Param("name") String name, @Param("user") User user);
- 单个参数的情况下且没有设置@Param注解会直接转换,忽略SQL中的引用名称。
- 多个参数情况:优先采用@Param中设置的名称,如果没有则用参数序号代替即“param1、param2…”
- 如果javac编译时设置了 -parameters编译参数,也可以直接获取源码中的变量名称作为key
- 以上所有转换逻辑均在ParamNameResolver中实现。
org.apache.ibatis.reflection.ParamNameResolver.getNamedParams(Object[] args)
2)参数映射
映射是指Map中的key如何与SQL中绑定的参数相对应。有以下这几种情况
- 单个原始类型:直接映射,忽略SQL中引用名称。
- Map类型:基于Map key映射。
- Object:基于属性名称映射,支持嵌套对象属性访问。
- 在Object类型中,支持通过“.”方式映射对象中的属性。如:user.age
org.apache.ibatis.scripting.defaults.DefaultParameterHandler.setParameters(PreparedStatement ps)
3)参数赋值
通过TypeHandler为PrepareStatement设置值,通常情况下一般的数据类型MyBatis都有与之相对应的TypeHandler
//根据匹配原始类型参数的TypeHandler接口具体实现类进行参数的赋值
org.apache.ibatis.scripting.defaults.DefaultParameterHandler.setParameters(PreparedStatement ps){
...
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = this.configuration.getJdbcTypeForNull();
}
try {
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (SQLException | TypeException var10) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var10, var10);
}
}
5、结果集处理
结果集封装是指读取ResultSet数据,并将每一行转换成相对应的对象。用户可在转换的过程当中通过ResultContext来控制是否要继续转换。转换后的对象都会暂存在ResultHandler中最后统一封装成list返回给调用方。

ResultSetHandler结果集处理器:此时还是数据行不是Java对象。
ResultHandler结果处理器:此时已经变成Java对象。
结果集转换中99%的逻辑都是在DefaultResultSetHandler中实现。整个流程可大致分为以下阶段:
1)读取结果集
2)遍历结果集当中的行
3)创建对象
4)填充属性
整个过程有点繁长,我们用源码地图可以很好的去追踪其中的流程:
点击链接

public class PreparedStatementHandler extends BaseStatementHandler {
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement)statement;
ps.execute();//执行查询
return this.resultSetHandler.handleResultSets(ps);//获取结果集
}
}
//结果集的处理都在该类中
org.apache.ibatis.executor.resultset.DefaultResultSetHandler
public class DefaultResultSetHandler implements ResultSetHandler {
//处理多个结果集(存储过程)
public List<Object> handleResultSets(Statement stmt) throws SQLException {}
//处理单个结果集
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {}
//处理结果行
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
if (resultMap.hasNestedResultMaps()) {
this.ensureNoRowBounds();
this.checkResultHandler();
//遍历复杂(嵌套)结果映射
this.handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
//遍历简单结果行映射
this.handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
//封装行对象
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {}
//创建结果对象(先创建一个空对象)
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {}
//填充属性-手动映射
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {}
//填充属性-自动映射
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {}
}
299

被折叠的 条评论
为什么被折叠?



