欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 新车 > Mybatis 是如何处理动态SQL的, 用到了哪些设计模式?

Mybatis 是如何处理动态SQL的, 用到了哪些设计模式?

2025/4/3 5:01:39 来源:https://blog.csdn.net/nmsoftklb/article/details/146590952  浏览:    关键词:Mybatis 是如何处理动态SQL的, 用到了哪些设计模式?

MyBatis 处理动态 SQL 的方式非常巧妙,它并没有直接在运行时去解析 XML 字符串,而是在初始化阶段就将动态 SQL 解析成了一个对象树结构,然后在运行时根据传入的参数去解释这个树结构,最终生成可执行的 SQL。

这个过程主要涉及以下几个关键组件和步骤:

  1. 解析阶段 (MyBatis 初始化时):

    • 当 MyBatis 解析 Mapper XML 文件时,遇到包含动态标签(如 <if>, <foreach>, <where>, <set>, <trim>, <choose> 等)的 SQL 语句时,它不会将整个 SQL 视为一个简单的字符串。
    • 取而代之,MyBatis 会使用一系列的 SqlNode 实现类来解析这些动态标签和静态 SQL 片段。
    • 每个动态标签和静态文本块都会被解析成一个对应的 SqlNode 对象(例如:<if> 对应 IfSqlNode,静态文本对应 StaticTextSqlNode<foreach> 对应 ForEachSqlNode 等)。
    • 这些 SqlNode 对象会按照它们在 XML 中出现的顺序和嵌套关系,组成一个树形结构(或一个 MixedSqlNode 包含的列表)。
    • 这个最终的 SqlNode 树(或根节点)会被封装到一个 DynamicSqlSource 对象中。
    • DynamicSqlSource 对象最终被设置到 MappedStatement 中。
  2. 运行阶段 (执行 Mapper 方法时):

    • 当调用 Mapper 接口的方法,需要执行对应的动态 SQL 时,MyBatis 会从 MappedStatement 中获取到 DynamicSqlSource
    • 调用 DynamicSqlSourcegetBoundSql(parameterObject) 方法。
    • getBoundSql 方法内部会创建一个 DynamicContext 对象。这个 DynamicContext 主要用于:
      • 存储传入的参数对象
      • 提供一个StringBuilder (sqlBuilder) 来逐步构建最终的 SQL 字符串。
      • 管理参数绑定(例如,在 <foreach> 中生成的临时变量)。
    • 接着,会调用根 SqlNodeapply(DynamicContext context) 方法。
    • apply 方法是 SqlNode 接口的核心。每个具体的 SqlNode 实现类会根据自己的逻辑来执行 apply
      • StaticTextSqlNode: 直接将它代表的静态 SQL 文本追加到 DynamicContextsqlBuilder 中。
      • IfSqlNode: 使用 OGNL (Object-Graph Navigation Language) 表达式引擎,根据 DynamicContext 中的参数对象来评估 test 属性中的条件。如果条件为真,则递归调用其内部包含的子 SqlNodeapply 方法。
      • ForEachSqlNode: 同样使用 OGNL 获取要迭代的集合,然后循环遍历集合。在每次迭代中,将当前项和索引(如果需要)设置到 DynamicContext 中,并递归调用其内部包含的子 SqlNodeapply 方法。它还会处理 open, close, separator 属性。
      • WhereSqlNode, SetSqlNode, TrimSqlNode: 这些节点比较特殊,它们会先调用其内部子节点的 apply 方法,然后对 DynamicContext 中已经生成的 SQL 进行后处理,例如智能地添加 WHERESET 关键字,并移除多余的 AND, OR 或逗号。
      • MixedSqlNode: 依次调用其包含的所有子 SqlNodeapply 方法。
    • 当根 SqlNodeapply 方法执行完毕后,DynamicContext 中的 sqlBuilder 就包含了根据传入参数动态生成的最终 SQL 字符串。
    • DynamicSqlSource 最后将生成的 SQL 字符串和对应的参数映射信息封装成一个 BoundSql 对象返回。

用到的主要设计模式:

MyBatis 在处理动态 SQL 时,巧妙地运用了以下几种设计模式:

  1. 组合模式 (Composite Pattern):

    • 这是最核心的模式。SqlNode 接口及其各种实现类(StaticTextSqlNode, IfSqlNode, MixedSqlNode 等)构成了典型的组合模式。
    • SqlNode 定义了一个统一的操作接口 apply(DynamicContext context)
    • 叶子节点(如 StaticTextSqlNode)实现了这个接口,执行具体的操作(追加文本)。
    • 容器节点(如 MixedSqlNode, IfSqlNode, ForEachSqlNode)也实现了这个接口,但它们的实现通常是递归地调用其子节点的 apply 方法。
    • 这使得 MyBatis 可以一致地处理单个 SQL 片段和复杂的、嵌套的动态 SQL 结构。客户端(DynamicSqlSource)只需要调用根节点的 apply 方法,整个树结构就会被处理。
  2. 解释器模式 (Interpreter Pattern):

    • 动态 SQL 标签(<if>, <foreach> 等)可以看作是一种领域特定语言 (DSL),用于定义如何根据参数生成 SQL。
    • SqlNode 的实现类可以看作是这个 DSL 的解释器。每个节点类都知道如何解释(apply)它所代表的特定语法元素(标签或文本)。
    • DynamicContext 在解释过程中传递上下文信息(参数、正在构建的 SQL)。
    • 整个 SqlNode 树的 apply 过程就是对这个动态 SQL 语言进行解释执行的过程。
  3. 构建器模式 (Builder Pattern):

    • 虽然运行时 SQL 的构建主要是通过 DynamicContext 中的 StringBuilder 累加完成的,但在 MyBatis 解析 XML 并创建 MappedStatement 的过程中,大量使用了构建器模式。例如,MappedStatement.Builder, SqlSourceBuilder 等,它们用于逐步构建复杂的配置对象。
    • 在动态 SQL 处理的解析阶段,构建器模式帮助将 XML 配置信息逐步构建成 SqlNode 树和 DynamicSqlSource 对象。
  4. 责任链模式 (Chain of Responsibility Pattern) (某种程度上):

    • 虽然不是严格的责任链模式,但像 WhereSqlNode, SetSqlNode, TrimSqlNode 这样的节点,它们在子节点处理完(调用完子节点的 apply)之后,会对自己和子节点生成的 SQL 进行后处理。这有点类似于责任链中的节点在处理请求后,还可以对结果进行修改或传递。它们负责处理 SQL 拼接中的特定问题(如多余的连接符)。
  5. 上下文对象模式 (Context Object Pattern):

    • DynamicContext 对象就是典型的上下文对象。它封装了在 SqlNode 树的 apply 方法调用链中需要共享的状态和数据(参数、SQL 构建器、参数绑定),避免了在方法间传递大量参数。

总结:

MyBatis 通过组合模式将动态 SQL 解析成 SqlNode 树,然后利用解释器模式在运行时根据 DynamicContext上下文对象)来解释这棵树,动态地生成最终的 SQL。构建器模式则在初始解析阶段发挥作用。这种设计使得动态 SQL 的处理逻辑清晰、结构化且易于扩展。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词