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

MyBatis 动态 SQL 精讲:告别硬编码的智能拼接艺术

🔍 MyBatis 动态 SQL 精讲:告别硬编码的智能拼接艺术

文章目录

  • 🔍 MyBatis 动态 SQL 精讲:告别硬编码的智能拼接艺术
  • 🧠 一、为什么需要动态 SQL?
    • 💡 JDBC 时代的痛点
  • 🛠️ 二、核心标签深度解析
    • 💡 动态 SQL 标签全景图
    • 🔥 1. if 标签:条件分支
    • 🔀 2. choose/when/otherwise:多路选择
    • 🎯 3. where 标签:智能 WHERE 子句
    • 🔄 4. set 标签:智能 UPDATE
    • 🔁 5. foreach 标签:循环遍历
    • 🎨 6. trim 标签:自定义修剪
  • 💡 三、最佳实践指南
    • 💡 可维护性设计原则
    • 🛡️ 1. 避免冗余条件
    • 📝 2. 保持 SQL 可读性
  • ⚠️ 四、常见陷阱与优化
    • 💡 性能与安全对比
    • 🔧 1. 空条件处理
    • ⚡ 2. foreach 性能优化
    • 🛡️ 3. SQL 注入防护
  • 🔚 总结与延伸
    • 📚 核心要点回顾

🧠 一、为什么需要动态 SQL?

💡 JDBC 时代的痛点

在传统 JDBC 开发中,我们经常遇到这样的代码:

// JDBC 动态拼接SQL的噩梦
StringBuilder sql = new StringBuilder("SELECT * FROM users WHERE 1=1 ");
if (name != null) {sql.append("AND name = '").append(name).append("' ");
}
if (email != null) {sql.append("AND email = '").append(email).append("' ");
}
// SQL注入风险!字符串拼接地狱!

​​MyBatis 动态 SQL 的优势​​:

JDBC手动拼接
SQL注入风险
代码冗长
难以维护
MyBatis动态SQL
预编译安全
标签化简洁
易于维护

🛠️ 二、核心标签深度解析

💡 动态 SQL 标签全景图

在这里插入图片描述

🔥 1. if 标签:条件分支

​​应用场景​​:多条件查询

<select id="findUsers" resultType="User">SELECT * FROM users<where><if test="name != null and name != ''">AND name LIKE #{name}</if><if test="email != null">AND email = #{email}</if><if test="status != null">AND status = #{status}</if></where>
</select>

​​输入参数​​:{name: “张”, status: 1}

​​生成 SQL​​:

SELECT * FROM users 
WHERE name LIKE ? AND status = ?

🔀 2. choose/when/otherwise:多路选择

​​应用场景​​:优先级条件查询

<select id="findUsers" resultType="User">SELECT * FROM users<where><choose><when test="id != null">id = #{id}</when><when test="email != null">email = #{email}</when><otherwise>status = 1</otherwise></choose></where>
</select>

​​输入参数​​:{email: “test@example.com”}

​​生成 SQL​​:

SELECT * FROM users WHERE email = ?

🎯 3. where 标签:智能 WHERE 子句

​​解决痛点​​:自动处理 AND/OR 和空条件

<!-- 传统方式需要写 WHERE 1=1 -->
<select id="findUsers" resultType="User">SELECT * FROM users<where><if test="name != null">name = #{name}</if><if test="email != null">AND email = #{email}</if></where>
</select>

​​输入参数​​:{name: null, email: “test@example.com”}

​​生成 SQL​​:

SELECT * FROM users WHERE email = ?

🔄 4. set 标签:智能 UPDATE

​​应用场景​​:动态更新字段

<update id="updateUser">UPDATE users<set><if test="name != null">name = #{name},</if><if test="email != null">email = #{email},</if><if test="status != null">status = #{status},</if></set>WHERE id = #{id}
</update>

​​输入参数​​:{id: 1, name: “张三”}

​​生成 SQL​​:

UPDATE users SET name = ? WHERE id = ?

🔁 5. foreach 标签:循环遍历

​​应用场景​​:批量操作和 IN 查询

<select id="findUsersByIds" resultType="User">SELECT * FROM usersWHERE id IN<foreach item="id" collection="ids" open="(" separator="," close=")">#{id}</foreach>
</select>

​​输入参数​​:{ids: [1, 2, 3, 5, 8]}

​​生成 SQL​​

SELECT * FROM users WHERE id IN (?, ?, ?, ?, ?)

