本文关于mybatis的源码解读主要基于mybatis运行主流程。例如:config文件解析、xml解析生成sql语句以及sql语句的具体执行过程,不包含例如缓存、插件等源码。(有机会单独写。。。。)
<!-- 华丽的分割线 -->
解析所有配置文件,放到configure里头,生成SqlSessionFactry
没有特别需要介绍的,主要就是产生SqlSession。
用于执行sql语句的对象,其实真正执行sql的对象是SqlSession里的Executor对象。可以看到起内部doQuery,doUpdate等方法利用StatementHandler执行sql语句。
Configure —— 配置文件的解析
SqlSessionFactoryBuilder会构建一个XMLConfigBuilder对象,他是BaseBuilder的子类,该对象会解析所有与Mybaties有关的xml文件,其中parse()方法的字方法parseConfiguratio()会解析所有xml文件,并填充到configuration对象里。(注意:该对象在BaseBuilder也就是XMLConfigBuilder的父类里)需要注意的一点是,该方法在每次启动项目后只会执行一次,XMLConfigBuilder的bollean属性parsed确保了这一点。另外,产生的configuration对象应该是全局唯一(单例),且无处不在的。因为其不光保存了mybatis-config.xml 里的基础配置,还保存了所有xxxMapper.xml文件里的信息。
至此,所有xml文件解析完毕,configure里的内容填充好了
Configure重点对象
其中比较重点的对象:resultMaps , parameterMaps,mappedStatements
存储的是所有mapper.xml文件里的resultMap定意的内容,其中key是mapper.xml全路径名 + resultMap的id;value是ResultMap,里面存着result对应pojo属性列表,数据库表里对应的列名列表,以及属性和列的映射关系——ResultMapping。ResultMapping里有更详细的内容,比如,属性java类型是啥,column的数据库类型是撒等等。
存储的是传入的参数映射。例如传入的查询条件对象等等就会存储到这里。又比如,新增的时候传入的pojo对象就会被分解为 属性—值得映射存在这里。
保存的是MappedStatement,MappedStatement保存的是一个映射节点(select|update|insert),包含需要我们配置的sql(存在sqlSource里)、sql的id、ResultMap、parameterType等。MappedStatement里面还有一个重要的对象,SqlSource,他提供了BoundSql用于根据我们mapper的配置生成sql语句,因此其内部就会有两个重要的内容:1、基础语句,比如:select from where啥的,这部分是根据mapper配置文件产生的。2、生成的语句,是根据传入的POJO或parameterMap生成的。两者拼接就形成了需要的map语句。
注意:每一个映射节点,select|update|insert等等,都对应着一个MappedStatement存储在configure的mappedStatements里面。每一个MappedStatement都对应着一个SqlSource生成一个BoundSql用于生成sql语句。
SqlSource —— sql语句的解析
他是一个接口,实现它的类有四个,比较重要的是DynamicSqlSource,StaticSqlSource。
StaticSqlSource解析的是mapper里一些写死的sql。例如:
DynamicSqlSource也就是动态SqlSource,是基于StaticSqlSource实现的。他是干什么的呢,用于生成动态sql的。比如,我们update里有很多<if></if>,<forEach></forEach>等等语句,这样就需要根据情况动态生成sql,就需要利用DynamicSqlSource来生成sql。请看他的 getBoundSql 方法:
其中 DynamicContext 里面存储着上下文数据。内部类 DynamicContext.ContextMap 存储着已经绑定的sql节点,例如if,forEach啥的。请注意:
那么sqlNode都有神马?有很多,如图
这个命名比较直观,可以看到,这些对应着mapper文件里的节点<if></if> <trim></trim> <foreach collection=""></foreach>等等,这些类就对应着解析不同的标签。这些类里都有一个关键方法:apply(DynamicContext var1)。他是干啥的呢?他是真正生成sql语句的,不断地递归调用apply方法最后生成sql语句。我们看一个最简单的节点ifSqlNode
再看另一个实现类:TextSqlNode
类似的类还有一些,可以看到当递归调用到这些类的apply方法的时候,递归就结束了。这是个什么思路呢?请看:
所以上图里的生成sql语句的过程就比较清楚了,分别是:
1、进入MixedSqlNode(一般就是rootSqlNode),循环内部的sqlNode;
2、StaticTestNode -> 插入了insert into user(;
3、TrimSqlNode -> 循环递归进入 IfSqlNode对象 -> 根据传入的参数是否满足条件进入 StaticTestSqlNode ->插入了name, 等等;
4、StaticTestNode -> 插入了)values (;
5、TrimSqlNode -> 循环递归进入 IfSqlNode对象 -> 根据传入的参数是否满足条件进入 TestSqlNode ->解析ognl表达式,插入了#{taskCode}代表的1111或2222 等等;
BonedSql
书里面的内容比价全面,这里引用一段《深入浅出mabyties技术原理与实战》内容:
基本的内容就是我们创建了叫XXXMapper的接口文件,需要mybaties给我们利用动态代理技术生成代理对象,去执行sql语句。MapperProxyFactory 是用于生成代理对象,内容不多。MapperProxy 里面是代理方法的位置,请看invoke 方法:
再看execute方法:
总之最后会进入到sqlSession的select方法:
譬如:(回顾一下mappedStatement !!)
在进入BaseExecutor(抽象类) 瞅一眼query方法:
doQuery方法是一个抽象方法,根据不同的类有不同的实现,可以瞅一眼SimpleExecutor,比较简单
SqlSession存在四大对象,可以查资料看一看:
摘一个资料里的图: