动态SQL
说到动态SQL,就不得不提Script,Java作为一个静态语音,代码需要先编译,然后再运行,虽然带来了效率,但是却损失了灵活性。
Spring为此还专门提供了一套SpEL用来封装Java脚本语言API
在MyBatis中,也支持动态SQL,想要将简单的String字符串编译成能运行的代码,需要其他的库的支持,MyBatis内部使用的是OGNL库。
在OgnlCache中,是MyBatis对OGNL的简单封装:
public static Object getValue(String expression, Object root) {
try {
Map context = Ognl.createDefaultContext(root, MEMBER_ACCESS, CLASS_RESOLVER, null);
return Ognl.getValue(parseExpression(expression), context, root);
} catch (OgnlException e) {
throw new BuilderException("Error evaluating expression '" + expression + "'. Cause: " + e, e);
}
}
主要便是增加了一层缓存。
有了上面的基础,我们就可以通过需求,来了解实现了:
在MyBatis中,动态SQL标签有如下几个:
if :通过条件判断执行SQL
choose :通过switch选择一条执行SQL 一般和when / otherwise一起使用
trim : 简单加工SQL,比如去除头尾的逗号等,同类的还有where / set
foreach : 遍历容器,将遍历的结果拼接成SQL
bind : 通过OGNL表达式获取指定的值,并绑定到环境变量中
简单的使用方式如下:
<select id="findActiveBlogWithTitleLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
</select>
可以看到,动态SQL的关键就是获取title的值,然后执行test对应的表达式,最后根据结果拼接SQL
最后也是比较重要的一点就是,MyBatis的动态SQL标签是可以嵌套使用的:
比如:
<update id="update" parameterType="User">
UPDATE users
<trim prefix="SET" prefixOverrides=",">
<if test="name != null and name != ''">
name = #{name}
</if>
<if test="age != null and age != ''">
, age = #{age}
</if>
<if test="birthday != null and birthday != ''">
, birthday = #{birthday}
</if>
</trim>
<where> 1=1
<if test="id != null">
and id = ${id}
</if>
</where>
</update>
这样的结构,就像是一颗树,需要层层遍历处理。
组合模式
前面说到了MyBatis处理动态SQL的需求,需要处理嵌套的标签。
而这个,恰好符合组合模式的解决场景。
在MyBatis中,处理动态SQL的关键类如下:
SqlNode : 用来表示动态标签的相关信息
NodeHandler : 用来处理SqlNode其他信息的类
DynamicContext : 用来保存处理整个标签过程中,解析出来的信息,主要元素为StringBuilder
SqlSource : 用来表示XML中SQL的信息,MyBatis中,动态SQL最终都会通过SqlSource表示
SqlNode接口的定义如下:
public interface SqlNode {
//处理目前的信息,并将处理完毕的信息追加到DynamicContext 中
boolean apply(DynamicContext context);
}
接下来从MyBaits创建以及使用SqlSource上来分析动态SQL的使用:
创建SqlSource的代码如下:
XMLScriptBuilder#parseScriptNode()
public SqlSource parseScriptNode() {
//创建组合模式中的根节点
MixedS