一、先认识一些关键接口和类
(1)Mybatis四大核心组件
- Executor:它作用于创建缓存,管理StatementHander的调用,为StatementHandler提供Configuration环境。
- StatementHander:它主要用于创建statement对象于数据库进行交互。
- ParamenterHandler:它主要用于在PreparedStatement对象的预编译sql的参数配置
- ResultSetHandler:它主要用于把查询结果和实体类进行绑定
1、Executor 接口(执行器用来执行sql语句)
主要包含四种实现类:
- SimpleExecutor :简单执行器,所有数据库操作委托给StatementHandler处理
- ReuseExecutor:复用执行器,与SimpleExecutor类似,只是根据sql缓存了JDBC的statement,遇到相同sql省去了statement的创建,需要手动执行flushStatement清除缓存。
- BatchExecutor:批量执行器,使用JDBC的batchUpdate方法,需要手动执行SqlSession的flushStatements立即执行sql。
- CachingExecutor:缓存执行器
2、StatementHandler接口(用于处理JDBC中的Statement接口)
- SimpleStatementHandler:对应JDBC中的Statement接口,用于简单sql的处理。
- PreparedStatementHandler:对应JDBC中的PreparedStatement预编译sql的接口。
- CallableStatementHandler:对应JDBC中的CallableStatement接口,用于执行存储过程相关的接口。
- RoutingStatemengtHandler:对以上三个接口的路由,只是负责创建和调用。
3、ParameterHandler接口(参数处理器,为PreparedStatement的sql语句参数动态赋值)
参数解释:
- getParameterObject: 用于读取参数
- setParameters: 用于对 PreparedStatement 的参数赋值
4、ResultSetHandler接口(用于结果集的处理,按照Mapper文件中的ResultType和ResultMap来封装成对应的对象并返回。)
只有DefaultResultSetHandler一个实现类
- handleResultSets:将statement执行后产生的结果集(多个结果集)映射为结果列表
- handleCursorResultSets:处理游标结果集
- handleOutputParameters :处理存储过程执行后的输出参数
(2)其他关键接口和类
1、Configuration类:是整个mybatis核心配置类,所有的xml等信息,最终都会解析到Configuration类
2、MappedStatement类:用于存mapper文件中的mapper标签信息。
3、ResultMap类:用于存放mapper文件中resultMap标签信息
4、ResultMapping类:存放mapper文件中resultMap标签中result信息,用作java和mysql的列映射类
5、boundSql类:用于处理动态sql后,返回sql字符串
6、ResultSetWrapper类:用于对ResultSet结果集的包装类
7、ResultHandler接口:用于自定义处理返回结果,使用sqlSessionTemplate的select方法实现,用于流式查询,防止大数据导致oom。
8、ResultContext接口:唯一实现类是DefaultResultContext。是ResultHandler接口的参数,每个ResultContext代表一条数据,可以通过getResultObject获取到数据。
9、TypeHandler接口:类型处理器。用于处理javaType和JdbcType之间的转换,还用于PreparedStatement设置参数值和从ResultSet或CallableStatement中取出值。
10、RowBounds类:用于mybatis逻辑分页,原理是取出所有数据,舍弃前面offset条数据,取剩下数据的limit条。性能不好。推荐使用PageHelper分页插件,底层是通过拦截器添加limit语句。
二、SqlSession执行的过程
无论我们使用Mapper的形式执行sql,还是sqlSessionTemplate的方式,最终都会执行到DefaultSqlSession的selectList方法(列表查询)
前面请参考:https://blog.csdn.net/sumengnan/article/details/113953507,这里就直接从selectList方法开始了。
1、DefaultSqlSession的selectList方法
从configuration中获取MappedStatement信息(可以获取到,因为项目启动时,所有mapper.xml文件中的内容都已经解析到configuration中了)
executor的类型是根据项目启动时的配置生成的,默认是Simple,如果开启缓存则是cachingExecutor
2、query方法是SimpleExecutor的抽象类BaseExecutor的方法用于查询
如果缓存中存在,则走handleLocallyCachedOutputParameters,否则走queryFromDatabase从数据库去查询数据。
3、BaseExecutor的queryFromDatabase方法用于查询
主要用于缓存些数据,之后调用doQuery方法
4、这个doQuery方法,才是真正让子类去实现的方法
4.1构建statementHandler
可以看到使用了RoutingStatementHandler算是静态代理类吧
之后又加入插件拦截器,用于拦截之后自定义处理,例如:PageHepler分页插件
4.2、开始准备Statement对象
从transaction中获取Connection数据库连接(这不就是JDBC的第一步吗?先获取数据库连接)
初始化statement:从connection中创建Statement对象(这不就是JDBC的第二步吗?创建Statement对象)
如果是Executor是PreparedExecutor的话,会获取prepareStatement对象,并且会执行handler.parameterize(stmt)方法。
去设置参数,如果是String类型的将执行StringTypeHandler类使用ps.setString(i, parameter);设置参数值(这不就是JDBC的第三步吗?预编译SQL参数赋值)
5、回到第4步的doQuery方法,继续执行handler.query方法
执行了statement.execute(sql);去执行sql查询。(这不就是JDBC的第四步吗?执行execute方法执行sql查询)
6、继续进入handleResultSets方法
结果集ResultSet可能有多个
6.1、进入getFirstResultSet获取结果集(这不就是JDBC的第五步吗?获取ResultSet结果集)
然后把结果包装进了ResultSetWrapper类中
7、进入HandleResultSet方法去处理结果集
如果你没有自定义ResultHandler处理结果集类,那么默认使用DefaultResultHandler去处理结果集
8、进入HandlerRowValues方法,处理每一行数据
handleRowValuesForSimpleResultMap为简单结果映射
handleRowValuesForNestedResultMap为嵌套结果映射(多表查询)。
9、进入简单结果映射
10、把处理的结果放在DefaultResultHandler的List集合中,返回