点击上方蓝色“程序猿DD”,选择“设为星标”

回复“资源”获取独家整理的学习资料!

来源 | https://my.oschina.net/zudajun/blog/687326

今天,我们将分析Mybatis之sqlFragment,可以翻译为sql片段,它的存在价值在于可复用sql片段,避免到处重复编写。

在工作中,往往有这样的需求,对于同一个sql条件查询,首先需要统计记录条数,用以计算pageCount,然后再对结果进行分页查询显示,看下面一个例子。

<sql id="studentProperties"><!--sql片段-->select stud_id as studId, name, email, dob, phonefrom students</sql><select id="countAll" resultType="int">select count(1) from (<include refid="studentProperties"></include><!--复用-->) tmp</select><select id="findAll" resultType="Student" parameterType="map">select * from (<include refid="studentProperties"></include><!--复用-->) tmp limit #{offset}, #{pagesize}</select>

这就是sqlFragment,它可以为select|insert|update|delete标签服务,可以定义很多sqlFragment,然后使用include标签引入多个sqlFragment。在工作中,也是比较常用的一个功能,它的优点很明显,复用sql片段,它的缺点也很明显,不能完整的展现sql逻辑,如果一个标签,include了四至五个sqlFragment,其可读性就非常差了。

sqlFragment里的内容是可以随意写的,它不需要是一个完整的sql,它可以是“,phone”这么简单的文本。

1.sqlFragment的解析过程

sqlFragment存储于Configuration内部。

protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");

解析sqlFragment的过程非常简单。

org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement(XNode)方法部分源码。

// 解析sqlFragment
sqlElement(context.evalNodes("/mapper/sql"));
// 为select|insert|update|delete提供服务
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));

sqlFragment存储于Map<String, XNode>结构当中。其实最关键的,是它如何为select|insert|update|delete提供服务的。

2.select|insert|update|delete标签中,解析include标签的过程

org.apache.ibatis.builder.xml.XMLStatementBuilder.parseStatementNode()方法源码。

// Include Fragments before parsing
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
// 重点关注的方法
includeParser.applyIncludes(context.getNode());// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);

注释“pre: <selectKey> and <include> were parsed and removed”,含义为解析完,并移除。为什么要移除呢?秘密都隐藏在applyIncludes()方法内部了。

