MyBatis Jdbc处理器StatementHandler解析
StatementHandler概要
MyBatis一个基于JDBC的Dao框架,但前面我们所学的会话、执行器半点没有提到jdbc,原因是MyBatis把所有跟JDBC相关的操作全部都放到了StatementHandler中。
一个SQL请求会经过会话,然后是执行器,最由StatementHandler执行jdbc最终到达数据库。其关系如下图:
这里要注意这三者之间比例是1:1:n。也就是说多个SQL操作对应一个会话,和唯一的执行器以及N个StatementHandler。这里的N取决于通过会话调用了多少次Sql(命中缓存除外)。
StatementHandler定义
JDBC处理器,基于JDBC构建JDBC Statement,并设置参数,然后执行Sql。每调用会话当中一次SQl,都会有与之相对应的且唯一的Statement实例。
StatementHandler结构
StatementHandler接口定义了JDBC操作的相关方法如下:
// 基于JDBC 声明Statement
Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;
// 为Statement 设置方法
void parameterize(Statement statement)
throws SQLException;
// 添加批处理(并非执行)
void batch(Statement statement)
throws SQLException;
// 执行update操作
int update(Statement statement)
throws SQLException;
// 执行query操作
<E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException;
StatementHandler 有三个子类SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler,分别对应JDBC中的Statement、PreparedStatement、CallableStatement。
大部分情况下都是预处理器,所以接下我们就针对PreparedStatementHandler来讲解其实现过程。
处理流程解析
总体流程
接下来了解statementHandler完整执行流程。如下时序图:
总共执行过程分为三个阶段:
-
预处理:这里预处理不仅仅是通过Connection创建Statement,还包括设置参数。
-
执行:包含执行SQL和处理结果映射两部分。
-
关闭:直接关闭Statement。
参数处理和结果集封装,涉及数据库字段和JavaBean之间的相互映射,相对复杂。所以分别使用ParameterHandler与ResultSetHandler两个专门的组件实现。接下来就一起了解一下参数处理与结果集封装的处理流程。
参数处理
参数处理即将Java Bean转换成数据类型。总共要经历过三个步骤,参数转换、参数映射、参数赋值。
参数转换
即将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、parm2...."
-
如果javac编译时设置了 -parameters 编译参数,也可以直接获取源码中的变量名称作为key
以上所有转换逻辑均在ParamNameResolver中实现。
参数映射
映射是指Map中的key如何与SQL中绑定的参数相对应。以下这几种情况
-
单个原始类型:直接映射,勿略SQL中引用名称
-
Map类型:基于Map key映射
-
Object:基于属性名称映射,支持嵌套对象属性访问
在Object类型中,支持通过“.”方式映射属中的属性。如:user.age
参数赋值
通过TypeHandler 为PrepareStatement设置值,通常情况下一般的数据类型MyBatis都有与之相对应的TypeHandler
结果集封装
指读取ResultSet数据,并将每一行转换成相对应的对象。用户可在转换的过程当中可以通过ResultContext来控制是否要继续转换。转换后的对象都会暂存在ResultHandler中最后统一封装成list返回给调用方
结果集转换中99%的逻辑DefaultResultSetHandler 中实现。整个流程可大致分为以下阶段:
-
读取结果集
-
遍历结果集当中的行
-
创建对象
-
填充属性