`

ibatis 3.0 Dynamic Sql 设计解析(并与2.x的差异)

阅读更多

前段时间ibatis3.0发布出来了,迫不及待,将其源码下载拜读。相对ibatis 2.x来说,3.0已是完全改变。具体我就不在这细说,论坛中有一个帖子介绍了ibatis 3.0的新特征及使用。

      由于其他模块的源码我还未细读,在这篇中,先来讨论Dynamic Sql在ibatis 3.0中的实现并比较2.x对应模块的设计。

 

写在前头的话:

      其实如从设计模式应用角度去看待ibatis 3.0中Dynamic Sql的实现,这篇跟我的上篇(HtmlParser设计解析(1)-解析器模式 )相同,都是使用Interpreter模式。

      这篇权当Interpreter模式的另一个demo,认我们体会这些开源项目中设计模式的使用。学习都是从模仿开始的,让 我们吸收高人们的经验,应用于我们实践项目需求中。

 

   从总结中提高:

   一、对比2.x中与3.0的Sqlmap中dynamic sql配置

   2.x:

Xml代码 复制代码  收藏代码
  1. < select   id = "dynamicGetAccountList"   parameterClass = "Account"   resultClass = "Account" >     
  2.       select ACC_ID as id,   
  3.       ACC_FIRST_NAME as firstName,   
  4.       ACC_LAST_NAME as lastName,   
  5.       ACC_EMAIL as emailAddress from ACCOUNT   
  6.          
  7.      < dynamic   prepend = "WHERE" >   
  8.        < isNotNull   prepend = "AND"   property = "emailAddress" >   
  9.          ACC_EMAIL  = #emailAddress#   
  10.        </ isNotNull >   
  11.        < isNotNull   property = "idList"   prepend = " or ACC_ID in " >       
  12.          < iterate   property = "idList"   conjunction = ","   open = "("   close = ")"   >       
  13.             #id#     
  14.          </ iterate >       
  15.        </ isNotNull >       
  16.      </ dynamic >   
  17. </ select >   
<select id="dynamicGetAccountList" parameterClass="Account" resultClass="Account"> 
      select ACC_ID as id,
      ACC_FIRST_NAME as firstName,
      ACC_LAST_NAME as lastName,
      ACC_EMAIL as emailAddress from ACCOUNT
	  
	<dynamic prepend="WHERE">
	  <isNotNull prepend="AND" property="emailAddress">
        ACC_EMAIL = #emailAddress#
      </isNotNull>
      <isNotNull property="idList" prepend=" or ACC_ID in ">   
		<iterate property="idList" conjunction="," open="(" close=")" >   
			#id#  
		</iterate>   
	  </isNotNull>   
    </dynamic>
</select>

 

   3.0:

Xml代码 复制代码  收藏代码
  1. < select   id = "dynamicGetAccountList"   parameterType = "Account"   resultType = "Account" >   
  2.       select ACC_ID as id,   
  3.       ACC_FIRST_NAME as firstName,   
  4.       ACC_LAST_NAME as lastName,   
  5.       ACC_EMAIL as emailAddress from ACCOUNT   
  6.   
  7.      < where >   
  8.          < if   test = "emailAddress != null" > ACC_EMAIL  = #{emailAddress} </ if >   
  9.          < if   test = "idList != null" >   
  10.             or ACC_ID IN   
  11.              < foreach   item = "id"   index = "index"   open = "("   close = ")"   separator = ","   collection = "idList" >   
  12.                 #{idList[${index}]}   
  13.              </ foreach >   
  14.         </ if >   
  15.      </ where >   
  16. </ select >   
<select id="dynamicGetAccountList" parameterType="Account" resultType="Account">
	  select ACC_ID as id,
	  ACC_FIRST_NAME as firstName,
	  ACC_LAST_NAME as lastName,
	  ACC_EMAIL as emailAddress from ACCOUNT

	<where>
		<if test="emailAddress != null">ACC_EMAIL = #{emailAddress}</if>
		<if test="idList != null">
			or ACC_ID IN
			<foreach item="id" index="index" open="(" close=")" separator="," collection="idList">
				#{idList[${index}]}
			</foreach>
	   </if>
	</where>
</select>

      从上面这个简单的比较中,第一感觉3.0了中其dynamic sql更加简洁明了。

      其二,test="emailAddress != null" 添加了OGNL的解释支持,可以动态支持更多的判断,这将不限于原2.x中提供

的判断逻辑,更不需要为每个判断条件加个标签进行配置。

      例如:<if test="id > 10 && id < 20"> ACC_EMAIL = #{emailAddress}</if>

               <if test="Account.emailAddress != null "> ACC_EMAIL = #{emailAddress}</if> ……

 

   二、2.x Dynamic Sql的设计

 

   2.1、2.x中dynamic流程。

   这里帖出,我先前在分析ibatis 2.3时画的一个对dynamic sql的整体使用的时序图,可能会显得乱而复杂。

ibatis dynamic sequence

   2.2、主要类设计

       在这,我们只关注这几个类:XMLSqlSource、DynamicSql、SqlTagHandler (具体类结构图见后)

      XMLSqlSource:相当于一个工厂类,其核心方法parseDynamicTags(),用于解析sql Tag,并判断是否是动态SQL标签。如果true,返回一个DynamicSql对象并创建多个SqlChildt对象添加至动态SQL列表中(addChild());false,返回RawSql对象(简单的SQL语句) 。

     DynamicSql:核心的动态SQL类。其动态条件判断逻辑,参数映射等都发生在这个类中。

     SqlTagHandle:动态条件判断接口,其每个动态SQL标签对应其一个子类。

  接下来,我们具体看下在DynamicSql类中核心方法。

    DynamicSql:

Java代码 复制代码  收藏代码
  1. private   void  processBodyChildren(StatementScope statementScope, SqlTagContext ctx, Object parameterObject, Iterator localChildren, PrintWriter out) {   
  2.      while  (localChildren.hasNext()) {  //XMLSqlSource 生成的动态SQL列表   
  3.       SqlChild child = (SqlChild) localChildren.next();   
  4.        if  (child  instanceof  SqlText) {   
  5.           ... ...  //组装SQL语句及映射SQL参数   
  6.       }  else   if  (child  instanceof  SqlTag) {   
  7.         SqlTag tag = (SqlTag) child;   
  8.         SqlTagHandler handler = tag.getHandler();  //得到动态SQL标签处理器   
  9.          int  response = SqlTagHandler.INCLUDE_BODY;   
  10.          do  {             
  11.           response = handler.doStartFragment(ctx, tag, parameterObject);  //处理开始片段   
  12.            if  (response != SqlTagHandler.SKIP_BODY) {  //是否跳过,意思该判断的条件为false   
  13.             processBodyChildren(statementScope, ctx, parameterObject, tag.getChildren(), pw);  //递归处理   
  14.             StringBuffer body = sw.getBuffer();   
  15.             response = handler.doEndFragment(ctx, tag, parameterObject, body);  //处理结束片段   
  16.             handler.doPrepend(ctx, tag, parameterObject, body);  //组装SQL   
  17.                
  18.           }   
  19.         }  while  (response == SqlTagHandler.REPEAT_BODY);   
  20.         ... ...    }   
  21. }  
private void processBodyChildren(StatementScope statementScope, SqlTagContext ctx, Object parameterObject, Iterator localChildren, PrintWriter out) {
    while (localChildren.hasNext()) { //XMLSqlSource 生成的动态SQL列表
      SqlChild child = (SqlChild) localChildren.next();
      if (child instanceof SqlText) {
          ... ... //组装SQL语句及映射SQL参数
      } else if (child instanceof SqlTag) {
        SqlTag tag = (SqlTag) child;
        SqlTagHandler handler = tag.getHandler(); //得到动态SQL标签处理器
        int response = SqlTagHandler.INCLUDE_BODY;
        do {          
          response = handler.doStartFragment(ctx, tag, parameterObject); //处理开始片段
          if (response != SqlTagHandler.SKIP_BODY) { //是否跳过,意思该判断的条件为false
            processBodyChildren(statementScope, ctx, parameterObject, tag.getChildren(), pw); //递归处理
            StringBuffer body = sw.getBuffer();
            response = handler.doEndFragment(ctx, tag, parameterObject, body); //处理结束片段
            handler.doPrepend(ctx, tag, parameterObject, body); //组装SQL
            
          }
        } while (response == SqlTagHandler.REPEAT_BODY);
		... ...    }
}

 

    2.3、SqlTagHandle设计

    首先看下SqlTagHandle处理类的结果图:


 

     ConditionalTagHandler:

Java代码 复制代码  收藏代码
  1. public   abstract   class  ConditionalTagHandler  extends  BaseTagHandler {   
  2.   ... ...   
  3.    public   abstract   boolean  isCondition(SqlTagContext ctx, SqlTag tag, Object parameterObject);   
  4.   
  5.    public   int  doStartFragment(SqlTagContext ctx, SqlTag tag, Object parameterObject) {   
  6.     ctx.pushRemoveFirstPrependMarker(tag);   
  7.      if  (isCondition(ctx, tag, parameterObject)) {   
  8.        return  SqlTagHandler.INCLUDE_BODY;   
  9.     }  else  {   
  10.        return  SqlTagHandler.SKIP_BODY;   
  11.     }   
  12.   }   
  13.   ... ...   
  14. }  
public abstract class ConditionalTagHandler extends BaseTagHandler {
  ... ...
  public abstract boolean isCondition(SqlTagContext ctx, SqlTag tag, Object parameterObject);

  public int doStartFragment(SqlTagContext ctx, SqlTag tag, Object parameterObject) {
    ctx.pushRemoveFirstPrependMarker(tag);
    if (isCondition(ctx, tag, parameterObject)) {
      return SqlTagHandler.INCLUDE_BODY;
    } else {
      return SqlTagHandler.SKIP_BODY;
    }
  }
  ... ...
}

 

   IsNullTagHandler:

Java代码 复制代码  收藏代码
  1. public   class  IsNullTagHandler  extends  ConditionalTagHandler {   
  2.      private   static   final  Probe PROBE = ProbeFactory.getProbe();   
  3.      public   boolean  isCondition(SqlTagContext ctx, SqlTag tag, Object parameterObject) {   
  4.          if  (parameterObject ==  null ) {   
  5.              return   true ;   
  6.         }  else  {   
  7.             String prop = getResolvedProperty(ctx, tag);   
  8.             Object value;   
  9.              if  (prop !=  null ) {   
  10.                 value = PROBE.getObject(parameterObject, prop);   
  11.             }  else  {   
  12.                 value = parameterObject;   
  13.             }   
  14.              return  value ==  null ;   
  15.         }   
  16.     }   
  17. }  
public class IsNullTagHandler extends ConditionalTagHandler {
	private static final Probe PROBE = ProbeFactory.getProbe();
	public boolean isCondition(SqlTagContext ctx, SqlTag tag, Object parameterObject) {
		if (parameterObject == null) {
			return true;
		} else {
			String prop = getResolvedProperty(ctx, tag);
			Object value;
			if (prop != null) {
				value = PROBE.getObject(parameterObject, prop);
			} else {
				value = parameterObject;
			}
			return value == null;
		}
	}
}

   至于其他的相关类,不在这列出了,有兴趣的可以找其源码了解下。

 

   2.4、总结ibatis 2.X Dynamic Sql 的设计

      从上面的分析中,可以体会出作者的dynamic sql这模块的设计思路。从装载sqlmap.xml中各sql配置(时序图中的1步),通过工厂创建DynamicSql和RawSql(时序图中的3步),然后分发之不同的处理器。

      在DynamicSql中则调用SqlTagHandle判断其条件(时序图中的10步)。而SqlTagHandle的设计使用策略者模式,让其不同的子类来处理这个判断逻辑。

      通过一系列的加工,最终组装一个Sql对象,将值set至MappedStatement(时序图中的14步)中,然后MappedStatement对象执行executeQueryWithCallback查询数据(时序图中的17步),这儿会调用先前组装的Sql对象(时序图中的19步)。至于这其中的细节已不在这篇的研究这内。

 

    三、3.0 Dynamic Sql的设计

           至于3.0其基本流程跟2.x是一样的,从装载  -> 参数映射 -> 执行SQL -> 返回结果。我们直接切入主题,分析是核心部分。先从一个简单的Dynamic Sql的测试用例开始。

 

   3.1、 测试用例

    dynamic sql test:

Java代码 复制代码  收藏代码
  1. @Test   
  2. public   void  shouldTrimWHEREInsteadOfORForSecondCondition()  throws  Exception {   
  3.   /* SELECT * FROM BLOG  
  4.     <where>  
  5.         <if test="id != false"> and ID = #{id} </if>  
  6.         <if test="name != false"> or NAME = #{name} </if>  
  7.     </where>  
  8.    */   
  9.      final  String expected =  "SELECT * FROM BLOG WHERE  NAME = ?" ;   
  10.     DynamicSqlSource source = createDynamicSqlSource(   
  11.              new  TextSqlNode( "SELECT * FROM BLOG" ),    
  12.                  new  WhereSqlNode(mixedContents(   
  13.                          new  IfSqlNode(   
  14.                                 mixedContents( new  TextSqlNode( "   and ID = ?  " )), "false" ),  new  IfSqlNode(mixedContents( new  TextSqlNode( "   or NAME = ?  " )),  "true" ))));   
  15.     BoundSql boundSql = source.getBoundSql( null );   
  16.     assertEquals(expected, boundSql.getSql());   
  17. }   
  18.   
  19. private  DynamicSqlSource createDynamicSqlSource(SqlNode... contents)   
  20.          throws  IOException, SQLException {   
  21.     createBlogDataSource();   
  22.      final  String resource =   ".../MapperConfig.xml" ;   
  23.      final  Reader reader = Resources.getResourceAsReader(resource);   
  24.     SqlSessionFactory sqlMapper =  new  SqlSessionFactoryBuilder()   
  25.             .build(reader);   
  26.     Configuration configuration = sqlMapper.getConfiguration();   
  27.     MixedSqlNode sqlNode = mixedContents(contents);   
  28.      return   new  DynamicSqlSource(configuration, sqlNode);   
  29. }   
  30.   
  31. private  MixedSqlNode mixedContents(SqlNode... contents) {   
  32.      return   new  MixedSqlNode(Arrays.asList(contents));   
  33. }  
	@Test
	public void shouldTrimWHEREInsteadOfORForSecondCondition() throws Exception {
	 /*	SELECT * FROM BLOG
		<where>
			<if test="id != false"> and ID = #{id} </if>
			<if test="name != false"> or NAME = #{name} </if>
		</where>
	   */
		final String expected = "SELECT * FROM BLOG WHERE  NAME = ?";
		DynamicSqlSource source = createDynamicSqlSource(
				new TextSqlNode("SELECT * FROM BLOG"), 
					new WhereSqlNode(mixedContents(
							new IfSqlNode(
									mixedContents(new TextSqlNode("   and ID = ?  ")),"false"), new IfSqlNode(mixedContents(new TextSqlNode("   or NAME = ?  ")), "true"))));
		BoundSql boundSql = source.getBoundSql(null);
		assertEquals(expected, boundSql.getSql());
	}

	private DynamicSqlSource createDynamicSqlSource(SqlNode... contents)
			throws IOException, SQLException {
		createBlogDataSource();
		final String resource =  ".../MapperConfig.xml";
		final Reader reader = Resources.getResourceAsReader(resource);
		SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder()
				.build(reader);
		Configuration configuration = sqlMapper.getConfiguration();
		MixedSqlNode sqlNode = mixedContents(contents);
		return new DynamicSqlSource(configuration, sqlNode);
	}

	private MixedSqlNode mixedContents(SqlNode... contents) {
		return new MixedSqlNode(Arrays.asList(contents));
	}

 

  有经验的人,我想一眼就能看出其3.0中的设计思想,从Test中可以看出,或者我上一篇介绍的HtmlParser NodeFilter。

    YES,在ibatis 3.0 dynamic sql设计正是应用了解释器模式,替换了原在这种需求下相对显得笨拙的策略者模式。

 

   下面具体看下类结构图。

 

   3.2、类结构图

   SqlNode Class Diagram :

   SqlSource Class Diagram :

 

   3.3、配置文件的解析

   在这,我就顺便提下ibatis解析组件对dynamic sql的解析方式,以代码见分晓吧。

 

   XMLStatementBuilder:

Java代码 复制代码  收藏代码
  1. public   void  parseStatementNode(XNode context) {   
  2.                ...  ...   
  3.     List<SqlNode> contents = parseDynamicTags(context);   
  4.     MixedSqlNode rootSqlNode =  new  MixedSqlNode(contents); //再次包装dynamic sql处理链   
  5.     SqlSource sqlSource =  new  DynamicSqlSource(configuration, rootSqlNode);  //默认初始化DynamicSqlSource   
  6.                ... ...   
  7.     builderAssistant.addMappedStatement(id, sqlSource, statementType,   
  8.             sqlCommandType, fetchSize, timeout, parameterMap,   
  9.             parameterTypeClass, resultMap, resultTypeClass,   
  10.             resultSetTypeEnum, flushCache, useCache, keyGenerator,   
  11.             keyProperty);  //将解析的所有属性构建成相应的对象存入全局的申明对象(MappedStatement)中,后面只传递该对象。   
  12. }   
  13.   
  14. private  List<SqlNode> parseDynamicTags(XNode node) {   
  15.     List<SqlNode> contents =  new  ArrayList<SqlNode>();   
  16.     NodeList children = node.getNode().getChildNodes();   
  17.      for  ( int  i =  0 ; i < children.getLength(); i++) {   
  18.         XNode child = node.newXNode(children.item(i));   
  19.         String nodeName = child.getNode().getNodeName();   
  20.          if  (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE   
  21.                 || child.getNode().getNodeType() == Node.TEXT_NODE) {   
  22.             String data = child.getStringBody( "" );   
  23.             contents.add( new  TextSqlNode(data));   
  24.         }  else  {   
  25.             NodeHandler handler = nodeHandlers.get(nodeName);   
  26.              if  (handler ==  null ) {   
  27.                  throw   new  BuilderException( "Unknown element <"  + nodeName  "> in SQL statement." );   
  28.             }   
  29.             handler.handleNode(child, contents);   
  30.         }   
  31.     }   
  32.      return  contents;   
  33. }            
  34. private  Map<String, NodeHandler> nodeHandlers =  new  HashMap<String, NodeHandler>() {   
  35.     {   
  36.         put( "where" new  WhereHandler());   
  37.         put( "set" new  SetHandler());   
  38.         put( "foreach" new  ForEachHandler());   
  39.         put( "if" new  IfHandler());   
  40.         ... ...   
  41.     }   
  42. };   
  43. private   interface  NodeHandler {   
  44.      void  handleNode(XNode nodeToHandle, List<SqlNode> targetContents);   
  45. }   
  46. private   class  WhereHandler  implements  NodeHandler {   
  47.      public   void  handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {   
  48.         List<SqlNode> contents = parseDynamicTags(nodeToHandle); // 遍历   
  49.         MixedSqlNode mixedSqlNode =  new  MixedSqlNode(contents); //对应测试用例中的mixedContents方法   
  50.         WhereSqlNode where =  new  WhereSqlNode(mixedSqlNode);   
  51.         targetContents.add(where);   
  52.     }   
  53. }   
  54. private   class  IfHandler  implements  NodeHandler {   
  55.      public   void  handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {   
  56.         List<SqlNode> contents = parseDynamicTags(nodeToHandle); //遍历   
  57.         MixedSqlNode mixedSqlNode =  new  MixedSqlNode(contents);   
  58.         String test = nodeToHandle.getStringAttribute( "test" );   
  59.         IfSqlNode ifSqlNode =  new  IfSqlNode(mixedSqlNode, test); //初始化对应的处理器   
  60.         targetContents.add(ifSqlNode); //   
  61.     }   
  62. // 其他的Handle详见ibatis源码~   
	public void parseStatementNode(XNode context) {
                ...  ...
		List<SqlNode> contents = parseDynamicTags(context);
		MixedSqlNode rootSqlNode = new MixedSqlNode(contents);//再次包装dynamic sql处理链
		SqlSource sqlSource = new DynamicSqlSource(configuration, rootSqlNode); //默认初始化DynamicSqlSource
                ... ...
		builderAssistant.addMappedStatement(id, sqlSource, statementType,
				sqlCommandType, fetchSize, timeout, parameterMap,
				parameterTypeClass, resultMap, resultTypeClass,
				resultSetTypeEnum, flushCache, useCache, keyGenerator,
				keyProperty); //将解析的所有属性构建成相应的对象存入全局的申明对象(MappedStatement)中,后面只传递该对象。
	}

	private List<SqlNode> parseDynamicTags(XNode node) {
		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));
			String nodeName = child.getNode().getNodeName();
			if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE
					|| child.getNode().getNodeType() == Node.TEXT_NODE) {
				String data = child.getStringBody("");
				contents.add(new TextSqlNode(data));
			} else {
				NodeHandler handler = nodeHandlers.get(nodeName);
				if (handler == null) {
					throw new BuilderException("Unknown element <" + nodeName "> in SQL statement.");
				}
				handler.handleNode(child, contents);
			}
		}
		return contents;
	}         
	private Map<String, NodeHandler> nodeHandlers = new HashMap<String, NodeHandler>() {
		{
			put("where", new WhereHandler());
			put("set", new SetHandler());
			put("foreach", new ForEachHandler());
			put("if", new IfHandler());
			... ...
		}
	};
	private interface NodeHandler {
		void handleNode(XNode nodeToHandle, List<SqlNode> targetContents);
	}
	private class WhereHandler implements NodeHandler {
		public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
			List<SqlNode> contents = parseDynamicTags(nodeToHandle);// 遍历
			MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);//对应测试用例中的mixedContents方法
			WhereSqlNode where = new WhereSqlNode(mixedSqlNode);
			targetContents.add(where);
		}
	}
	private class IfHandler implements NodeHandler {
		public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
			List<SqlNode> contents = parseDynamicTags(nodeToHandle);//遍历
			MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
			String test = nodeToHandle.getStringAttribute("test");
			IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);//初始化对应的处理器
			targetContents.add(ifSqlNode);//
		}
	} // 其他的Handle详见ibatis源码~

    上面是其解析代码的一部分,我想从这几行代码中,可以看出作者的思想了(遍历XML各节点,以节点名查找相应对应的处理器,分发之该处理器执行"业务分析" — 策略者模式,这样在XML中定义了多少标签,这里就需要多少个类与之对应,但如果策略类太多,这种方式就显得笨拙了)。

 

    以下就是其核心类的一部分源码,先看再说。

    3.4、DynamicSqlSource(核心类)

Java代码 复制代码  收藏代码
  1. public   class  DynamicSqlSource  implements  SqlSource {   
  2.      public  DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {   
  3.          this .configuration = configuration;   
  4.          this .rootSqlNode = rootSqlNode;   
  5.     }   
  6.      public  BoundSql getBoundSql(Object parameterObject) {   
  7.         DynamicContext context =  new  DynamicContext(parameterObject); //组装后的结果存储类   
  8.         rootSqlNode.apply(context); //调用SqlNode解释sql,并组装成完整的sql(SqlNode的客户端调用就在这)   
  9.         SqlSourceBuilder sqlSourceParser =  new  SqlSourceBuilder(configuration);   
  10.         Class parameterType = parameterObject ==  null  ? Object. class  : parameterObject.getClass();   
  11.         SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType);   
  12.         BoundSql boundSql = sqlSource.getBoundSql(parameterObject);   
  13.          for  (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {   
  14.             boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());   
  15.         }   
  16.          return  boundSql;   
  17.     }   
  18. }  
public class DynamicSqlSource implements SqlSource {
	public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
		this.configuration = configuration;
		this.rootSqlNode = rootSqlNode;
	}
	public BoundSql getBoundSql(Object parameterObject) {
		DynamicContext context = new DynamicContext(parameterObject);//组装后的结果存储类
		rootSqlNode.apply(context);//调用SqlNode解释sql,并组装成完整的sql(SqlNode的客户端调用就在这)
		SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
		Class parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
		SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType);
		BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
		for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
			boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
		}
		return boundSql;
	}
}

 

   3.5、SqlNode

 

Java代码 复制代码  收藏代码
  1. public   interface  SqlNode {   
  2.      public   boolean  apply(DynamicContext context);   
  3. }  
public interface SqlNode {
	public boolean apply(DynamicContext context);
}

 

    MixedSqlNode.class

Java代码 复制代码  收藏代码
  1. public   class  MixedSqlNode  implements  SqlNode {   
  2.         ... ....   
  3.      public   boolean  apply(DynamicContext context) {   
  4.          //遍历组装的解析内容   
  5.          for  (SqlNode sqlNode : contents) {    
  6.              // 转发至相关解释器处理   
  7.             sqlNode.apply(context);    
  8.         }   
  9.          return   true ;   
  10.     }   
  11. }  
public class MixedSqlNode implements SqlNode {
        ... ....
	public boolean apply(DynamicContext context) {
		//遍历组装的解析内容
		for (SqlNode sqlNode : contents) { 
			// 转发至相关解释器处理
			sqlNode.apply(context); 
		}
		return true;
	}
}

   IfSqlNode.class

相关推荐

Global site tag (gtag.js) - Google Analytics