org.apache.ibatis.builder.xml.XMLIncludeTransformer.applyIncludes(Node, Properties)方法源码。

  /*** Recursively apply includes through all SQL fragments.* @param source Include node in DOM tree* @param variablesContext Current context for static variables with values*/private void applyIncludes(Node source, final Properties variablesContext) {if (source.getNodeName().equals("include")) {// new full context for included SQL - contains inherited context and new variables from current include nodeProperties fullContext;String refid = getStringAttribute(source, "refid");// replace variables in include refid valuerefid = PropertyParser.parse(refid, variablesContext);Node toInclude = findSqlFragment(refid);Properties newVariablesContext = getVariablesContext(source, variablesContext);if (!newVariablesContext.isEmpty()) {// merge contextsfullContext = new Properties();fullContext.putAll(variablesContext);fullContext.putAll(newVariablesContext);} else {// no new context - use inherited fullyfullContext = variablesContext;}// 递归调用applyIncludes(toInclude, fullContext);if (toInclude.getOwnerDocument() != source.getOwnerDocument()) {toInclude = source.getOwnerDocument().importNode(toInclude, true);}// 将include节点,替换为sqlFragment节点source.getParentNode().replaceChild(toInclude, source);while (toInclude.hasChildNodes()) {// 将sqlFragment的子节点(也就是文本节点),插入到sqlFragment的前面toInclude.getParentNode().insertBefore(toInclude.getFirstChild(), toInclude);}// 移除sqlFragment节点toInclude.getParentNode().removeChild(toInclude);} else if (source.getNodeType() == Node.ELEMENT_NODE) {NodeList children = source.getChildNodes();for (int i=0; i<children.getLength(); i++) {// 递归调用applyIncludes(children.item(i), variablesContext);}} else if (source.getNodeType() == Node.ATTRIBUTE_NODE && !variablesContext.isEmpty()) {// replace variables in all attribute valuessource.setNodeValue(PropertyParser.parse(source.getNodeValue(), variablesContext));} else if (source.getNodeType() == Node.TEXT_NODE && !variablesContext.isEmpty()) {// replace variables ins all text nodessource.setNodeValue(PropertyParser.parse(source.getNodeValue(), variablesContext));}}

上面是对源码的解读,为了便于理解,我们接下来采用图示的办法,演示其过程。

3.图示过程演示

①解析节点

<select id="countAll" resultType="int">select count(1) from (<include refid="studentProperties"></include>) tmp</select>

②include节点替换为sqlFragment节点

<select id="countAll" resultType="int">select count(1) from (<sql id="studentProperties">select stud_id as studId, name, email, dob, phonefrom students</sql>
) tmp</select>

③将sqlFragment的子节点(文本节点)insert到sqlFragment节点的前面。注意,对于dom来说,文本也是一个节点,叫TextNode。

<select id="countAll" resultType="int">select count(1) from (select stud_id as studId, name, email, dob, phonefrom students<sql id="studentProperties">select stud_id as studId, name, email, dob, phonefrom students</sql>
) tmp</select>

④移除sqlFragment节点

<select id="countAll" resultType="int">select count(1) from (select stud_id as studId, name, email, dob, phonefrom students
) tmp</select>

⑤最终结果如图所示

(Made In QQ截图及时编辑)

如此一来,TextNode1 + TextNode2 + TextNode3,就组成了一个完整的sql。遍历select的三个子节点,分别取出TextNode的value,append到一起,就是最终完整的sql。

这也是为什么要移除<selectKey> and <include>节点的原因。

这就是Mybatis的sqlFragment,以上示例,均为静态sql,即static sql。

往期推荐

扛住100亿次请求?我们来试一试!

SpringBoot + Mybatis + Druid + PageHelper 实现多数据源分页

Java 中的 BigDecimal,你真的会用吗?

华为阿里下班时间曝光:所有的光鲜,都有加班的味道

MySQL 的 Binlog 日志处理工具(Canal,Maxwell,Databus,DTS)对比

Serverless:为我们到底带来了什么

星球限时拼团优惠进行中

我的星球是否适合你?

点击阅读原文看看我们都聊过啥?

Mybatis是如何实现SQL语句复用功能的?相关推荐

  1. sql if 和insert_拼多多面试:Mybatis是如何实现SQL语句复用功能的?

    在工作中,往往有这样的需求,对于同一个sql条件查询,首先需要统计记录条数,用以计算pageCount,然后再对结果进行分页查询显示,看下面一个例子. <sql id="student ...

  2. MyBatis超详细介绍——SQL语句构建器类

    MyBatis超详细介绍--SQL语句构建器类 (本文作为学习笔记,了解更多请参考:MyBatis参考文档) MyBatis3提供了SQL类帮助构造SQL语句: private String sele ...

  3. Mybatis映射文件动态SQL语句-01

    因为在很多业务逻辑复杂的项目中,往往不是简单的sql语句就能查询出来自己想要的数据,所有mybatis引入了动态sql语句, UserMapper.xml <?xml version=" ...

  4. mybatis log4j 在日志中打印sql_SpringBoot整合MyBatis+详细打印执行SQL语句

    图片来源网路 为啥写这篇文章呢,有人可能会说springBoot的mybatis的starter都有了写这篇文章不是多此一举吗?难道是真的吗?其实我一开始也是使用mybatis的starter的,但是 ...

  5. Mybatis 开启控制台打印sql语句

    概述 springboot+mybatis整合过程中,开启控制台sql语句打印的多种方式: 方法1 在springboot+mybatis整合中,可以将springboot的配置文件添加如下一段也可: ...

  6. 使用Mybatis相关类生成sql语句

    项目中有时需要实现动态sql功能(非mybatis动态sql片段): sql语句中可以使用mybatis的各种标签,注意<>等符号的转义,最后会根据输入的参数生成最终的sql,需要引入my ...

  7. 黑马程序员视频教程学习mybatis框架常用注释SQL语句学习笔记?

    mybatis学习笔记 常用注释增删改查SQL语句 常用注释拓展SQL语句 解决实体类属性和数据库表中的属性名称不相同的问题: mybatis注解之一对一查询: mybatis注解之一对多查询: my ...

  8. Mybatis拦截器修改sql语句

    拦截器介绍 MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能. MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用.默认情况下,MyBatis 允许 ...

  9. 解决Mybatis结合db2时sql语句换行出现的问题

    最近项目用到mybatis3.1和db28.1版本的数据库,出现一个问题,我研究半天把mybatis源码给改了,竟然成功解决. 直接入正题:在mybatis映射文件中写sql语句时如果有折行情况就报错 ...

最新文章

  1. 新闻智能分类练习赛开始报名啦!最先达到80分就可以领GPU,技术书籍!
  2. 2020移动apn接入点哪个快_为什么都是4G网你的就没别人快?跟我这样设置,网速直线提升...
  3. Array.splice()--删除数组中重复的数据
  4. SAP Fiori Elements - smart filter bar study - filter display logic
  5. c语言程序输入n个数字排序,输入n个数字然后进行排序,用C语言编写。注意是n个数啊,不是确定的个数。...
  6. 右键单击文件夹进入命令行窗口
  7. java sql分页_mysql、sqlserver、oracle分页,java分页统一接口实现
  8. MYSQL返回指定时间间隔函数DATE_SUB和TO_DAYS详解
  9. java ArrayList的实现
  10. java基础——Vector集合知识点
  11. 第2章[2.2] Ext JS多类型终端-电脑、移动端(手机、平板)
  12. python3除法运算_Python2和Python3中除法操作/的不同
  13. sql取整数_SQL高级功能
  14. r语言作业:出租车数据基础数据分析、时间处理等
  15. 部分大学上演造假运动闯关本科评估(图)
  16. 做支付行业这些支付方式你需要知道!
  17. 在n*n方阵里填入1,2,...n*n,要求填成蛇形
  18. Synergy:亚马逊云服务(AWS)在亚太地区排名第一
  19. win7在网上邻居上看不到别的电脑如何解决
  20. ERD Commander 2005 Boot CD 剖析及汉化试验

热门文章

  1. linux 命令输出 保存到文件 日志记录
  2. golang 代码安全审计
  3. Linux里如何查找文件内容 grep
  4. linux kernel makefile analysis
  5. fast system call 快速系统调用
  6. Xen与KVM虚拟化技术调研报告
  7. dellr320故障_dell r320服务器,idrac问题
  8. eBPF学习——抓取内核网络中的socket信息
  9. 计算机专业知识考试 一,计算机专业知识试题(事业单位计算机专业知识考试试卷)(1)...
  10. matlab 图像显著性检测ft_图像显著性检测总结