组合( Composite )模式就是把对象组合成树形结构,以表示“部分-整体”的层次结构,用户可以像处理一个简单对象一样来处理一个复杂对象,从而使得调用者无需了解复杂元素的内部结构。
组合模式中的角色有:
-
抽象组件(容器):定义了树形结构中所有类的公共行为,例如add(),remove()等方法。
-
树叶:最终实现类,没有子类。
-
树枝:有子类的管理类,并通过管理方法调用其管理的子类的相关操作。
-
调用者:通过容器接口操作整个树形结构。
具体组合模式的例子可以参考 设计模式整理
现在我们来说一下SqlNode是什么,来看这么一段配置文件
<select id="findByGameTypeCount" resultType="java.lang.Long"> select count(*) from betdetails a inner join UserBetOrder b on a.orderId = b.id <where> <if test="gameType != null and gameType > 0"> a.gameType = #{gameType} and </if> <if test="currDrawno != null"> b.currentDrawno = #{currDrawno} and </if> <if test="orderId != null and orderId > 0"> a.orderId = #{orderId} and </if> <if test="status != null and status >= 0"> a.status = #{status} and </if> <if test="userId != null and userId > 0"> b.userId = #{userId} and </if> <if test="start != null"> a.createTime >= #{start} and </if> <if test="end != null"> a.createTime <= #{end} and </if> 1 = 1 </where></select>
<insert id="insertBetdetailsByBatch" parameterType="java.util.List"> insert into betdetails(id,orderId,actorIndex,createTime,ballIndex,ballValue,betAmount,rate1,rate2,rate3,gameType,status,betResult,awardAmount,ballName) values <foreach collection="list" item="item" index="index" separator=","> (#{item.id},#{item.orderId},#{item.actorIndex},#{item.createTime},#{item.ballIndex},#{item.ballValue},#{item.betAmount},#{item.rate1},#{item.rate2},#{item.rate3},#{item.gameType},#{item.status},#{item.betResult},#{item.awardAmount},#{item.ballName}) </foreach></insert>
这其中的<if><where><foreach>节点就是SqlNode节点,SqlNode是一个接口,代表着组合模式中的容器。只要是有SqlNode,那就代表着一定是一个动态的SQL,里面就有可能会有参数#{}
public interface SqlNode { //SqlNode接口中定义的唯一方法,该方法会根据用户传入的实参,解析该SqlNode所记录的动态SQL节点,并调用DynamicContext.appendSql()方法将解析后的SQL片段追加到 //DynamicContext.sqlBuilder中保存 //当SQL节点下的所有SqlNode完成解析后,就可以从DynamicContext中获取一条动态生成的完整的SQL语句 boolean apply(DynamicContext context);}
我们先来看一下DynamicContext是什么,它的核心字段如下
private final ContextMap bindings; //参考上下文 //在SqlNode解析动态SQL时,会将解析后的SQL语句片段添加到该属性中保存,最终拼凑出一条完成的SQL语句private final StringBuilder sqlBuilder = new StringBuilder();
ContextMap是一个内部类,继承于HashMap,重写了get方法
static class ContextMap extends HashMap<String, Object> { private static final long serialVersionUID = 2977601501966151582L; //将用户传入的参数封装成MetaObject对象(类实例中检查类的属性是否包含getter,setter方法) private MetaObject parameterMetaObject; public ContextMap(MetaObject parameterMetaObject) { this.parameterMetaObject = parameterMetaObject; } @Override public Object get(Object key) { String strKey = (String) key; //如果ContextMap中已经包含了该key,则直接返回 if (super.containsKey(strKey)) {