当前位置: 首页 > news >正文

手写MyBatis第87弹:从SqlNode树到可执行SQL的转换奥秘

MyBatis动态SQL运行时解析:从SqlNode树到可执行SQL的转换奥秘

静态文本节点:动态SQL中的不变基石

目录

DynamicSqlSource:运行时解析的调度中心

关键方法:getBoundSql的深度解析

DynamicContext:SQL生成的运行时环境

上下文绑定的重要作用

SqlNode树的应用过程:深度优先遍历

实际执行示例

BoundSql:可执行SQL的完整封装

参数映射的重要性

运行时解析 vs 启动时解析:性能权衡

DynamicSqlSource(运行时解析)

RawSqlSource(启动时解析)

实际性能影响

高级特性与最佳实践

1. 参数绑定的高级用法

2. 动态SQL的性能优化

3. 调试技巧

实际应用场景分析

场景1:复杂查询条件构建

场景2:多租户数据隔离

总结


  🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞

💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝欢迎留言讨论

🔥🔥🔥(源码 + 调试运行 + 问题答疑)

🔥🔥🔥  有兴趣可以联系我。文末有免费源码

免费获取源码。

更多内容敬请期待。如有需要可以联系作者免费送

更多源码定制,项目修改,项目二开可以联系作者
点击可以进行搜索(每人免费送一套代码):千套源码目录(点我)

2025元旦源码免费送(点我)

我们常常在当下感到时间慢,觉得未来遥远,但一旦回头看,时间已经悄然流逝。对于未来,尽管如此,也应该保持一种从容的态度,相信未来仍有许多可能性等待着我们。

在MyBatis动态SQL的复杂体系中,StaticTextSqlNode扮演着最基础却至关重要的角色。它如同建筑中的砖块,虽然简单但构成了整个SQL语句的骨架。当我们深入分析动态SQL的执行过程时,会发现即使是最复杂的动态查询,最终也由这些静态文本片段组合而成。

StaticTextSqlNode的实现简洁而高效:

 public class StaticTextSqlNode implements SqlNode {private final String text;public StaticTextSqlNode(String text) {this.text = text;}@Overridepublic boolean apply(DynamicContext context) {context.appendSql(text);return true;}}

这种简洁性掩盖了它的重要性——在动态SQL的解析树中,所有的动态逻辑最终都通过组合这些静态文本来构建完整的SQL语句。

DynamicSqlSource:运行时解析的调度中心

DynamicSqlSource是实现动态SQL能力的核心类,它负责在每次SQL执行前,根据传入的参数动态生成最终的SQL语句。这种设计体现了MyBatis的一个重要哲学:解析时机决定性能特征

关键方法:getBoundSql的深度解析

