【源码解析】MyBatis动态SQL

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 你知道Mybatis动态sql是做什么,都有哪些?动态sql的执行原理吗?

 文章较长,建议收藏】本文解析Mybatis动态sql源码以及动态sql的执行原理

Mybatis系列文章:

【深度好文】谈谈你对MyBatis的理解:MyBatis整体架构

【源码解析】谈谈你对 MyBatis工作原理 的理解 : MyBatis 工作流程源码解析

【源码解析】谈谈你对 MyBatis结果集映射和参数绑定 的理解:MyBatis结果集映射源码解析,详细分析了 handleRowValuesForSimpleResultMap() 等方法实现映射的核心步骤

目录

动态 SQL 语句解析

Mybatis动态sql是做什么的?都有哪些动态sql?

动态sql的执行原理

OGNL 表达式语言

DynamicContext 源码解析

SqlNode 源码解析

动态 SQL 语句解析

这部分我们要知道Mybatis动态sql是做什么的?都有哪些动态sql?动态sql的执行原理?

Mybatis动态sql是做什么的?都有哪些动态sql?

Mybatis动态sql可以让我们在 xml 映射文件内,以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能,Mybatis提供了9种 动态sql标签 trim、where、set、foreach、if、choose、when、otherwise、bind。

动态sql的执行原理

       执行原理,MyBatis 会将 Mapper 映射文件中定义的 SQL 语句解析成 SqlSource 对象,其中的动态标签、SQL 语句文本等,会解析成对应类型的 SqlNode 对象包含了 “${}”占位符的动态 SQL 片段;动态 SQL 语句中使用到了 OGNL 表达式,读写 JavaBean 属性值、执行 JavaBean 方法这两个基础功能。

OGNL 表达式语言

       OGNL 表达式是相对完备的一门表达式语言,我们可以通过 “对象变量名称.方法名称(或属性名称)” 调用一个 JavaBean 对象的方法(或访问其属性),还可以通过 “@[类的完全限定名]@[静态方法(或静态字段)]” 调用一个 Java 类的静态方法(或访问静态字段)。

       MyBatis 为了提高 OGNL 表达式的工作效率,添加了一层 OgnlCache 来缓存表达式编译之后的结果(不是表达式的执行结果),OgnlCache 通过一个 ConcurrentHashMap<String, Object> 类型的集合(expressionCache 字段,静态字段)来记录OGNL 表达式编译之后的结果。通过缓存拿到表达式编译的结果之后,OgnlCache 底层还会依赖上述示例中的 OGNL 工具类以及 OgnlContext 完成表达式的执行。

DynamicContext 源码解析

在 MyBatis 解析一条动态 SQL 语句的时候,整个流程非常长,其中涉及多层方法的调用、方法的递归、复杂的循环等,其中产生的中间结果需要有一个地方进行存储,那就是 DynamicContext 上下文对象

       DynamicContext 中有两个核心属性:一个是 sqlBuilder 字段(StringJoiner 类型),用来记录解析之后的 SQL 语句;另一个是 bindings 字段,用来记录上下文中的一些 KV 信息。        

       DynamicContext 定义了一个 ContextMap 内部类,ContextMap 用来记录运行时用户传入的、用来替换“#{}”占位符的实参。在 DynamicContext 构造方法中,会根据传入的实参类型决定如何创建对应的 ContextMap 对象,核心代码如下:

public DynamicContext(Configuration configuration, Object parameterObject) {
    if (parameterObject != null && !(parameterObject instanceof Map)) {
        // 对于非Map类型的实参,会创建对应的MetaObject对象,并封装成ContextMap对象
        MetaObject metaObject = configuration.newMetaObject(parameterObject);
        boolean existsTypeHandler = configuration.getTypeHandlerRegistry().hasTypeHandler(parameterObject.getClass());
        bindings = new ContextMap(metaObject, existsTypeHandler);
    } else {
        // 对于Map类型的实参,这里会创建一个空的ContextMap对象
        bindings = new ContextMap(null, false);
    }
    // 这里实参对应的Key是_parameter
    bindings.put(PARAMETER_OBJECT_KEY, parameterObject);
    bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());
}

image.gif

读取这个 ContextMap 的地方主要是在 OGNL 表达式中,也就是在 DynamicContext 中定义了一个静态代码块,指定了 OGNL 表达式读写 ContextMap 集合的逻辑,这部分读取逻辑封装在 ContextAccessor 中。

