2019独角兽企业重金招聘Python工程师标准>>>

上一篇博文中,介绍了可复用的sql片段,通过<include>标签进行引入,而<include>标签内一般存放的是静态sql,其实,sql片段也是可以放置动态sql标签内容。

1. Mybatis支持的动态sql及基本用法

org.apache.ibatis.scripting.xmltags.XMLScriptBuilder.nodeHandlers(String)部分源码。

  NodeHandler nodeHandlers(String nodeName) {Map<String, NodeHandler> map = new HashMap<String, NodeHandler>();map.put("trim", new TrimHandler());map.put("where", new WhereHandler());map.put("set", new SetHandler());map.put("foreach", new ForEachHandler());map.put("if", new IfHandler());map.put("choose", new ChooseHandler());map.put("when", new IfHandler());map.put("otherwise", new OtherwiseHandler());map.put("bind", new BindHandler());return map.get(nodeName);}

Mybatis所支持的动态sql标签:trim|where|set|foreach|if|choose|when|otherwise|bind。

  <select id="findStudents" parameterType="customMap" resultType="StudentResult">select * from STUDENTS where 1 = 1 <choose><when test="name != null">and name = #{name}</when><when test="email != null">and EMAIL = #{email}</when><otherwise>and PHONE = "123"</otherwise></choose></select><select id="countAll" resultType="int">select count(1) from (select stud_id as studId, name, email, dob, phonefrom students<where><if test="id != null">AND STUD_ID &lt; 310</if></where>) tmp </select><select id="findAllStudents" resultMap="StudentResult" parameterMap="customMap"><bind name="status" value="'status'"/>SELECT * FROM STUDENTS WHERE STUD_ID > #{id}, #{status},${driver}</select><insert id="insertStudents" useGeneratedKeys="true" keyProperty="studId" parameterType="java.util.ArrayList">INSERT INTOSTUDENTS(STUD_ID, NAME, EMAIL, DOB, PHONE)VALUES<foreach collection="list" item="item" index="index" separator=","> (#{item.studId},#{item.name},#{item.email},#{item.dob}, #{item.phone}) </foreach> </insert>

为了避免篇幅过长,我们简单列举了几个动态sql的基本用法,我们的重点依然是剖析Mybatis动态sql的底层设计原理。

2. SqlSource

在Mybatis中,每一个select|insert|update|delete标签,都会被解析为一个MappedStatement对象,SqlSource就是MappedStatement对象中一个属性,其最终执行的sql字符串就是由SqlSource提供的。

public final class MappedStatement {private SqlSource sqlSource;
}

org.apache.ibatis.builder.xml.XMLStatementBuilder.parseStatementNode()部分源码:

SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);

(Made In IntelliJ Idea IDE)

DynamicSqlSource:处理动态sql。

RawSqlSource:处理静态sql,其内部装饰StaticSqlSource。

StaticSqlSource:处理静态sql,无论是静态sql,还是动态sql,最终的处理结果,都是静态sql。

ProviderSqlSource:处理注解Annotation形式的sql。

DynamicSqlSource和StaticSqlSource的最大区别在于:StaticSqlSource的String sql,可以直接获取使用,而DynamicSqlSource的String sql需要逐一根据条件解析并拼接出最终的sql,方能使用。

3. DynamicSqlSource以及SqlNode

public class DynamicSqlSource implements SqlSource {private Configuration configuration;private SqlNode rootSqlNode;
}
public interface SqlNode {boolean apply(DynamicContext context);
}

boolean apply(DynamicContext context):该方法的含义为,将sql的处理结果,append到DynamicContext context对象中,DynamicContext可以理解为StringBuilder对象的功能,它的作用就是计算sql片段并append到一起,形成最终的sql。对该方法的理解非常重要,只有理解了这个方法,才能真正明白一个完整sql是如何组装出来的。

下面的伪代码,展示了SqlNode.apply(DynamicContext)方法设计的核心原理。

        StringBuilder sb = new StringBuilder();IfSqlNode.apply(StringBuilder sb) {sb.append("select ");}SetSqlNode.apply(StringBuilder sb) {sb.append("* from ss ");}sb.toString();//output: select * from ss

DynamicSqlSource为动态sql源,而SqlNode则具体代表了动态sql源中具体的动态sql类型。

(Made In IntelliJ Idea IDE)

上面的SqlNode,基本上见名知意,我们着重解释一下容易迷惑的两个SqlNode。

VarDeclSqlNode:处理动态sql标签<bind>的SqlNode类。

