Mybatis的SqlSource SqlNode BoundSql

news2024/11/14 18:32:43

学习链接

MyBatis SqlSource解析

【Mybatis】Mybatis源码之SqlSource#getBoundSql获取预编译SQL

Mybatis中SqlSource解析流程详解

Mybatis TypeHandler解析

图解

Mybatis的SqlSource&SqlNode - processon

在这里插入图片描述

DynamicSqlSource

public class DynamicSqlSource implements SqlSource {

  	private final Configuration configuration;
  	
  	private final SqlNode rootSqlNode;

  	public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
    	this.configuration = configuration;
    	this.rootSqlNode = rootSqlNode;
  	}
  	
	/**
	 * 获取一个BoundSql对象
	 *
	 * DynamicSqlSource和 RawSqlSource都会转化为 StaticSqlSource,然后才能给出一个 BoundSql对象。
	 *
	 * @param parameterObject 参数对象
	 * @return BoundSql对象
	 */
	@Override
	public BoundSql getBoundSql(Object parameterObject) {
	
	    // 创建DynamicSqlSource的辅助类,用来记录DynamicSqlSource解析出来的SQL片段信息和参数信息
	    DynamicContext context = new DynamicContext(configuration, parameterObject);
	    
	    // 这里会从根节点开始,对节点逐层调用apply方法,经过这一步后,动态节点"${}"都被替换,这样 DynamicSqlSource便不再是动态的,而是静态的。
	    rootSqlNode.apply(context);
	    
	    // 处理占位符,汇总参数信息
	    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);	    
	    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
	    
	    // 使用SqlSourceBuilder处理"#{}",将其转化为"?",然后创建ParameterMapping,最终生成了StaticSqlSource对象
	    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
	    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
	    
	    // 把context.getBindings()的参数放到boundSql的metaParameters中进行保存
	    context.getBindings().forEach(boundSql::setAdditionalParameter);
	    
	    return boundSql;
	}

}	

RawSqlSource

通过观察RawSqlSource与DynamicSqlSource的区别,可以注意到:

  1. 首先需要判定在mapper.xml中写的一个sql标签语句到底是动态sqlSource还是rawSqlSource?这个在XMLScriptBuilder#parseDynamicTags中有解析过程,凡是含有${} 或者 还有动态标签(如trim,where,if等9个标签)的 的都是动态的
  2. DynamicSqlSource和RawSqlSource有什么共同点和区别?它们都有解析阶段 和 运行阶段,在解析阶段时,RawSqlSource在构造方法中就已经得到了StaticSqlSource了(因为里面没有需要动态解析的内容了,注意#{}不属于动态解析的范畴,只有那9个动态标签和${}才算动态的),而DynamicSqlSource需要在运行阶段根据传参,才能获得StaticSqlSource,需要根据实际传参才能确定sql,所以叫动态嘛。所以它们最终都会获取StaticSqlSource,参与后面的流程。
public class RawSqlSource implements SqlSource {

  private final SqlSource sqlSource;

  public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) {
    this(configuration, getSql(configuration, rootSqlNode), parameterType);
  }

  public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> clazz = parameterType == null ? Object.class : parameterType;
    sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
  }

  private static String getSql(Configuration configuration, SqlNode rootSqlNode) {
    DynamicContext context = new DynamicContext(configuration, null);
    rootSqlNode.apply(context);
    return context.getSql();
  }

  @Override
  public BoundSql getBoundSql(Object parameterObject) {
    return sqlSource.getBoundSql(parameterObject);
  }

}

GenericTokenParser#parse

/**
 * 该方法主要 完成占位符的定位工作,然后把占位符的替换工作交给与其关联的 TokenHandler 处理
 * @param text
 * @return
 */
public String parse(String text) {
    if (text == null || text.isEmpty()) {
        return "";
    }
    // search open token
    // 查找openToken的位置
    int start = text.indexOf(openToken);
    if (start == -1) {
        return text;
    }
    char[] src = text.toCharArray();
    int offset = 0;
    final StringBuilder builder = new StringBuilder();
    StringBuilder expression = null;
    // 当存在openToken时,才继续处理
    while (start > -1) {
        if (start > 0 && src[start - 1] == '\\') {
            // this open token is escaped. remove the backslash and continue.
            builder.append(src, offset, start - offset - 1).append(openToken);
            offset = start + openToken.length();
        } else {
            // found open token. let's search close token.
            if (expression == null) {
                expression = new StringBuilder();
            } else {
                expression.setLength(0);
            }
            // 拼接从0到openToken之前的字符
            builder.append(src, offset, start - offset);
            // 设置offset值为openToken结束的位置
            offset = start + openToken.length();
            // 从offset值之后开始找第一个closeToken的位置
            int end = text.indexOf(closeToken, offset);
            // 如果存在,则继续处理
            while (end > -1) {
                if (end > offset && src[end - 1] == '\\') {
                    // this close token is escaped. remove the backslash and continue.
                    expression.append(src, offset, end - offset - 1).append(closeToken);
                    offset = end + closeToken.length();
                    // 继续查找当前closeToken之后的closeToken
                    end = text.indexOf(closeToken, offset);
                } else {
                    expression.append(src, offset, end - offset);
                    break;
                }
            }
            // 如果不存在
            if (end == -1) {
                // close token was not found.
                // 拼接剩余的字符
                builder.append(src, start, src.length - start);
                // 设置offset为字符数组的长度
                offset = src.length;
            } else {
                /**
                 * DynamicCheckerTokenParser:如果存在,则设置当前SQL为动态的
                 * BindingTokenParser:获取$变量的值
                 * ParameterMappingTokenHandler:将#替换为?,并构建参数映射ParameterMapping
                 */
                builder.append(handler.handleToken(expression.toString()));
                // 设置offset值为closeToken结束的位置
                offset = end + closeToken.length();
            }
        }
        start = text.indexOf(openToken, offset);
    }
    // 拼接剩余的字符
    if (offset < src.length) {
        builder.append(src, offset, src.length - offset);
    }
    return builder.toString();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/901669.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【leetcode 力扣刷题】哈希表初尝试

哈希表 刷题初尝试 哈希表基础知识242. 有效的字母异位词383. 赎金信49. 字母异位词分组438. 找到字符串中所有字母异位词 哈希表基础知识 哈希表是一种数据结构&#xff0c;也叫散列表。哈希表中存储的是键值对&#xff0c;即(key&#xff0c;value)&#xff0c;根据key直接查…

7-1 选择法排序

分数 20 全屏浏览题目 切换布局 作者 C课程组 单位 浙江大学 本题要求将给定的n个整数从大到小排序后输出。 输入格式&#xff1a; 输入第一行给出一个不超过10的正整数n。第二行给出n个整数&#xff0c;其间以空格分隔。 输出格式&#xff1a; 在一行中输出从大到小有序…

openai多模态大模型:clip详解及使用

引言 CLIP全称Constrastive Language-Image Pre-training&#xff0c;是OpenAI推出的采用对比学习的文本-图像预训练模型。CLIP惊艳之处在于架构非常简洁且效果好到难以置信&#xff0c;在zero-shot文本-图像检索&#xff0c;zero-shot图像分类&#xff0c;文本→图像生成任务…

JAVA基础知识(五)——面向对象(中)

面向对象&#xff08;中&#xff09; 一、面向对象特征之一&#xff1a;封装与隐藏1.1 简介1.2 封装性的体现1.3 四种访问权限修饰符 二、类的成员之三&#xff1a;构造器2.1 构造器的特征2.2 构造器的作用2.3 语法格式2.4 构造器分类2.5 构造器重载2.6 属性赋值过程 三、扩展知…

Python的六种参数?

很多人说&#xff0c;Python的参数类型有四种、五种&#xff0c;我个人认为归纳起来是六种参数&#xff0c;分别为&#xff1a;位置参数&#xff08;Positional Arguments&#xff09;、默认参数&#xff08;Default Arguments&#xff09;、关键字参数&#xff08;Keyword Arg…

[C++11]

文章目录 1. 自动类型推导1.1 auto1.1.1 推导规则1.1.2 auto的限制1.1.3 auto的应用1.1.4 范围for 1.2 decltype1.2.1 推导规则1.2.2 decltype的应用 1.3 返回类型后置 2.可调用对象包装器、绑定器2.1 可调用对象包装器2.1.1 基本用法2.1.2 作为回调函数使用 2.2 绑定器 3. usi…

系统架构设计专业技能 · 信息系统基础

系列文章目录 系统架构设计专业技能 网络技术&#xff08;三&#xff09; 系统架构设计专业技能 系统安全分析与设计&#xff08;四&#xff09;【系统架构设计师】 系统架构设计高级技能 软件架构设计&#xff08;一&#xff09;【系统架构设计师】 系统架构设计高级技能 …

vue 关闭prettier警告warn

这个就是我们创建vue cli的时候 把这个给默认上了 关闭这个只需在.eslintrc.js页面里边添加一行代码"prettier/prettier": "off"

Mac OS 中JDK 环境(jdk 1.8.0_831)安装配置、环境变量配置及卸载操作

前言&#xff1a; 摊牌了&#xff0c;本来就有点喜新厌旧的我&#xff0c;特意把系统和开发环境都拉到比较高&#xff0c;想试验一下兼容性和某些新特性&#xff0c;探索了一下新大陆&#xff0c;也见识了各种光怪陆离的妖魔鬼怪。 因为要着手云平台项目的重构改版和新系统的架…

5G技术与其对智能城市、物联网和虚拟现实领域的影响

随着第五代移动通信技术&#xff08;5G&#xff09;的到来&#xff0c;我们即将迈向一个全新的数字化世界。5G技术的引入将带来更高的速度、更低的延迟和更大的连接性&#xff0c;推动了智能城市、物联网和虚拟现实等领域的发展。 首先&#xff0c;5G技术将带来超越以往的网络速…

global 可不能随便用啊!寻找1-10000内的 Spyder 数遇到的小问题,却是大思考

今天遇到这样一个问题&#xff1a;寻找1-10000内的 Spyder 数&#xff0c;Spyder 数是指&#xff0c;如果将一个数的每个位上的数字相乘&#xff0c;再将这些乘积相加&#xff0c;最终得到的和等于原始数&#xff0c;那么该数就是 Spyder 数。 逻辑分析 这个逻辑其实很简单&…

软考A计划-系统集成项目管理工程师-法律法规-上

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

vulnhub靶机DarkHole_2

靶机下载地址&#xff1a;DarkHole: 2 ~ VulnHub 靶机发现 arp-scan -l 扫描端口 nmap --min-rate 10000 -p- 192.168.21.145 扫描服务 nmap -sV -sT -O -p22,80 192.168.21.145 漏洞扫描 nmap --scriptvuln -p22,80 192.168.21.145 这里有git源码泄露 git clone mirrors…

浅析DIX与DIF(T10 PI)

文章目录 概述DIF与DIX端到端数据保护 DIFDIF保护类型 SCSI设备支持DIFStandard INQUIRY DataExtended INQUIRY Data VPD pageSPT字段GRD_CHK、APP_CHK、REF_CHK字段 READ CAPACITY(16)响应信息 SCSI命令请求读命令请求写命令请求 DIF盘格式化相关参考 概述 DIF与DIX DIF&…

RK3588平台开发系列讲解(AI 篇)RKNN-Toolkit2 API 介绍

文章目录 一、RKNN 初始化及对象释放二、RKNN 模型配置沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇章主要讲解 RKNN-Toolkit2 API 详细说明。 一、RKNN 初始化及对象释放 在使用 RKNN Toolkit2 的所有 API 接口时,都需要先调用 RKNN()方法初始化 RKNN 对象,…

C++进阶 特殊类的设计

本篇博客介绍&#xff1a;介绍几种特殊的类 特殊类的设计 设计一个类不能被拷贝设计一个类 只能在堆上创建对象设计一个类 只能在栈上创造对象设计一个类不能被继承单例模式饿汉模式懒汉模式单例模式对象的释放问题 总结 设计一个类不能被拷贝 我们的拷贝只会发生在两个场景当…

【3D激光SLAM】LOAM源代码解析--scanRegistration.cpp

系列文章目录 【3D激光SLAM】LOAM源代码解析–scanRegistration.cpp 写在前面 本系列文章将对LOAM源代码进行讲解&#xff0c;在讲解过程中&#xff0c;涉及到论文中提到的部分&#xff0c;会结合论文以及我自己的理解进行解读&#xff0c;尤其是对于其中坐标变换的部分&…

chatGPT-对话爱因斯坦

引言 阿尔伯特爱因斯坦&#xff08; 1879年 3 月 14 日 – 1955 年 4 月 18 日&#xff09;是一位出生于德国的理论物理学家&#xff0c;被广泛认为成为有史以来最伟大、最有影响力的科学家之一。他以发展相对论而闻名&#xff0c;他还对量子力学做出了重要贡献&#xff0c;因…

安装搭建私有仓库Harbor

目录 一、安装docker编排工具docker compose 二、安装Harbor软件包 三、修改配置文件 四、运行安装脚本 五、安装后验证 六、使用Harbor 一、安装docker编排工具docker compose 在github上选择自己想要的版本下载 https://github.com/docker/compose/releases 下载好…

Android AppCompatActivity标题栏操作

使用 AndroidStudio 新建的工程默认用 AppCompatActivity &#xff0c;是带标题栏的。 记录下 修改标题栏名称 和 隐藏标题栏 的方法。 修改标题栏名称 Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R…