您的位置:首页 > 编程语言 > Java开发

Spring mybatis源码篇章-XMLLanguageDriver解析sql包装为SqlSource

2017-03-27 22:20 936 查看

前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-MybatisDAO文件解析(二)


前文主要分析了Spring中XML配置文件方式的加载MappedStatement,本文在前文的基础上简单了解下Mybatis的SQL语法并且看下CRUD节点是如何被解析的

mapper的动态sql语法

具体的动态sql的使用可在官网查看Mybatis 3 | Dynamic SQL

1.if,条件判断

<select id="findActiveBlogWithTitleLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
</select>

2.choose/when/otherwise,类似于if/else语句判断

<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>

3.trim,多搭配where进行SQL语句拼装

<!--prefix/suffix属性均是由prefixOverrides/suffixOverrides的条件满足后实行的覆盖策略-->

<!--对开头有AND或者OR的值直接替换为WHERE-->
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>

4.set,类比数据库的
set
关键字(其会消除多余的,分隔符)

<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>

5.foreach,集合迭代输出

<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>

6.bind,创建额外属性并应用

<select id="selectBlogsLike" resultType="Blog">
<bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
SELECT * FROM BLOG
WHERE title LIKE #{pattern}
</select>

7.
_paramter
_databaseId
是mybatis的内部属性。前者代表方法参数类,多与bind节点搭配使用,后者则为
settings
属性指定的数据库标识id

SQL节点解析帮助类-XMLLanguageDriver

Mybatis默认XML驱动类为XMLLanguageDriver,其主要作用于解析select|update|insert|delete节点为完整的SQL语句。

笔者此处直接查看主要方法createSqlSource()代码

public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
return builder.parseScriptNode();
}

引出XMLScriptBuilder#parseScriptNode()方法,我们再继续观察如何创建SqlSource对象(真实目的也是为了解析SQL节点)

public SqlSource parseScriptNode() {
//获取select/update/insert/delete节点下的Sql语句节点包装成相应的SqlNode对象
//每个CRUD语句可能都有多个SqlNode对象
List<SqlNode> contents = parseDynamicTags(context);
//包装成混合型SqlNode对象,'Mixed'译为混合,很贴切的名字
MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
SqlSource sqlSource = null;
//是否为动态语句
if (isDynamic) {
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
//否则生成普通版SqlSource
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
return sqlSource;
}

笔者此处简单看下对各类节点的归类处理,XMLScriptBuilder#parseDynamicTags()方法

private List<SqlNode> parseDynamicTags(XNode node) {
//获取CRUD节点下所有子节点,包括文本内容<trim>等动态sql节点
List<SqlNode> contents = new ArrayList<SqlNode>();
NodeList children = node.getNode().getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
XNode child = node.newXNode(children.item(i));
if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
//直接获取数据内容,类似"select * from .."纯文本语句
String data = child.getStringBody("");
TextSqlNode textSqlNode = new TextSqlNode(data);
//TextSqlNode对象主要回去检查纯文本语句是否含有'${'和'}'字符串,有则为true
if (textSqlNode.isDynamic()) {
contents.add(textSqlNode);
isDynamic = true;
} else {
//返回最普通的含有data的StaticTextSqlNode对象
contents.add(new StaticTextSqlNode(data));
}
} else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
String nodeName = child.getNode().getNodeName();
//获取节点名对应的NodeHandler对象,无非就是TrimNodeHandler/WhereNodeHandler等等
NodeHandler handler = nodeHandlers.get(nodeName);
if (handler == null) {
throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
}
//调用同一接口实现解析生成SqlNode对象并统一存入List<SqlNode>集合中
handler.handleNode(child, contents);
isDynamic = true;
}
}
return contents;
}


SqlNode是一个接口,内部只有一个方法apply(),通过parseDynamicTags()方法获取的SqlNode集合是为了方便SqlSource类去真正的生成SQL语句


结束语


代码太多容易看的眼花缭乱,不易理解,决定缩短代码所占篇幅,宁可分门别类讲也不全一股脑灌输


下节预告

笔者认为为了更为深切的理解CRUD节点语法,我们必须准确的去查看下其中的NodeHandler接口类是如何处理不同的CRUD节点,方便我们编写SQL节点语句更精确
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: