昨天的我是个小木匠
图文不符系列😛
工作中经常用到MyBatis,刚开始学习的时候,只会使用,却不知道底层原理,最近看了《MyBatis技术内幕》还有Debug跟踪了一下源码,去学习它的底层思想。
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
图化两个流程
在Github中将源码拉下来,Debug了binding包下的BindingTest类,简单分析整个流程:
一、数据初始化阶段
二、查询阶段
源码分析
代码位置:org.apache.ibatis.binding.BindingTest
初始化
|
|
Configuration配置参数
讲下配置巨多的Configuration: org.apache.ibatis.session.Configuration
mybatis的所有配置信息都存储到Configuration类中,在初始化的时候进行加载
简单介绍它的属性对应的含义:
- Environment:配置了环境ID(决定加载哪种环境-生产OR开发)、事务工厂和数据源
- Settings: 主要是运行时的全局设置
- Registrys: 配置了很多注册器:
①:mapperRegistry,映射注册器,主要用来加载自定义mapper;
②:typeHandlerRegistry,类型处理器,主要用于保存JDBC Type,JAVA Type与处理器的映射关系,各个Handler主要继承BaseTypeHandler,同时用户可以继承自定义实现;
③:typeAliasRegistry,类型别名注册器,但是typeAliasRegister构造方法里面已经有很多基础包装类型,在构建configuration中为何还要加载??
④:LanguageDriverRegistry,脚本语言注册器,在configuration初始化时,设定XMLLanguageDriver为默认驱动,并添加RawLanguageDriver驱动
创建SqlSessionFactory
在初始化的时候,创建SqlSessionFactory调用的方法是
org.apache.ibatis.session.SqlSessionFactoryBuilder.build(Configuration config)
到此,创建完sqlSessionFactory之后,初始化阶段就完成了。
查询阶段
简单查询🌰:
打开sqlSession会话连接
刚才创建的是DefaultSqlSessionFactory,最终打开会话会调用org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource
到此,获取sqlSession连接就结束了
通过mapper接口进行查询
mapperProxy:映射器代理,代理模式
从刚才取出的sqlSession获得mapper资源,接着调用mapper的接口方法,会进入mapperProxy代理方法
org.apache.ibatis.binding.MapperProxy#invoke(有种AOP的切面思想,与Spring类似??)
mapperMethod:映射器方法
经过代理之后,进入org.apache.ibatis.binding.MapperMethod#execute执行
接着进入DefaultSqlSession进行查询
org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne(java.lang.String, java.lang.Object)
使用执行器进行查询
org.apache.ibatis.executor.BaseExecutor#query(MappedStatement, Object, RowBounds, ResultHandler, CacheKey, BoundSql)
查询到Object结果后,进入结果抽取器进行解析
org.apache.ibatis.executor.ResultExtractor#extractObjectFromList
返回结果Object后,经过PropertyTokenizer(属性分词器)、BeanWrapper(Bean包装器)进行metaResultObject结果设置:
org.apache.ibatis.reflection.wrapper.BeanWrapper#set
调用到这个地方,其实已经从blog表中查询到一条blog记录,然后由于mapper文件设定的sql语句是:
官网解释节点含义:
- constructor :用于在实例化类时,注入结果到构造方法中
- idArg :ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
- arg : 将被注入到构造方法的一个普通结果
- id : 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
- result : 注入到字段或 JavaBean 属性的普通结果
- association : 一个复杂类型的关联;许多结果将包装成这种类型
嵌套结果映射 : 关联可以指定为一个 resultMap 元素,或者引用一个- collection : 一个复杂类型的集合
嵌套结果映射 : 集合可以指定为一个 resultMap 元素,或者引用一个- discriminator : 使用结果值来决定使用哪个 resultMap
- case : 基于某些值的结果映射
嵌套结果映射 : 一个 case 也是一个映射它本身的结果,因此可以包含很多相 同的元素,或者它可以参照一个外部的 resultMap。
可以看出,这条查询语句涉及到三个表,所以第一次查询到blog表中的一条记录后,循环调用查询流程,进行一个author查询和posts集合查询
将所有查询结果汇总:
org.apache.ibatis.executor.loader.ResultLoaderMap#loadAll
最后通过Javassist进行延迟加载:
org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory.EnhancedResultObjectProxyImpl#invoke
好吧,这个太底层了,这个我没看懂,有了解的小伙伴请跟我说说~~😶😶
结果返回之后,就要进行会话连接中断之类的就不用说了。好了,整个调用链路都清晰了,只能说,发明这个持久化框架的Clinton Begin大神🐂🍺!
挖坑
先挖个坑,下一篇要学MyBatis的分页组件(虽然GitHub已经有,原则是不要重复造轮子,但是只学习了调用原理,还是多去深入学习一下吧~)
参考资料:
①:Mybatis源码分析(一)- Configuration配置文件详解
②:mybatis源码中文注释
③:《MyBatis技术内幕》–徐郡明