🎨 6. trim 标签:自定义修剪

​​应用场景​​:更精细的字符串处理

<update id="updateUser">UPDATE users<trim prefix="SET" suffixOverrides=","><if test="name != null">name = #{name},</if><if test="email != null">email = #{email},</if></trim>WHERE id = #{id}
</update>

💡 三、最佳实践指南

💡 可维护性设计原则

可维护性
单一职责
清晰命名
避免嵌套过深
适当注释

🛡️ 1. 避免冗余条件

​​不推荐​​:

<if test="name != null and name != ''">AND name = #{name}
</if>
<if test="name != null and name != ''">AND email = #{email}
</if>

​​推荐​​:

<where><if test="condition.name != null and condition.name != ''">name = #{condition.name}</if><if test="condition.email != null">email = #{condition.email}</if>
</where>

📝 2. 保持 SQL 可读性

​​复杂动态 SQL 示例​​:

<select id="searchUsers" resultType="User">/* 用户综合查询 */SELECT * FROM users<where><!-- 姓名条件 --><if test="name != null">AND (name LIKE #{name} OR nick_name LIKE #{name})</if><!-- 状态条件 --><if test="statusList != null and statusList.size() > 0">AND status IN<foreach item="status" collection="statusList" open="(" separator="," close=")">#{status}</foreach></if><!-- 时间范围 --><if test="startTime != null and endTime != null">AND create_time BETWEEN #{startTime} AND #{endTime}</if></where>ORDER BY id DESC
</select>

⚠️ 四、常见陷阱与优化

💡 性能与安全对比

问题类型问题描述解决方案
条件缺失所有if条件都不满足,SQL语法错误使用<where>标签
SQL注入不当使用${}导致注入风险始终优先使用#{}
性能问题foreach批量过大导致SQL过长分批处理,每批1000条
空集合空集合在foreach中导致SQL错误先判断集合是否为空

🔧 1. 空条件处理

​​问题代码​​:

<select id="findUsers" resultType="User">SELECT * FROM usersWHERE<if test="name != null">name = #{name}</if><if test="email != null">AND email = #{email}</if>
</select>

​​当所有if都不满足时生成​​:

SELECT * FROM users WHERE

​​修复方案​​:

<select id="findUsers" resultType="User">SELECT * FROM users<where><if test="name != null">name = #{name}</if><if test="email != null">AND email = #{email}</if></where>
</select>

⚡ 2. foreach 性能优化

​​批量插入优化​​:

<insert id="batchInsertUsers">INSERT INTO users (name, email) VALUES<foreach item="user" collection="users" separator=",">(#{user.name}, #{user.email})</foreach>
</insert>

**​​风险​​:**一次插入10000条数据会导致SQL过长

​​解决方案​​:分批处理

// 服务层分批处理
public void batchInsert(List<User> users) {int batchSize = 1000;for (int i = 0; i < users.size(); i += batchSize) {List<User> batch = users.subList(i, Math.min(i + batchSize, users.size()));userMapper.batchInsertUsers(batch);}
}

🛡️ 3. SQL 注入防护

​​危险用法​​(绝对避免!):

<select id="findUsers">SELECT * FROM users ORDER BY ${orderBy} ${orderDirection}
</select>

​​安全用法​​:

<select id="findUsers">SELECT * FROM users ORDER BY <choose><when test="orderBy == 'name'">name</when><when test="orderBy == 'email'">email</when><otherwise>id</otherwise></choose><choose><when test="orderDirection == 'desc'">DESC</when><otherwise>ASC</otherwise></choose>
</select>

🔍 MyBatis 动态 SQL 精讲:告别硬编码的智能拼接艺术
作为拥有十年企业级开发经验的架构师,我将带您深入探索 MyBatis 动态 SQL 的强大功能。本文不仅解析核心标签,更通过实战案例展示如何构建灵活、高效且安全的数据库查询!

目录

🧠 一、为什么需要动态 SQL?


🛠️ 二、核心标签深度解析


💡 三、最佳实践指南


⚠️ 四、常见陷阱与优化


🚀 五、实战业务场景

🧠 一、为什么需要动态 SQL?
💡 JDBC 时代的痛点
在传统 JDBC 开发中,我们经常遇到这样的代码:

// JDBC 动态拼接SQL的噩梦
StringBuilder sql = new StringBuilder("SELECT * FROM users WHERE 1=1 “);
if (name != null) {
sql.append(“AND name = '”).append(name).append(”’ “);
}
if (email != null) {
sql.append(“AND email = '”).append(email).append(”’ ");
}
// SQL注入风险!字符串拼接地狱!
​​MyBatis 动态 SQL 的优势​​:

graph LR
A[JDBC手动拼接] --> B[SQL注入风险]
A --> C[代码冗长]
A --> D[难以维护]

E[MyBatis动态SQL] --> F[预编译安全]
E --> G[标签化简洁]
E --> H[易于维护]style B fill:#f99,stroke:#333
style C fill:#f99,stroke:#333
style D fill:#f99,stroke:#333
style F fill:#9f9,stroke:#333
style G fill:#9f9,stroke:#333
style H fill:#9f9,stroke:#333

🛠️ 二、核心标签深度解析
💡 动态 SQL 标签全景图
mindmap
root(MyBatis动态SQL)
条件控制
if
choose/when/otherwise
SQL结构
where
set
trim
循环遍历
foreach
其他
bind
🔥 1. if 标签:条件分支
​​应用场景​​:多条件查询

SELECT * FROM users AND name LIKE #{name} AND email = #{email} AND status = #{status} ​​输入参数​​:{name: "张", status: 1}

​​生成 SQL​​:

SELECT * FROM users
WHERE name LIKE ? AND status = ?
🔀 2. choose/when/otherwise:多路选择
​​应用场景​​:优先级条件查询

SELECT * FROM users id = #{id} email = #{email} status = 1 ​​输入参数​​:{email: "test@example.com"}

​​生成 SQL​​:

SELECT * FROM users WHERE email = ?
🎯 3. where 标签:智能 WHERE 子句
​​解决痛点​​:自动处理 AND/OR 和空条件

SELECT * FROM users name = #{name} AND email = #{email} ​​输入参数​​:{name: null, email: "test@example.com"}

​​生成 SQL​​:

SELECT * FROM users WHERE email = ?
🔄 4. set 标签:智能 UPDATE
​​应用场景​​:动态更新字段

UPDATE users name = #{name}, email = #{email}, status = #{status}, WHERE id = #{id} ​​输入参数​​:{id: 1, name: "张三"}

​​生成 SQL​​:

UPDATE users SET name = ? WHERE id = ?
🔁 5. foreach 标签:循环遍历
​​应用场景​​:批量操作和 IN 查询

SELECT * FROM users WHERE id IN #{id} ​​输入参数​​:{ids: [1, 2, 3, 5, 8]}

​​生成 SQL​​:

SELECT * FROM users WHERE id IN (?, ?, ?, ?, ?)
🎨 6. trim 标签:自定义修剪
​​应用场景​​:更精细的字符串处理

UPDATE users name = #{name}, email = #{email}, WHERE id = #{id} 💡 三、最佳实践指南 💡 可维护性设计原则 graph TD A[可维护性] --> B[单一职责] A --> C[清晰命名] A --> D[避免嵌套过深] A --> E[适当注释]
style B fill:#9f9,stroke:#333
style C fill:#9f9,stroke:#333

🛡️ 1. 避免冗余条件
​​不推荐​​:

AND name = #{name} AND email = #{email} ​​推荐​​: name = #{condition.name} email = #{condition.email} 📝 2. 保持 SQL 可读性 ​​复杂动态 SQL 示例​​: /* 用户综合查询 */ SELECT * FROM users AND (name LIKE #{name} OR nick_name LIKE #{name})
<!-- 状态条件 -->
<if test="statusList != null and statusList.size() > 0">AND status IN<foreach item="status" collection="statusList" open="(" separator="," close=")">#{status}</foreach>
</if><!-- 时间范围 -->
<if test="startTime != null and endTime != null">AND create_time BETWEEN #{startTime} AND #{endTime}
</if>
ORDER BY id DESC ⚠️ 四、常见陷阱与优化 💡 性能与安全对比 | 问题类型 | 问题描述 | 解决方案 | |---------|---------|---------| | **条件缺失** | 所有if条件都不满足,SQL语法错误 | 使用``标签 | | **SQL注入** | 不当使用`${}`导致注入风险 | 始终优先使用`#{}` | | **性能问题** | `foreach`批量过大导致SQL过长 | 分批处理,每批1000条 | | **空集合** | 空集合在`foreach`中导致SQL错误 | 先判断集合是否为空 | 🔧 1. 空条件处理 ​​问题代码​​: SELECT * FROM users WHERE name = #{name} AND email = #{email} ​​当所有if都不满足时生成​​:

SELECT * FROM users WHERE
​​修复方案​​:

SELECT * FROM users name = #{name} AND email = #{email} ⚡ 2. foreach 性能优化 ​​批量插入优化​​: INSERT INTO users (name, email) VALUES (#{user.name}, #{user.email}) ​​风险​​:一次插入10000条数据会导致SQL过长

​​解决方案​​:分批处理

// 服务层分批处理
public void batchInsert(List users) {
int batchSize = 1000;
for (int i = 0; i < users.size(); i += batchSize) {
List batch = users.subList(i, Math.min(i + batchSize, users.size()));
userMapper.batchInsertUsers(batch);
}
}
🛡️ 3. SQL 注入防护
​​危险用法​​(绝对避免!):

SELECT * FROM users ORDER BY ${orderBy} ${orderDirection} ​​安全用法​​: SELECT * FROM users ORDER BY name email id DESC ASC # 🚀 五、实战业务场景 ## 💡 电商平台商品查询
<select id="searchProducts" resultType="Product">SELECT * FROM products<where><!-- 关键词搜索 --><if test="keyword != null">AND (name LIKE CONCAT('%', #{keyword}, '%') OR description LIKE CONCAT('%', #{keyword}, '%'))</if><!-- 价格范围 --><if test="minPrice != null">AND price >= #{minPrice}</if><if test="maxPrice != null">AND price <= #{maxPrice}</if><!-- 类目筛选 --><if test="categoryIds != null and categoryIds.size() > 0">AND category_id IN<foreach item="categoryId" collection="categoryIds" open="(" separator="," close=")">#{categoryId}</foreach></if><!-- 商品状态 --><if test="status != null">AND status = #{status}</if></where>ORDER BY<choose><when test="sortBy == 'price'">price</when><when test="sortBy == 'sales'">sales_count</when><otherwise>create_time</otherwise></choose><choose><when test="sortOrder == 'desc'">DESC</when><otherwise>ASC</otherwise></choose>
</select>

🔍 执行流程全景

客户端Mapper接口动态SQL引擎数据库调用查询方法(参数)解析动态SQL应用if/where/foreach发送最终SQL返回结果结果映射返回业务对象客户端Mapper接口动态SQL引擎数据库

🔚 总结与延伸

📚 核心要点回顾

1.​​if/choose​​:条件分支控制

2.​​where/set​​:智能SQL结构处理

3.​​foreach​​:循环遍历操作

​安全第一​​:始终优先使用#{}

5.​​性能优化​​:批量操作分批处理

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

相关文章:

  • 【Depth与RGB对齐算法(D2C)】
  • PPT处理控件Aspose.Slides教程:在 C# 中将 PPTX 转换为 Markdown
  • 项目一系列-第7章 父子组件通信
  • vue3使用axios向spring boot 发送请求,请求响应时间快慢交替问题分析
  • 探索 Vertex AI 与 Elasticsearch
  • Ubuntu 从零到一搭建 Appium+Python 自动化环境(含下厨房真机实战)—2025 版
  • 导出wireshark的FLV RAW数据并进行分析
  • 第13集 当您的USB设备不在已实测支持列表,如何让TOS-WLink支持您的USB设备--答案Wireshark USB抓包
  • [数据结构] ArrayList与顺序表(下)
  • indexDB快速上手
  • 2015考研数学(二)真题
  • 让模糊物体变清晰的视频AI:快速提升画质指南
  • 51c大模型~合集175
  • pcl_案例2 叶片与根茎的分离
  • Redis发布订阅:实时消息系统的极简解决方案
  • MyBatis延迟加载
  • 云计算学习100天-第29天
  • Node.js 的模块化规范是什么?CommonJS 和 ES6 模块有什么区别?
  • Python DELL Logo
  • day1 ———C++———变量和字符串的使用
  • AI驱动企业数字化转型:解码未来三年的智能化变革密码
  • STAGEWISE实战指南:从集成到使用的完整解决方案
  • AI在商业领域的多元应用:从写作助手到精准运营,解锁AI商业工具新价值
  • 流程控制语句(3)
  • 操作系统中的死锁是什么意思
  • 农行广西区分行携手广西专精特新商会共探金融赋能专精特新企业新路径
  • 用KPI导航数字化转型:制造企业如何科学评估系统上线成效
  • 流程控制语句(2)
  • Java网络编程(UDP, TCP, HTTP)
  • 【Linux基础知识系列:第一百一十五篇】使用gzip与bzip2进行压缩