本文只记录单独使用mybatis框架,并未使用spring整合。
此方法为测试方法,首先第一步将配置文件读成字节流,重点看sqlsessionfactorybuilder中的build方法
此处走的是一个build的重载方法,生成一个XMLconfigBuilder对象
这边初始化了XMLConfigBuilder的参数,重点关注XPathParser对象
其中关注属性:document为xml文件的根节点,validation是否开启验证,variables对应节点下的键值对集合,xpath解析xml的对象。然后继续走到XMLConfigBuilder的parse方法。
首先看parser(XPathParser).evalNode方法,实际就是将该标签解析通过xpath为一个node对象然后封装为一个XNode对象返回。
此方法解析configuration标签下所有子标签,其中主要有plugin(拦截器插件增强解析),environments(数据库链接配置)、mapper(mapper.xml文件解析)需要重点了解,首先是pluginElement
可以看出遍历根节点的子节点,然后找出配置的interceptor信息,转为interceptor对象,设置参数属性,添加到配置中。
最后将interceptor对象放入interceptorChain对象的拦截器集合中。此处解析插件完成
接下来是environment,首先判断一下是不是一个具体有效的环境配置,此处做了两个操作,一是配置事务管理器transactionManager,二是是配置数据源datasource,最后封装成Environment对象。
接下来是解析mapper.xml配置文件,首先判断是骚包类型还是一个url或者一个class路径,此处我用的是reource,然后加载为字节输入流,这里又会创建一个XMLMapperBuilder对象
有和上述XMLConfigBuilder相同的属性此处不在赘述Ps:idea换了个养眼的背景,颜色变了。
此处进入xmlMapperBuilder的parse方法,解析Mapper.xml文件,先判断是否加载过资源
这边可以看到,先获取namespace的值,如果没有设置或者为空就会抛出异常,接着将namesapce设置到builderAssistant中,这个对象是一个MapperBuilderAssistant对象,也就是Mapper解析助理对象,mapperbuider解析文件会将信息放入assistant中解析。接下来就是解析配置其他命名空间缓存,以及本命名空间缓存,接着就是请求参数的map映射,已经结果集的参数map映射,然后就是通用sql语句的解析。最后就开始解析每个sql标签中的sql,这里重点看一下。
红框就是执行解析的方法
这边逻辑容易理解,就是解析sql的各项配置,我举出其中我觉得难理解的地方解释一下XMLIncludeTransformer includeParser =new XMLIncludeTransformer(configuration,builderAssistant);和includeParser.applyIncludes(context.getNode());这两个方法是解析include标签的,processSelectKeyNodes(id, parameterTypeClass, langDriver);这个方法是解析SelectKet标签的。然后就到了sqlSource对象的创建,这边是生成动态sql的主要逻辑,进入看一下。
首先创建了一个XMLScriptBuilder对象
总共有三个参数一个是上下文 一个是是否是动态sql一个是参数类型。
首先解析sql,此处时使用了node类型知识点,可去搜索相关知识,如果是静态的则会生成一个静态的sqlNode对象,如果是动态的则会根据不同的标签生成不同的sqlNode对象存入list集合
接下来看解析方法,主要是判断是否是动态sql,如果是就创建DynamicSqlSource对象,如果不是就创建RawSqlSource对象,其中sqlSource有一个方法
从这里可以看出不同的sqlSource对象getBoundSql方法不同,也就是在执行sql的时候对sql解析不同,等调用查询的时候在具体看如何处理的。
此处是判断insert语句是否设置了需要返回主键
此处时将解析好的sql封装为mapperstatement对象并保存
此处可以看到最后会以key-value形式存储到configuration的map集合中,key就是namespace.id value就是mappedStatement(解析的sql标签的内容),所以Mapper.xml方法是不支持重载的。
解析完之后回到这里,有一个重要方法bindMapperForNamespace
此方法调用了configuration.addMapper方法
可以看到这边存放了type对应的对象代理工厂(用于生成代理对象)放入mapperRegister放入Map中,type是传入的namespace名,也就是Mapper接口对象。
至此mybatis xml配置文件启动流程解析完毕,接下来是执行流程。
首先通过SqlSessionFactoryBuilder.openSession方法生成sqlsession,然后调用sqlsession的getMapper方法。传入的参数是需要生成代理对象的类。
此处可看到getMapper方法实际上调用的MapperRegister方法
首先从Map集合中找出对象对应的代理工厂对象,然后调用工厂对象的newInstance方法
这边可以看出用的是JDK动态代理,MapperProxy是一个实现了InvocationHandler类的接口,对方法进行了判断处理,具体执行的时候看。
此处执行findAll方法会进入invoke方法,首先如果是Object类则不进行其他操作直接执行原方法,然后判断是否有默认实现方法,如果有就走invokeDefaultMethod方法,如果没有就走cachedMapperMethod方法,此步骤主要是从缓存中获取MapperMethod方法,如果没有则生成一个放入缓存中。最后调用MapperMethod方法的execute方法。
首先判断是那种类型,本例用的select,然后判断是否需要返回值,以及resultHandler,然后进入对应的执行方法,此处我是返回多个对象也就是第二个if判断。
首先获取请求参数,然后判断是否有分页,我这边没有,进入else逻辑执行selectList方法
此处获取对应的MappedStatement(是解析的每个标签的信息),然后执行query方法,这边看getBoundSql。
这边要先从sqlSource中获取BoundSql对象,这里sqlSource是DynamicSqlSource.
sql:存放了sql;parameterMappings 记录参数顺序; parameterObject请求参数,如果是单个参数则是一个对象,如果传入多个参数则是一个ParamMap集合;additionalParameters 参数map;metaParameters一个metaObject对象通过这个MetaObject对象可以很方便获取或设置originalObject对象(传入对象参数)的值
首先创建DynamicContext对象,存储一些参数 sql信息,然后执行SqlNode的apply方法
这边遍历了之前存入的sqlNode对象分别执行apply方法
可以看出这段sql一共三个SqlNode,首先看第一个StaticTextSqlNode
就是简单的String拼接,接着看IfsqlNode
此处就是通过OgnlCache.getValue方法判断条件以及传入的参数是否符合要求并返回,如果符合要求就会走MixedSqlNode.apply方法,如果不符合就不会操作。此时的MixedSqlNode.apply就是将符合条件标签里的sql拼接上去。
解析完sql之后,获取参数类型,通过sqlSourceParser.parse将sql中#{}解析为?,然后生成BoundSql对象。
之后调用真正的query方法,首先会去查找是否开启缓存。如果没有,则直接执行query方法
首先会先去从一级缓存查找(一级缓存默认开启),如果找不到则会去儿执行查询方法queryFromDataBase
这边主要是一级缓存占位,然后查询结果之后在设置一级缓存
这边主要是生成statementHandler,以及预处理sql,然后执行statementhandler.query方法
这边查询到结果之后交由ResultSetHandler处理
这边就是一些转换逻辑,就不做具体介绍了。至此Mybatis的执行流程也解析完成,本流程是基于拥有动态sql的条件下解析的。最后再介绍一下Mybatis的核心组件
StatementHandler
封装了JDBC Statement操作,负责对JDBC statement的操作,如设置参
数、将Statement结果集转换成List集合。
ParameterHandler
负责对用户传递的参数转换成JDBC Statement所需要的参数,
ResultSetHandler
负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;
Executor
MyBatis执行器,是MyBatis调度的核心,负责SQL语句的生成和查询缓 存
的维护