前段时间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:
- <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>??
<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:
- <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>??
<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的整体使用的时序图,可能会显得乱而复杂。
?? 2.2、主要类设计
?? ? ? 在这,我们只关注这几个类:XMLSqlSource、DynamicSql、SqlTagHandler (具体类结构图见后)
?? ? ?XMLSqlSource:相当于一个工厂类,其核心方法parseDynamicTags(),用于解析sql Tag,并判断是否是动态SQL标签。如果true,返回一个DynamicSql对象并创建多个SqlChildt对象添加至动态SQL列表中(addChild());false,返回RawSql对象(简单的SQL语句) 。
?? ? DynamicSql:核心的动态SQL类。其动态条件判断逻辑,参数映射等都发生在这个类中。
?? ? SqlTagHandle:动态条件判断接口,其每个动态SQL标签对应其一个子类。
??接下来,我们具体看下在DynamicSql类中核心方法。
?? ?DynamicSql:
- 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); ??
- ????????...?...????} ??
- }??
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:
- 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; ??
- ????} ??
- ??} ??
- ??...?... ??
- }??
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:
- 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; ??
- ????????} ??
- ????} ??
- }??
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:
- @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)); ??
- }??
@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:
- 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源码~??
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(核心类)
- 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; ??
- ????} ??
- }??
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
?
- public?interface?SqlNode?{ ??
- ????public?boolean?apply(DynamicContext?context); ??
- }??
public interface SqlNode { public boolean apply(DynamicContext context);}
?
?? ?MixedSqlNode.class
- public?class?MixedSqlNode?implements?SqlNode?{ ??
- ????????...?.... ??
- ????public?boolean?apply(DynamicContext?context)?{ ??
- ????????//遍历组装的解析内容 ??
- ????????for?(SqlNode?sqlNode?:?contents)?{? ??
- ????????????//?转发至相关解释器处理 ??
- ????????????sqlNode.apply(context);? ??
- ????????} ??
- ????????return?true; ??
- ????} ??
- }??
public class MixedSqlNode implements SqlNode { ... .... public boolean apply(DynamicContext context) { //遍历组装的解析内容 for (SqlNode sqlNode : contents) { // 转发至相关解释器处理 sqlNode.apply(context); } return true; }}
???IfSqlNode.class
- spring mvc +ibatis+db2连接数据库的配置如何写啊小弟我链接不下
- Ibatis sql地图执行
- ibatis sql地图 插入数据返回数据id有关问题
- dynamic web project怎么转成tomcat project
- Ibatis 查询话语selec count(*),getSqlMapClientTemplate().后面应该接什么
- struts2+spring+hibernater+ibatis 调整框架
- ibatis 怎么直接执行sql语句,待!100分
- 问大家一个 dynamic web project 和 tomcat的有关问题
- ibatis 动态查询解决思路
- ibatis 中的Oracle语句中# #好像不生效,不能正确选择出数据(很急)解决思路
- 新手问:怎么在前台显示列表纪录?框架是用struts2+spring2.5+Ibatis
- ibatis+spring+struts 有时候抛出这个错误,有时没有,那个页面每次点击都很慢
- struts+iBatis+mysql,该如何解决
- Cause: java.sql.SQLException: 关闭的连接]; nested exception is com.ibatis.common.jdbc.exception.NestedSQLException解决思路
- ibatis 报语法异常
- eclipse 怎么发布 Dynamic web project到Tomcat
- 关键字条件查询分页有关问题<SpringMVC/ibatis>
- spring struts ibatis 架构 权限控制怎么做
- SSI框架 ibatis 中 queryforList查询不到数据,该怎么解决
- ibatis 里面写sql语句的xml怎么提示
- ibatis 在vs2010上provider.xsd应该放到哪个目录上
- ibatis 在vs2010下provider.xsd应该放到哪个目录下啊解决方法
- IBatis.Net框架连接oracle数据库,该怎么处理
- ibatis.net,该怎么解决
- Dynamic.cs 这个扩充库是否只支持linq to sql
- ibatis mysql转换成Oracle,实施语句中# #好像不生效,不能正确选择出数据(很急)
- Informix Dynamic Server Version 7.31 能否创建自定义函数
- informix -Unable to initialize the Dynamic Server.解决思路
- informix -Unable to initialize the Dynamic Server.解决办法
- Informix Dynamic Server Configuration 有关问题