我们在 MyBatis核心组件Executor(一) 中介绍了 Executor 执行的主要流程,介绍了在查询数据库之前进行的一些处理,如操作数据库之前我们会进行查询缓存等操作。
还介绍了Executor的几个实现类,如每次访问都要创建新的PrepareStatement对象的SimpleExecutor,访问数据库会重用缓存中的statement对象的ReuseExecutor,以及现批量执行多条SQL语句的能力BatchExecutor。
这里我们再详细介绍一下我们连接数据库进行的操作,我们首先看一下我们直接使用JDBC方式连接操作数据库的过程。
我们JDBC其实主要分为几步,就是首先获取连接Connection,然后得到Statement,接着就是执行SQL语句得到其结果集,并进行与POJO类的映射。
其实我们MyBatis也是大致分为这几部,并且在Executor中有专门的类分别进行处理。
StatementHandler: 它的作用是使用数据库的Statement或PrepareStatement执行操作,启承上启下作用;ParameterHandler: 对预编译的SQL语句进行参数设置, SQL语句中的的占位符 ? 都对应BoundSql.parameterMappings 集合中的一个元素,在该对象中记录了对应的参数名称以及该参数的相关属性ResultSetHandler: 对数据库返回的结果集( ResultSet)进行封装,返回用户指定的实体类型现在我们首先来了解一些我们的StatementHandler类,StatementHandler完成Mybatis最核心的工作,也是Executor实现的基础;功能包括:
创建statement对象为sql语句绑定参数执行增删改查等SQL语句、将结果映射集进行转化。 BaseStatementHandler: 所有子类的抽象父类,定义了初始化 Statement 的操作顺序,由子类实现具体的实例化不同的 Statement(模板模式)RoutingStatementHandler: Excutor 组件真正实例化的子类,使用静态代理模式,根据上下文决定创建哪个具体实体类SimpleStatmentHandler: 使用 Statement 对象访问数据库,无须参数化PreparedStatmentHandler: 使用预编译 PrepareStatement 对象访问数据库CallableStatmentHandler: 调用存储过程这里我们就以上述的核心 BaseStatementHandler 开始介绍,其实在上次我们就已经讲过了该BaseStatementHandler的了,如下:
我们从我们上次说过的截图开始说起,这里我们就看看它是如何决定实例化 BaseStatementHandler 的哪一个实现类的
发现它是由 RoutingStatementHandler 获取 StatementHandler 的
这里它是会根据我们的设置进行实例化对于的Statement的,这里默认的是PREPARED,也就是我们会进行预编译的 如果我们想使用不进行预编译的也是可以设置的,如下:
上述我们讲述了通过 StatementHandler 获取到我们的 Statement,那么在获取到了Statement之后呢,我们就需要通过 ParameterHandler 对预编译中的参数进行设置,如占位符等
如果是这一种 SimpleStatementHandler 类,那么它就对应了我们在JDBC中的 Statement 的形式,就是不会进行预编译的形式,那么自然就无需进行参数设置了
如果是这一种 PreParedStatementHandler 类,那么它就对应了我们在JDBC中的 PreParedStatement 的形式,就是会进行预编译的模式,那么自然就要进行参数设置
这里我们会先获取我们Configuration中的我们之前解析的Mapper中SQL语句的参数集合,遍历给其参数进行赋值
如我们经常使用的,为String类型的参数进行设值,是不是发现我们在JDBC中写的代码一模一样
在上述我们通过StatementHandler获取到了Statement,以及通过ParameterHandler对SQL语句的参数进行了处理,现在我们的SQL语句就可以直接执行了。
这里的SimpleStatementHandler和PrepareStatementHandler两个类有一定的区别,但是其实都是一样的,我们了解JDBC中的写法这里就很清楚了
执行完后我们就可以得到一个结果集,然后我们这里就需要通过ResultSetHandler对我们的结果集进行处理,将其赋值到我们的POJO类中去。
这里为我们取我们的 MyBatis初始化阶段 中获取到的Configuration中的MappedStatement,在里面获取到我们解析的ResultMap,即我们的映射关系
这里我们会对我们结果集中的数据进行处理
这里还会考虑处理一些嵌套的ResultMap,这里我们就看个简单的ResultMap
这里我们会进行一些分页的处理,我们MyBatis的分页其实是逻辑分页,就是在数据中找到所需要的部分返回出来,而不是只查询加载所需的部分,所以我们一般不使用其自带的分页,而是使用第三方的分页插件。
然后就是和我们JDBC中有点类似了,就是通过next()一行行解析映射我们的结果集数据
这里首先会根据我们设置的Type类型,去通过反射实例化我们需要映射的实体类对象,然后就是创建我们的MetaObject用于对实体类的赋值,这里我们在 MyBatis反射模块分析 介绍过
然后就是对我们配置的映射关系进行映射,在映射时还会判断我们是不是开启的启动映射,因为我们在ResultMap绑定映射关系时是可以开始自动映射的