getBoundSql方法是整个动态SQL执行流程的入口点:

 public class DynamicSqlSource implements SqlSource {private final Configuration configuration;private final SqlNode rootSqlNode;@Overridepublic BoundSql getBoundSql(Object parameterObject) {// 步骤1:创建动态上下文DynamicContext context = new DynamicContext(configuration, parameterObject);// 步骤2:应用SqlNode树生成SQLrootSqlNode.apply(context);// 步骤3:解析生成的SQL字符串SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());// 步骤4:获取最终的BoundSqlBoundSql boundSql = sqlSource.getBoundSql(parameterObject);// 步骤5:添加动态参数for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());}return boundSql;}}

这个过程涉及多个关键对象和步骤,让我们逐一深入分析。

DynamicContext:SQL生成的运行时环境

DynamicContext不仅是一个简单的字符串构建器,它维护了SQL生成过程中所需的所有上下文信息:

 public class DynamicContext {public static final String PARAMETER_OBJECT_KEY = "_parameter";public static final String DATABASE_ID_KEY = "_databaseId";private final ContextMap bindings;private final StringBuilder sqlBuilder = new StringBuilder();public DynamicContext(Configuration configuration, Object parameterObject) {// 初始化绑定上下文if (parameterObject != null && !(parameterObject instanceof Map)) {MetaObject metaObject = configuration.newMetaObject(parameterObject);bindings = new ContextMap(metaObject);} else {bindings = new ContextMap(null);}bindings.put(PARAMETER_OBJECT_KEY, parameterObject);bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());}public void appendSql(String sql) {sqlBuilder.append(sql);sqlBuilder.append(" ");}public String getSql() {return sqlBuilder.toString().trim();}public Map<String, Object> getBindings() {return bindings;}}

上下文绑定的重要作用

上下文绑定机制使得动态SQL能够访问各种运行时信息:

  • 方法参数对象及其属性

  • 数据库标识信息

  • 在动态SQL处理过程中产生的中间变量

  • OGNL表达式求值所需的上下文

SqlNode树的应用过程:深度优先遍历

当调用rootSqlNode.apply(context)时,整个SqlNode树开始工作。这个过程采用深度优先遍历算法:

  1. 根节点启动:通常是MixedSqlNode,它遍历所有子节点

  2. 条件评估:遇到IfSqlNode时,评估OGNL表达式决定是否处理子节点

  3. 文本追加StaticTextSqlNode直接将文本内容追加到上下文

  4. 递归处理:复杂节点继续递归处理其子节点树

实际执行示例

考虑以下动态SQL:

 <select id="findUser" parameterType="map" resultType="User">SELECT id, name, email FROM users<where><if test="name != null">AND name = #{name}</if><if test="status != null">AND status = #{status}</if></where></select>

当传入参数{name: "张三", status: null}时,执行过程如下:

  1. MixedSqlNode开始处理所有子节点

  2. StaticTextSqlNode追加"SELECT id, name, email FROM users"

  3. WhereSqlNode开始处理WHERE条件

  4. 第一个IfSqlNode评估name != null为true,处理其子节点

  5. StaticTextSqlNode追加"AND name = #{name}"

  6. 第二个IfSqlNode评估status != null为false,跳过其子节点

  7. WhereSqlNode智能移除开头的"AND",生成"WHERE name = #{name}"

最终生成的SQL:SELECT id, name, email FROM users WHERE name = ?

BoundSql:可执行SQL的完整封装

BoundSql对象包含了执行SQL所需的所有信息:

 public class BoundSql {private final String sql;private final List<ParameterMapping> parameterMappings;private final Object parameterObject;private final Map<String, Object> additionalParameters;private final MetaObject metaParameters;// 构造函数、getter和setter方法}

参数映射的重要性

ParameterMapping包含了参数处理的元数据:

  • 参数名称

  • 参数类型

  • 类型处理器

  • 结果映射信息

这些信息使得MyBatis能够正确地将Java对象转换为JDBC参数,并处理查询结果的映射。

运行时解析 vs 启动时解析:性能权衡

理解动态SQL的解析时机对于性能优化至关重要:

DynamicSqlSource(运行时解析)

  • 解析时机:每次SQL执行前

  • 适用场景:包含动态标签(<if>, <where>等)的SQL

  • 性能特征:每次执行都有解析开销,但适应动态参数变化

  • 内存占用:保存SqlNode树,内存占用相对固定

RawSqlSource(启动时解析)

  • 解析时机:应用启动时,仅解析一次

  • 适用场景:纯静态SQL,不包含动态标签

  • 性能特征:一次解析,多次使用,性能更优

  • 内存占用:保存解析后的静态SQL

实际性能影响

在高压力的生产环境中,这种设计选择会产生显著影响。我们曾经在一个电商系统中发现,将频繁调用的动态查询重构为静态查询后,QPS提升了约15%。

高级特性与最佳实践

1. 参数绑定的高级用法

 // 在动态SQL中访问映射器方法参数<if test="_parameter != null and _parameter.name != null">AND name = #{_parameter.name}</if>​// 使用@Param注解指定参数名List<User> findUsers(@Param("name") String name, @Param("status") Integer status);​// 在XML中直接使用命名参数<if test="name != null">AND name = #{name}</if>

2. 动态SQL的性能优化

  • 避免过度动态化:如果某些条件几乎总是存在,考虑将其设为静态部分

  • 合理使用OGNL:复杂的OGNL表达式会影响性能

  • 批量处理优化:对于批量操作,考虑使用<foreach>标签的批量特性

3. 调试技巧

 // 获取实际执行的SQL用于调试@Select("SELECT * FROM users WHERE name = #{name}")List<User> findByName(@Param("name") String name);​// 通过日志输出实际SQL// 配置mybatis日志级别为DEBUG

实际应用场景分析

场景1:复杂查询条件构建

在管理后台的筛选功能中,动态SQL极大地简化了代码:

 <select id="searchProducts" parameterType="map" resultType="Product">SELECT * FROM products<where><if test="categoryId != null">AND category_id = #{categoryId}</if><if test="minPrice != null">AND price >= #{minPrice}</if><if test="maxPrice != null">AND price <= #{maxPrice}</if><if test="keywords != null and keywords != ''">AND (name LIKE CONCAT('%', #{keywords}, '%') OR description LIKE CONCAT('%', #{keywords}, '%'))</if><if test="statusList != null and statusList.size() > 0">AND status IN<foreach collection="statusList" item="status" open="(" close=")" separator=",">#{status}</foreach></if></where>ORDER BY <choose><when test="sortBy == 'price'">price</when><when test="sortBy == 'sales'">sales_count</when><otherwise>create_time</otherwise></choose><if test="sortOrder != null and sortOrder == 'desc'">DESC</if></select>

场景2:多租户数据隔离

在SaaS系统中,动态SQL可以优雅地处理租户数据隔离:

<select id="findTenantData" parameterType="map" resultType="BusinessData">SELECT * FROM business_dataWHERE tenant_id = #{tenantId}<if test="filters != null"><include refid="commonFilters"/></if>
</select>

总结

MyBatis动态SQL的运行时解析机制是一个精心设计的系统,它通过SqlNode树、DynamicContextBoundSql的协同工作,实现了声明式SQL到可执行SQL的转换。理解这一机制不仅有助于更好地使用MyBatis,还能在遇到复杂问题时提供有效的调试思路。

关键要点:

  • StaticTextSqlNode是构建动态SQL的基础单元

  • DynamicSqlSource在运行时根据参数动态生成SQL

  • BoundSql封装了最终的可执行SQL和参数信息

  • 解析时机的选择(运行时vs启动时)是重要的性能权衡

通过深入理解这些核心概念,开发者可以编写出既灵活又高性能的数据访问层代码,充分发挥MyBatis动态SQL的强大能力。

🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞

💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝欢迎留言讨论

🔥🔥🔥(源码 + 调试运行 + 问题答疑)

🔥🔥🔥  有兴趣可以联系我。文末有免费源码

💖学习知识需费心,
📕整理归纳更费神。
🎉源码免费人人喜,
🔥码农福利等你领!

💖常来我家多看看,
📕网址:扣棣编程
🎉感谢支持常陪伴,
🔥点赞关注别忘记!

💖山高路远坑又深,
📕大军纵横任驰奔,
🎉谁敢横刀立马行?
🔥唯有点赞+关注成!

往期文章推荐:

基于Springboot + vue实现的学生宿舍信息管理系统
免费获取宠物商城源码--SpringBoot+Vue宠物商城网站系统 
【2025小年源码免费送】

⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇点击此处获取源码⬇⬇⬇⬇⬇⬇⬇⬇⬇

http://www.dtcms.com/a/431424.html

相关文章:

  • Hot100——普通数组
  • Linux 软件安装和进程管理
  • [创业之路-645]:手机属于通信?还是属于消费类电子?还是移动互联网?
  • 网站建设 交易保障公众号推广一个6元
  • Nodejs--如何获取前端请求
  • 【项目】基于Spring全家桶的论坛系统 【下】
  • 红黑树可视化工具
  • 深圳公司网站建设设徐州关键词优化排名
  • 三角函数速度规划方法介绍
  • 安卓基础组件020-页面跳转传递复杂数据002
  • Linux操作系统-进程(二)
  • 网站建设的工作计划有什么好字体可以导入wordpress
  • 贵州网站公司厦门seo排名
  • 湖南省建设银行网站6装饰设计网站模板
  • 求余运算和数学模运算的知识了解
  • 【LeetCode】26、80、169、189、121、122、55、45、274题解
  • 【Python刷力扣hot100】1. Two Sum
  • 网站建设属于什么行业类别wordpress数据库文件在哪里设置
  • CCF-CSP认证考试 202309-3 梯度求解
  • JavaScript 保留关键字详解
  • 赣州那里有做网站的公司中国最新军事新闻最新消息2023
  • 使用 Python 将文件上传到 Supabase Storage 并记录元数据
  • 测试报告——“问卷考试系统”项目
  • 网站开发者常见问题网站建设 010
  • Digi-Star精准饲喂系统:牧场收益提升的利器,把Digi XBee模块应用到了极致
  • LED点阵屏
  • 图片方向矫正工具,批量处理超高效
  • 网站开发系统源代码模板兔自用主题WordPress
  • 鄢陵县网站wordpress编辑器器更换
  • 安阳网站推广优化点击最多的网站