Mybatis的sql组装详解

上一篇分析了SqlSession执行sql的过程,其中并没有分析sql是从哪里来的,今天就来仔细分析下。

Sql来源

从上一篇的最后一步执行sql那里倒推sql的来源,源码主要过程如下图:

可以看到最后是通过BoundSql直接获取的sql,然后往前倒推最后发现是通过MappedStatement的getBoundSql方法返回的。MappedStatement在之前分析mapper的时候知道一个执行sql对应一个MappedStatement对象,它封装有mybatis中需要执行一条sql的所有信息,所以从这里获取也是理所应当的。

MappedStatement的getBoundSql方法

那么就来看下MappedStatement的getBoundSql方法吧,源码如下图:

首先是右边MappedStatement的getBoundSql方法,这次改了下sql传递了两个参数,但是对程序基本没什么影响。可以看到BoundSql是通过SqlSource创建的,通过debug知道是DynamicSqlSource对象。

左边是DynamicSqlSource的getBoundSql方法,可以先看后面的创建BoundSql对象,是通过SqlSource初始化的,而创建SqlSource对象需要的第一个参数是通过context.getSql()得到的。

而在getBoundSql方法第一步是初始化了context,从上图的debug可以看到目前context的信息,bindings属性已经通过初始化把参数设置进去,而sqlBuilder还是一个空字符串。关键代码就在当前打断点的“rootSqlNode.apply(context);”这行代码。

从上图可以看到rootSqlNode是一个MixedSqlNode对象,MixedSqlNode对象的apply方法是遍历属性contents的所有元素并执行它们的apply方法,可以看到contents只有一个TextSqlNode对象的元素。所以最终来到TextSqlNode对象。

TextSqlNode对象的text属性保存的就是还没有进行处理的sql。

TextSqlNode处理sql

TextSqlNode处理sql的源码如下图:

右侧是TextSqlNode的apply方法,参数context就是上一步需要的context,他里面包含有请求参数,从上一张源码图可以知道它的属性sqlBuilder此时还是一个空字符串,而apply就是去拼接sqlBuilder。

apply方法首先创建了一个GenericTokenParser对象,GenericTokenParser对象有三个属性openToken、closeToken、handler。其中openToken、closeToken分别对应字符串${},而handler的属性context对应传递进来的context,injectionFilter属性是TextSqlNode的injectionFilter。apply第二行代码“context.appendSql(parser.parse(text));”可以看出来parser.parse(text)就是生成sql的代码。其中text是TextSqlNode中没有处理的原始sql。

所以sql生成在GenericTokenParser的parse方法,parse方法的关键源码如上图左侧。主要步骤分析如下:

1、从sql找到第一个“${”位置start,获取到元素sql(text)的字符数组src;

2、把src从开始到start处的字符拼接到结果builder上,也就是把“${”的内容拼接到结果上;

3、从start+2的位置开始找“}”的位置end,从src数组里取出“${”、“}”之间的字符组成字符串并根据字符串从hander中取出字符串对应的参数值。并把值拼接到结果builder上。

最后还有一点代码没有截取到“start = text.indexOf(openToken,offset);

}while (start > -1);”也就是第4步。

4、继续找到下一个“${”并拼接。最终组成完整的sql。

实际上GenericTokenParser的parse方法还是比较简单的,就是把传递进来的参数text中openToken、closeToken中间的内容替换成对应的参数。

总结

从之前的分析我们知道一个MappedStatement对应一个sql,那么如何从MappedStatement获取组装好的sql呢,实际上是MappedStatement的属性sqlSource。

而sqlSource实际上是依靠rootSqlNode,在之前我们分析过解析mapper文件是把sql生成了嵌套的各种SqlNode子类。今天就看到他们的使用了。不过今天的算是比较简单的,后面来一个稍微复杂一点的看看sql的解析过程。

Java程序员日常学习笔记,如理解有误欢迎各位交流讨论!

(0)

相关推荐

  • SpringBoot实现用户统一管理与单点登陆

    SpringBoot实现用户统一管理与单点登陆前言最近在开发产品的过程中,需要将业务功能拆分成独立子系统,既可以单独使用也可以集成部署,这里就需要对框架进行扩展,支持用户统一管理与单点登陆.我们的基础 ...

  • 图解MyBatis

    回复"面试"获取全套面试资料 在以前文章中,我们对Mybatis进行了入门级的介绍教小师妹快速入门Mybatis,看这篇就够了,今天我们来从一个全局的角度看看Mybatis. 本文 ...

  • 阿里面试:Mybatis中方法和SQL是怎么关联起来的呢?

    回复"面试"获取全套面试资料 本文:3126字 | 阅读时长:4分10秒 今天是Mybatis源码分析第四篇,也是最后一篇. 老规矩,先上案例代码: public class My ...

  • 电动车组装详解(一)

    电动车组装详解(一) 1.电动车结构: 车体. 电驱动装置(电机). 可充电电池. 充电器和控制系统五大部分组成. (一)车体:电动车的车体五花八门,直观的感觉就是从外形来区分,电动独轮车,电动滑板车 ...

  • 6000字!SQL窗口函数详解。

    大家好,我是宝器! 今天想重提窗口函数.原因是前几天在群里提起了这个名字,忘了是什么缘由提起的,但令我吃惊的是,竟还有同学想从事数据分析却不知道窗口函数!那感觉就仿佛用勺子吃面条不知道有筷子这种好东西 ...

  • SQL注入详解

    SQL注入详解 一:什么是sql注入 SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编写时的疏忽,通过SQL语句,实现无账号登录,甚至篡改数据库. 二:S ...

  • (三)MyBatis从入门到入土——使用详解

    MyBatis使用详解 上篇我们手动开发了一个MyBatis项目,但是我们仅仅是编写了代码,对于整个项目是如何运行以及每个代码的意义都没有仔细的分析和说明,那么接下来我们就开始分析每个代码的意义以及如 ...

  • SQL之CASE WHEN用法详解

    简单CASE WHEN函数:CASE SCORE WHEN 'A' THEN '优' ELSE '不及格' ENDCASE SCORE WHEN 'B' THEN '良' ELSE '不及格' END ...

  • MS SQL Server 备份与恢复详解

    MS SQL Server 备份与恢复详解

  • SQL执行效率优化:效率提升几万倍的操作详解

                              本文介绍的是SQL执行效率优化的详细操作,相关内容请点击:SQL执行效率的优化.我用的数据库是MySQL5.6,下面简单的介绍下场景场景本文介绍的是 ...

  • 写了10年的代码,我最怕写Mybatis这些配置,现在有详解了

    在使用 mybatis 过程中, 当手写 JavaBean和XML 写的越来越多的时候, 就越来越同意出错.这种重复性的工作, 我们当然不希望做那么多. 还好, mybatis 为我们提供了强大的代码 ...

  • spring与mybatis整合详解

    spring与mybatis整合详解 在数据库dbmis中创建student表: 建立Maven项目,创建Dao层,POJO层,Controller层,并配置Mapper.applicationCon ...