MyBatis之Jdbc处理器StatementHandler解析

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 {}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值