- 浏览: 280022 次
-
文章分类
最新评论
-
guji528:
oracle的统计函数真的很强大
Oracle 使用LAG和LEAD函数统计 -
haiyangyiba:
受教了
No Dialect mapping for JDBC type: 3 -
elvishehai:
能不能给个完整点的例子呀,我一直都没有试成功过,
简单的使用jackson的annotation -
miaoyue:
如果我想把name属性转成注释之后,之前的注释还在怎么做?
PowerDesigner 中将Comment(注释)及Name(名称)内容互相COPY的VBS代码 -
ianl_zt:
以上方法只适合在hibernate2中使用,现在hiberna ...
Hibernate的批量更新和删除
前段时间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>
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>
从上面这个简单的比较中,第一感觉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); ... ... } }
2.3、SqlTagHandle设计
首先看下SqlTagHandle处理类的结果图:
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; } } }
至于其他的相关类,不在这列出了,有兴趣的可以找其源码了解下。
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)); }
有经验的人,我想一眼就能看出其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源码~
上面是其解析代码的一部分,我想从这几行代码中,可以看出作者的思想了(遍历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; } }
3.5、SqlNode
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; } }
IfSqlNode.class
public class IfSqlNode implements SqlNode { ... ... public IfSqlNode(SqlNode contents, String test) { this.test = test; this.contents = contents; this.evaluator = new ExpressionEvaluator(); } public boolean apply(DynamicContext context) { if (evaluator.evaluateBoolean(test, context.getBindings())) {//OGNL Expressions contents.apply(context); return true; // } return false; } }
TextSqlNode.class
public class TextSqlNode implements SqlNode { private String text; public TextSqlNode(String text) { this.text = text; } public boolean apply(DynamicContext context) { GenericTokenParser parser = new GenericTokenParser("${", "}", new BindingTokenParser(context)); context.appendSql(parser.parse(text));//组装sql return true; } private static class BindingTokenParser implements GenericTokenParser.TokenHandler { private DynamicContext context; public BindingTokenParser(DynamicContext context) { this.context = context; } public String handleToken(String content) { try { Object value = Ognl.getValue(content, context.getBindings()); return String.valueOf(value); } catch (OgnlException e) { throw new BuilderException("Error evaluating expression '" + content + "'. Cause: " + e, e); } } } }
通过这些代码,再结合上面的测试用例就能够明白个七七八八,简单说就是解释器模式(Interpreter)的一个demo。
其中SqlNode接口中方法非常简单,就一个apply(),接受一个DynamicContext的参数。
TextSqlNode 在这扮演终结表达式角色,在这个解释类中再没有contents.apply(context);的方法出现,到此结束,退出遍历,添加一个SQL条件,因在此之前其他解释器已判定所有的指定条件是否符合。
其他的解释类都是非终结表达式角色,为TextSqlNode做保护判断,是否允许进行这个"地带"。
3.6、总结
通过上面的分析,我们不难看出,其结构非常简单、清晰。
需求是:用户通过指定的标签按指定的规则组装业务逻辑。这里必须是指定,因为从上代码中看,其这模块不适用用户自定义扩展。
解决方案是:XMLStatementBuilder读取配置(每个标签对就一个配置解析类 - 采用策略者模式),生成一个SqlSource的对象。再次,Executor执行时需要得到一个BoundSql对象,这时调用SqlNode对象将符合用户条件的组装成完整SQL(每个标签也同时对应一个解释器或者说条件判断器 - 解释器模式 ),最后从Executor从BoundSql读取需要的值,执行客户端的操作。OVER。其中灵活之处在SqlNode客户端,可由用户自行构造对象链。
我想这时对ibatis 3.0 的dynamic sql设计应有所了解。当然,在这只是粗略的体现其作者的思想,详细、完整还需要看完整源码。
相对于2.x版本来说,其大致思想及流程是不变的,只是采取不同的方式去处理。相对2.x,3.0 dynamic sql这模块显得更为轻巧,在解释配置时,就将层次分析清楚,然后运用解释器模式有效的配合了,对配置的解释及生成完整的SQL。就像剥竹笋一样,一层一层,清晰可见。
以上是本人粗略里分析的ibatis的dynamic sql这模块并与之2.x的进行简单的比较,简洁的体现ibatis作者在改版时的设计思想的变化,有对照及总结才有提高。我这权当抛砖引玉,如其中有什么错误,请大家指出,并请大家多多指教。
相关推荐
标题和描述均聚焦于ibatis 3.0 Dynamic Sql的设计解析以及它与2.x版本的差异,这是一个关于ORM(对象关系映射)框架ibatis在处理动态SQL方面的重要升级和改进的主题。以下是对该主题的深入探讨: ### 重要概念:...
ibatis,ibatis,ibatis,ibatis,ibatis
内容概要:本文主要探讨了SNS单模无芯光纤的仿真分析及其在通信和传感领域的应用潜力。首先介绍了模间干涉仿真的重要性,利用Rsoft beamprop模块模拟不同模式光在光纤中的传播情况,进而分析光纤的传输性能和模式特性。接着讨论了光纤传输特性的仿真,包括损耗、色散和模式耦合等参数的评估。随后,文章分析了光纤的结构特性,如折射率分布、包层和纤芯直径对性能的影响,并探讨了镀膜技术对光纤性能的提升作用。最后,进行了变形仿真分析,研究外部因素导致的光纤变形对其性能的影响。通过这些分析,为优化光纤设计提供了理论依据。 适合人群:从事光纤通信、光学工程及相关领域的研究人员和技术人员。 使用场景及目标:适用于需要深入了解SNS单模无芯光纤特性和优化设计的研究项目,旨在提高光纤性能并拓展其应用场景。 其他说明:本文不仅提供了详细的仿真方法和技术细节,还对未来的发展方向进行了展望,强调了SNS单模无芯光纤在未来通信和传感领域的重要地位。
发那科USM通讯程序socket-set
嵌入式八股文面试题库资料知识宝典-WIFI.zip
源码与image
内容概要:本文详细探讨了物流行业中路径规划与车辆路径优化(VRP)的问题,特别是针对冷链物流、带时间窗的车辆路径优化(VRPTW)、考虑充电桩的车辆路径优化(EVRP)以及多配送中心情况下的路径优化。文中不仅介绍了遗传算法、蚁群算法、粒子群算法等多种优化算法的理论背景,还提供了完整的MATLAB代码及注释,帮助读者理解这些算法的具体实现。此外,文章还讨论了如何通过MATLAB处理大量数据和复杂计算,以得出最优的路径方案。 适合人群:从事物流行业的研究人员和技术人员,尤其是对路径优化感兴趣的开发者和工程师。 使用场景及目标:适用于需要优化车辆路径的企业和个人,旨在提高配送效率、降低成本、确保按时交付货物。通过学习本文提供的算法和代码,读者可以在实际工作中应用这些优化方法,提升物流系统的性能。 其他说明:为了更好地理解和应用这些算法,建议读者参考相关文献和教程进行深入学习。同时,实际应用中还需根据具体情况进行参数调整和优化。
嵌入式八股文面试题库资料知识宝典-C and C++ normal interview_8.doc.zip
内容概要:本文介绍了基于灰狼优化算法(GWO)的城市路径规划优化问题(TSP),并通过Matlab实现了该算法。文章详细解释了GWO算法的工作原理,包括寻找猎物、围捕猎物和攻击猎物三个阶段,并提供了具体的代码示例。通过不断迭代优化路径,最终得到最优的城市路径规划方案。与传统TSP求解方法相比,GWO算法具有更好的全局搜索能力和较快的收敛速度,适用于复杂的城市环境。尽管如此,算法在面对大量城市节点时仍面临运算时间和参数设置的挑战。 适合人群:对路径规划、优化算法感兴趣的科研人员、学生以及从事交通规划的专业人士。 使用场景及目标:①研究和开发高效的路径规划算法;②优化城市交通系统,提升出行效率;③探索人工智能在交通领域的应用。 其他说明:文中提到的代码可以作为学习和研究的基础,但实际应用中需要根据具体情况调整算法参数和优化策略。
嵌入式八股文面试题库资料知识宝典-Intel3.zip
嵌入式八股文面试题库资料知识宝典-2019京东C++.zip
嵌入式八股文面试题库资料知识宝典-北京光桥科技有限公司面试题.zip
内容概要:本文详细探讨了十字形声子晶体的能带结构和传输特性。首先介绍了声子晶体作为新型周期性结构在物理学和工程学中的重要地位,特别是十字形声子晶体的独特结构特点。接着从散射体的形状、大小、排列周期等方面分析了其对能带结构的影响,并通过理论计算和仿真获得了能带图。随后讨论了十字形声子晶体的传输特性,即它对声波的调控能力,包括传播速度、模式和能量分布的变化。最后通过大量实验和仿真验证了理论分析的正确性,并得出结论指出散射体的材料、形状和排列方式对其性能有重大影响。 适合人群:从事物理学、材料科学、声学等相关领域的研究人员和技术人员。 使用场景及目标:适用于希望深入了解声子晶体尤其是十字形声子晶体能带与传输特性的科研工作者,旨在为相关领域的创新和发展提供理论支持和技术指导。 其他说明:文中还对未来的研究方向进行了展望,强调了声子晶体在未来多个领域的潜在应用价值。
嵌入式系统开发_USB主机控制器_Arduino兼容开源硬件_基于Mega32U4和MAX3421E芯片的USB设备扩展开发板_支持多种USB外设接入与控制的通用型嵌入式开发平台_
e2b8a-main.zip
少儿编程scratch项目源代码文件案例素材-火柴人跑酷(2).zip
内容概要:本文详细介绍了HarmonyOS分布式远程启动子系统,该系统作为HarmonyOS的重要组成部分,旨在打破设备间的界限,实现跨设备无缝启动、智能设备选择和数据同步与连续性等功能。通过分布式软总线和分布式数据管理技术,它能够快速、稳定地实现设备间的通信和数据同步,为用户提供便捷的操作体验。文章还探讨了该系统在智能家居、智能办公和教育等领域的应用场景,展示了其在提升效率和用户体验方面的巨大潜力。最后,文章展望了该系统的未来发展,强调其在技术优化和应用场景拓展上的无限可能性。 适合人群:对HarmonyOS及其分布式技术感兴趣的用户、开发者和行业从业者。 使用场景及目标:①理解HarmonyOS分布式远程启动子系统的工作原理和技术细节;②探索该系统在智能家居、智能办公和教育等领域的具体应用场景;③了解该系统为开发者提供的开发优势和实践要点。 其他说明:本文不仅介绍了HarmonyOS分布式远程启动子系统的核心技术和应用场景,还展望了其未来的发展方向。通过阅读本文,用户可以全面了解该系统如何通过技术创新提升设备间的协同能力和用户体验,为智能生活带来新的变革。
嵌入式八股文面试题库资料知识宝典-C and C++ normal interview_1.zip
少儿编程scratch项目源代码文件案例素材-激光反弹.zip
内容概要:本文详细介绍了COMSOL相控阵检测技术在有机玻璃斜楔上放置16阵元进行工件内部缺陷检测的方法。首先阐述了相控阵检测技术的基本原理,特别是通过控制各阵元的激发时间和相位来实现声波的聚焦和扫描。接着,重点解析了横孔缺陷的反射接收波,解释了波的折射现象及其背后的物理原因。最后,通过实例展示了COMSOL模拟声波传播过程的成功应用,验证了该技术的有效性和准确性。 适合人群:从事固体力学、无损检测领域的研究人员和技术人员,尤其是对相控阵检测技术和COMSOL仿真感兴趣的读者。 使用场景及目标:适用于需要精确检测工件内部缺陷的研究和工业应用场景,旨在提高检测精度和效率,确保产品质量和安全。 其他说明:文中提到的声速匹配现象有助于理解波在不同介质间的传播特性,这对优化检测参数设置有重要意义。