在Mybatis框架中,除了执行器的逻辑,比较重要的就是同JDBC打交道的StatementHandler,它封装了JDBC的连接、查询、返回值封装等。
结构
StatementHandler的基本结构同执行器中的基本机构类似,都是一个接口,一个Base抽象类,多个继承类。
包结构如下:
类图如下:
在StatementHandler中定义了基础方法,如下:
根据三个子类的命名,可以知道,每个子类都是提供了不同的statement,来操作JDBC。
在基类BaseStatementHandler中,提供了一个抽象方法来初始化子类自己的statement。
例如PreparedStatementHandler中:
使用 preparedStatement对数据库的SQL进行操作。
执行流程
下面以preparedStatement为例,来讲解执行流程,流程如下:
首先经过BaseStatementHandler,这里就是为了处理共性的逻辑。真正实行的其实是设置的BaseStatementHandler的子类,通过参数的Handler处理参数,最后通过结果的Handler处理结果。
下面分步骤来查看执行过程
1、StatementHandler初始化和获取
在执行器的doQuery中可以看到,如果获取StatementHandler的
之后通过RoutingStatementHandler判断初始化哪个处理器,
然后是根据初始化的StatmentHandler获取Statement,开始调用的就是BaseStatementHandler的
prepare()方法,里面就是instantiateStatement()方法,由各子类实现。
之后就是 设置参数,可以看到设置参数一样是在prepareStatement()方法中,调用的就是StatementHandler中的设置参数方法,而在StatementHandler中调用的是parameterHandler(DefaultParameterHandler)的。
最后就是执行,看doQuery中其实是调用StatementHandler中的query
StatementHandler中的query直接执行完毕后,调用ResultSetHandler中的参数处理方法。
这样整个mybatis执行流程就结束了。其实复杂的就是参数封装转换和结果集的转换。
参数封装
参数封装分为3个步骤,第一个步骤是参数转换,第二个步骤是参数映射,第三个步骤是参数赋值。
1、参数转换:mybatis中参数转换分为两种,第一种是单个参数并且没有@Param注解,不做处理直接透传,第二种是转换成Map,并且可以发现,Map中有两套参数,第一套参数是arg/@Param定义的名字为key,第二套是param为key
debug可以看到
2、参数映射:根据参数不同,也是分为三种情况第一种单个参数,直接映射即可,同时忽略了参数名字,第二种基于Map的Key Value,第三种基于对象的映射,可以使用 user.age的语法获取对象中属性的值。
程序首先会根据Sql中的#{}\${}解析需要的参数
然后根据参数进行映射
最后采用TypeHandler进行参数的设置。
结果转换
ResultSetHandler -> ResultContext -> ResultHandler
1、ResultSetHandler 主要用于结果接获取,最复杂
2、ResultContext 用于结果集判断,例如分页、跳过某几行
3、ResultHandler 使用List封装最后的结果
下面查看 ResultSetHandler方法,只看处理一个结果集的流程,处理的结果接最后交给ResultHandler(实现类为 DefaultResultHandler)中的list:
下面是处理结果行 handleRowValues,也区分是否存在子查询和简单结果集处理,下面看简单结果集:
在handleRowValuesForSimpleResultMap方法中可以看到,直接遍历ResultSet,如果ResultContext没有停止或分页,rowValue 就是我们要的对象
跳过制定的行,第一种是直接跳到行,第二种是便利到行:
通过 getRowValue 方法把行数据转换成最后的对象:
创建一个空对象,createResultObject,分为几种情况:
① 原始类型
② 制定构造方法
③ 自动映射,根据返回行的类型进行映射
创建完对象之后就是赋值了,赋值分为2种,在getRowValue()可以看到 为自动映射(applyAutomaticMappings)、手动映射(applyPropertyMappings)
其实结果集映射分为也分为是否存在嵌套查询的情况。