MyBatis源码(四)之mapper文件解析和动态Sql解析启动阶段

上篇博文介绍了Mybatis 执行流程全貌 非常重要。本博文mybatis启动过程是如何解析配置文件的。
这还得从Spring的配置文件中SqlSessionFactoryBean 创建SqlsessionFactory说起。

Spring配置文件

该Bean实现了InitializingBean【又是一个Spring的拓展点】在Spring实例化设置和属性之后会触发afterPropertySet的调用。

SqlSessionFactory的创建

每个mapper.xml文件对一个XmlMapperBuilder.parse()

mapper.parse

如下图有一堆以Builder为后缀的类,该部分类继承关系较为复杂,在跟踪代码的时候父子类之间的方法来回调用,为了不被mybatis的套路,所以配上几张图来有个大致的理解全貌。

配置文件解析

看下Mapper.xml的解析类XmlMapperBuilder核心解析方法:

XmlMapperBuilder

方法见名知意:

扫描二维码关注公众号,回复: 1865502 查看本文章
解析 < paramenterMap>标签
解析 < resultMap> 标签
解析 < select> < insert> < update> < delete>标签  

这个几个都是同一套路:以 seclect 标签解析为例:

标签 < select id=”myId” resultMap=”BaseResultMap” parameterType=”java.lang.Long”>
方法 XmlMapperStatmentBuilder.buildStatementFromContext(List< Xnode>)

套路如下:

1. 解析完标签的属性如:resultMap,id,parameterType之后
2. builderAssistant.addMappedStatement(attrVal1,attrVal2...)
3. MappedStatement.Builder statementBuilder(xxx);
4. MappedStatement statement = statementBuilder.build();
5. configuration.addMappedStatement(statement);

就是这个套路

第二步的addxxxx 方法(本例addMappedStatement)的特点是一大堆的入参属性,带着这些参数

创建一个标签对应的模型类,然后通过各自的内部类Builder创建相应的对象,在add到Configuration中

比如< select> < insert>创建MapperStatement,
维护一个内部类Builder,这个builder.build方法创建相应MapperStatement。
builderAssistant.addParameterMap(attrVal1,attrVal2...);《paramenterMap》的属性

再比如< resultMap> ResultMap,
builderAssistant.addResultMap(attrVal1,attrVal2...);//《resultMap》的属性
维护一个内部类Builder,这个builder.build方法创建相应ResultMap。

继续以< select> 为例:
上面的步骤只是将标签和属性解析完成,但是我们的业务Sql还没有解析、拼装,sql有可能还是动态的。
上面的解析过程中有一行代码:
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
这里写图片描述
看到select insert 等标签内容和子标签的解析使用XmlScriptBuilder

XmlScriptBuilder三个成员

private XNode context;//当前< select>标签
private boolean isDynamic;
private Class<?> parameterType;

成员context就是当前的select标签–sqlNode

这里写图片描述

方法返回List< SqlNode>稍后举例说明这里边装的是什么内容。

这里写图片描述

parseDynamicTags的入参node就是context 当前标签select

其实处理动态sql的代码:
NodeHandler nodeHandler = nodeHandler(nodeName)

这里写图片描述

handler.handleNode(child, contents);//方法内部是个递归的
每个handler都会处理一个动态标签,以if标签为例:
这里写图片描述
处理过程:

递归调用parseDynamicTags(当前node,targetContents)

为了更好的说明一条真实的Sql:

这里我们一条Sql为例说明返回值List< SqlNode>的内容:

    <select id="batchQueryBySellerIds" resultMap="BaseResultMap">
        SELECT
            <include refid="Base_Column_List" />
        FROM
            hrd_demand_matching hdm
        WHERE hdm.demand_id = #{demandId}
            AND hdm.seller_member_id in
            <foreach collection="sellerMemberIdLs" item="sellerMemberId" 
                     open="(" close=")" separator=",">
                #{sellerMemberId}
            </foreach>
    </select>

我们的业务sql遇到标签就会生成一个SqlNode include除外,
将解析之后拆分【遇到标签就拆】生成SQLNode之后放入到List< SqlNode> 然后传给MixedSqlNode的构造函数,名字也好混合的SqlNode集合,会产生下图到结构:
这里写图片描述

第五个sqlNode就是递归产生的。

其实每个Sqlnode都对应一个处理器下图是对应关系:
handler.handleNode(child, contents);//方法内部是个递归的

动态标签处理器和对应的SqlNode关系图

至此SqlSource也创建出来了,只有一个动态标签创建DynamicSqlSource装有MixedSqlNode。

此时并没有sql产生,sql的产生需要参数来决定最终sql,参数决定sql的样子,这个需要在运行的时候才能获取,本博文是在启动过程中的分析。

猜你喜欢

转载自blog.csdn.net/mayongzhan_csdn/article/details/78564350