public class VarDeclSqlNode implements SqlNode {private final String name;private final String expression;public VarDeclSqlNode(String var, String exp) {name = var;expression = exp;}@Overridepublic boolean apply(DynamicContext context) {final Object value = OgnlCache.getValue(expression, context.getBindings());// 由于没有sql可append,仅是把bind标签的变量名和值保存至上下文参数列表内context.bind(name, value);return true;}
}

MixedSqlNode:意为混合的SqlNode,它保存了其他多种SqlNode的集合,可以看做是一个List<SqlNode>列表,事实也确实如此。

DynamicSqlSource中的SqlNode rootSqlNode属性,通常都是MixedSqlNode对象(完全是静态sql时,可能是一个StaticTextSqlNode),而MixedSqlNode对象又保存了所有的List<SqlNode>集合,这也是通过一个rootSqlNode,就能找到所有SqlNode的深层原因。

4. SqlNode的组合设计模式

public class ForEachSqlNode implements SqlNode {private SqlNode contents;
}

SqlNode,采用了组合设计模式,组合设计模式可以用来表示经典的树型结构,有人不禁要问,组合设计模式,它的属性,应该List<SqlNode>集合,怎么会是单一的SqlNode呢?

前面说的MixedSqlNode,就代表了List<SqlNode>集合,所以,它是换汤不换药的经典组合设计模式。

举例:ForEachSqlNode内部,可能是一个StaticTextSqlNode,看XML就一目了然。

<foreach collection="list" item="item" index="index" separator=","> (#{item.studId},#{item.name},#{item.email},#{item.dob}, #{item.phone})
</foreach>

5. NodeHandler

SqlNode是由NodeHandler创建出来的。

(Made In EDrawMax)

  private class ChooseHandler implements NodeHandler {public ChooseHandler() {// Prevent Synthetic Access}@Overridepublic void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {List<SqlNode> whenSqlNodes = new ArrayList<SqlNode>();List<SqlNode> otherwiseSqlNodes = new ArrayList<SqlNode>();handleWhenOtherwiseNodes(nodeToHandle, whenSqlNodes, otherwiseSqlNodes);SqlNode defaultSqlNode = getDefaultSqlNode(otherwiseSqlNodes);ChooseSqlNode chooseSqlNode = new ChooseSqlNode(whenSqlNodes, defaultSqlNode);targetContents.add(chooseSqlNode);}private void handleWhenOtherwiseNodes(XNode chooseSqlNode, List<SqlNode> ifSqlNodes, List<SqlNode> defaultSqlNodes) {List<XNode> children = chooseSqlNode.getChildren();for (XNode child : children) {String nodeName = child.getNode().getNodeName();NodeHandler handler = nodeHandlers(nodeName);if (handler instanceof IfHandler) {handler.handleNode(child, ifSqlNodes);} else if (handler instanceof OtherwiseHandler) {handler.handleNode(child, defaultSqlNodes);}}}
//...
}

上面的例子,可以清楚看出,<choose>标签是和<when>、<otherwise>标签配合使用的,创建ChooseSqlNode时,就同时创建了when、otherwise的逻辑,而when会转换为if标签处理,otherwise则转换为SqlNode处理,一般是StaticTextSqlNode。

map.put("if", new IfHandler());
map.put("when", new IfHandler());

因篇幅问题,我们不再逐一描述,读者可自行查看。

6. LanguageDriver

LanguageDriver是一个辅助工具类,用于创建SqlSource。

(Made In IntelliJ Idea IDE)
XMLLanguageDriver:用于创建动态、静态SqlSource。

RawLanguageDriver:在确保只有静态sql时,可以使用,不得含有任何动态sql的内容,否则,请使用XMLLanguageDriver。它其实是对XMLLanguageDriver创建的结果进行唯静态sql检查而已,发现有动态sql的内容,就抛异常。

/*** As of 3.2.4 the default XML language is able to identify static statements* and create a {@link RawSqlSource}. So there is no need to use RAW unless you* want to make sure that there is not any dynamic tag for any reason.* * @since 3.2.0* @author Eduardo Macarron*/
public class RawLanguageDriver extends XMLLanguageDriver {@Overridepublic SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {SqlSource source = super.createSqlSource(configuration, script, parameterType);checkIsNotDynamic(source);return source;}
// ...
}

总结:本篇博文,介绍了Mybatis动态sql的基本结构,基本用法,基本设计原理,下一篇将仔细分析Mybatis使用OGNL计算表达式以及处理#{name}和${name}的时机和它们之间的异同。面试过程中,面试官可能会问你在Mybatis中,#{name}和${name}有什么区别。

版权提示:文章出自开源中国社区,若对文章感兴趣,可关注我的开源中国社区博客(http://my.oschina.net/zudajun)。(经过网络爬虫或转载的文章,经常丢失流程图、时序图,格式错乱等,还是看原版的比较好)

转载于:https://my.oschina.net/zudajun/blog/735553

Mybatis3.4.x技术内幕(十七):Mybatis之动态Sql设计原本(上)相关推荐

  1. jQuery技术内幕:深入解析jQuery架构设计与实现原理

    为什么80%的码农都做不了架构师?>>>    jQuery技术内幕:深入解析jQuery架构设计与实现原理 本书由阿里巴巴资深前端开发工程师撰写,从源代码角度全面而系统地解读了jQ ...

  2. jQuery技术内幕:深入解析jQuery架构设计与实现原理1

    jQuery技术内幕:深入解析jQuery架构设计与实现原理 高 云 著 图书在版编目(CIP)数据 jQuery技术内幕:深入解析jQuery架构设计与实现原理 / 高云著. -北京:机械工业出版社 ...

  3. 《MSSQL2008技术内幕:T-SQL语言基础》读书笔记(上)

    索引: 一.SQL Server的体系结构 二.查询 三.表表达式 四.集合运算 五.透视.逆透视及分组 六.数据修改 七.事务和并发 八.可编程对象 一.SQL Server体系结构 1.1 数据库 ...

  4. Hadoop技术内幕:深入解析MapReduce架构设计与实现原理 (大数据技术丛书) - 电子书下载(高清版PDF格式+EPUB格式)...

    Hadoop技术内幕:深入解析MapReduce架构设计与实现原理 (大数据技术丛书)-董西成著 在线阅读                   百度网盘下载(ihhy) 书名:Hadoop技术内幕:深 ...

  5. Hadoop技术内幕:深入解析MapReduce架构设计与实现原理

    <Hadoop技术内幕:深入解析MapReduce架构设计与实现原理> 基本信息 作者: 董西成 丛书名: 大数据技术丛书 出版社:机械工业出版社 ISBN:9787111422266 上 ...

  6. 9、mybatis中动态sql的使用

    对于初学者,如何进行mybatis的学习呢?我总结了几点,会慢慢的更新出来.首先大家需要了解mybatis是什么.用mybatis来做什么.为什么要用mybatis.有什么优缺点:当知道了为什么的时候 ...

  7. java day55【 Mybatis 连接池与事务深入 、 Mybatis 的动态 SQL 语句、 Mybatis 多表查询之一对多 、 Mybatis 多表查询之多对多】...

    第1章 Mybatis 连接池与事务深入 1.1 Mybatis 的连接池技术 1.1.1 Mybatis 连接池的分类 1.1.2 Mybatis 中数据源的配置 1.1.3 Mybatis 中 D ...

  8. 2022/5/1 Mybatis框架动态SQL

    目录 1丶动态 SQL 2丶if标签 3丶choose.when.otherwise 4丶trim.where.set 5丶foreach 6丶script 7丶bind 8丶多数据库支持 9丶动态 ...

  9. MyBatis 03 动态SQL

    MyBatis 03 动态SQL 文章目录 MyBatis 03 动态SQL 一.学习目标 二.动态SQL if 标签2-1 if 标签2-2 where标签2-1 where 标签2-2 choos ...

  10. MyBatis的动态SQL详解

    MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑. MyBatis中用于实现动态SQL的元素主要有:   if choose(when,otherwis ...

最新文章

  1. crontab脚本,定时任务相关细节
  2. STM32:从菜鸟到牛人就是如此简单!为了学习单片机而去学习单片机的思路是不对的
  3. C++与C#混合编程
  4. sharepoint_study_5
  5. 平板电脑黑苹果EFI_保姆级别教你安装黑苹果,提供大量EFI与工具驱动!
  6. 查看电脑显卡(GPU)是否支持CUDA
  7. 智协云店通+BitCOO加入4WiN.io全球互贸链 | 翼次元空间+Fund++
  8. 深度之眼Pytorch打卡(六):将数据集切分成训练集、验证集和测试集的方法
  9. dropbox 怎么使用_如何在一台PC上使用多个Dropbox帐户
  10. 艺体计算机教师考核细则,音体美教师考核办法
  11. 阐明iOS证书和provision文件
  12. python判断手机号运营商_基于python的-使用正则表达式验证手机号并匹配运营商和所述地域...
  13. 避雷秘籍:iOS过审的基础条件
  14. php实现个人微信给自己好友发送消息
  15. Jackknife 刀切法
  16. python 直方图均衡化
  17. OpenJ_Bailian - 2977 生理周期 【枚举】
  18. AnnotationConfigApplicationContext@51016012 has not been refreshed yet
  19. Android Studio中no module问题
  20. 简单介绍一下经济学中 比较优势 的概念

热门文章

  1. 模仿c#Func和Action的函数指针模板
  2. 简单搞一下 Oracle 存储过程测试!
  3. 网页英文字体和中文字体应用
  4. 颜色空间——Gamma与线性颜色空间
  5. 9.11排序与查找(二)——对字符串数组进行排序,将全部的变位词排在相邻的位置...
  6. GoF23种设计模式之创建型模式之建造者模式
  7. Local Database Overview for Windows Phone
  8. 相机标定—了解相机模型
  9. [贪心][模拟] Jzoj P5811 简单的填数
  10. 经纬度转换为三维坐标