public class DynamicContext {
  public static final String PARAMETER_OBJECT_KEY = "_parameter";
  static {
    OgnlRuntime.setPropertyAccessor(ContextMap.class, new ContextAccessor());
  }
  static class ContextAccessor implements PropertyAccessor {
    @Override
    public Object getProperty(Map context, Object target, Object name)
        throws OgnlException {
      Map map = (Map) target;
      // getProperty() 方法会将传入的 target 参数(实际上就是 ContextMap)转换为 Map
      // 并先尝试按照 Map 规则进行查找
      Object result = map.get(name);
      if (map.containsKey(name) || result != null) {
        return result;
      }
      // 查找失败之后,会尝试获取“_parameter”对应的 parameterObject 对象
      // 从 parameterObject 中获取指定的 Value 值。
      Object parameterObject = map.get(PARAMETER_OBJECT_KEY);
      if (parameterObject instanceof Map) {
        return ((Map)parameterObject).get(name);
      }
      return null;
    }
}

image.gif

SqlNode 源码解析

在 MyBatis 处理动态 SQL 语句的时候,会将动态 SQL 标签解析为 SqlNode 对象,多个 SqlNode 对象就是通过组合模式组成树形结构供上层使用的

public interface SqlNode {
    // apply()方法会根据用户传入的实参,解析该SqlNode所表示的动态SQL内容并
    // 将解析之后的SQL片段追加到DynamicContext.sqlBuilder字段中暂存。
    // 当SQL语句中全部的动态SQL片段都解析完成之后,就可以从DynamicContext.sqlBuilder字段中
    // 得到一条完整的、可用的SQL语句了
    boolean apply(DynamicContext context);
}

image.gif

TrimSqlNode 对应 MyBatis 动态 SQL 语句中的 <trim> 标签;ChooseSqlNode在 MyBatis 的动态 SQL 语句中,我们可以使用 <choose><when><otherwise> 三个标签来实现类似的效果;ForeachSqlNode 对应 <foreach> 标签对一个集合进行迭代;VarDeclSqlNode 抽象了 <bind> 标签;<choose> 标签会被 MyBatis 解析成 ChooseSqlNode 对象<otherwise> 标签会被解析成 MixedSqlNode 对象。StaticTextSqlNode 用于表示非动态的 SQL 片段;TextSqlNode 实现抽象了包含 “${}”占位符的动态 SQL 片段;IfSqlNode 实现类对应了动态 SQL 语句中的 <if><when> 标签

image.gif编辑

本部分说明了动态 SQL 语句中使用的 OGNL 表达式语言,动态 SQL 语句中占位符的处理逻辑。分析了 DynamicContext 对象源码,其中维护了解析动态 SQL 语句上下文信息;随后我还分析了组合模式,因为它是 MyBatis 组合各动态 SQL 节点的设计思想。


相关文章
|
24天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
64 2
|
8天前
|
PyTorch Shell API
Ascend Extension for PyTorch的源码解析
本文介绍了Ascend对PyTorch代码的适配过程,包括源码下载、编译步骤及常见问题,详细解析了torch-npu编译后的文件结构和三种实现昇腾NPU算子调用的方式:通过torch的register方式、定义算子方式和API重定向映射方式。这对于开发者理解和使用Ascend平台上的PyTorch具有重要指导意义。
|
12天前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
42 12
|
23天前
|
SQL IDE 数据库连接
IntelliJ IDEA处理大文件SQL:性能优势解析
在数据库开发和管理工作中,执行大型SQL文件是一个常见的任务。传统的数据库管理工具如Navicat在处理大型SQL文件时可能会遇到性能瓶颈。而IntelliJ IDEA,作为一个强大的集成开发环境,提供了一些高级功能,使其在执行大文件SQL时表现出色。本文将探讨IntelliJ IDEA在处理大文件SQL时的性能优势,并与Navicat进行比较。
29 4
|
25天前
|
存储 安全 Linux
Golang的GMP调度模型与源码解析
【11月更文挑战第11天】GMP 调度模型是 Go 语言运行时系统的核心部分,用于高效管理和调度大量协程(goroutine)。它通过少量的操作系统线程(M)和逻辑处理器(P)来调度大量的轻量级协程(G),从而实现高性能的并发处理。GMP 模型通过本地队列和全局队列来减少锁竞争,提高调度效率。在 Go 源码中,`runtime.h` 文件定义了关键数据结构,`schedule()` 和 `findrunnable()` 函数实现了核心调度逻辑。通过深入研究 GMP 模型,可以更好地理解 Go 语言的并发机制。
|
1月前
|
消息中间件 缓存 安全
Future与FutureTask源码解析,接口阻塞问题及解决方案
【11月更文挑战第5天】在Java开发中,多线程编程是提高系统并发性能和资源利用率的重要手段。然而,多线程编程也带来了诸如线程安全、死锁、接口阻塞等一系列复杂问题。本文将深度剖析多线程优化技巧、Future与FutureTask的源码、接口阻塞问题及解决方案,并通过具体业务场景和Java代码示例进行实战演示。
47 3
|
16天前
|
SQL Java 数据库连接
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
|
29天前
|
SQL 监控 安全
员工上网行为监控软件:SQL 在数据查询监控中的应用解析
在数字化办公环境中,员工上网行为监控软件对企业网络安全和管理至关重要。通过 SQL 查询和分析数据库中的数据,企业可以精准了解员工的上网行为,包括基础查询、复杂条件查询、数据统计与分析等,从而提高网络管理和安全防护的效率。
28 0
|
5月前
|
SQL Java 数据库连接
mybatis动态SQL常用语法总结
MyBatis 使用 OGNL 表达式语言处理动态SQL,如 `if` 标签进行条件判断,`choose`、`when`、`otherwise` 实现多条件选择,`where`、`set` 管理SQL关键字,`trim` 提供通用修剪功能,`foreach` 遍历集合数据。`sql` 和 `include` 用于代码重用,`selectKey` 处理插入后的返回值。参数传递支持匿名、具名、列表、Map、Java Bean和JSON方式。注意SQL转义及使用合适的jdbcType映射Java类型。
103 7
|
6月前
|
SQL XML Java
MyBatis第四课动态SQL
MyBatis第四课动态SQL

热门文章

最新文章

推荐镜像

更多