2228 字
约 7 分钟
17
动态SQL
无标签

第07章 动态SQL

学习动态 SQL 的编写和各种动态标签的使用
学习进度:28% 代码演示按钮 💻 查看完整代码 - 在线IDE体验

🎯 学习目标

📚

理解核心概念

掌握本章的核心概念和基本原理
🔧

掌握实践技能

学会相关的配置方法和实践技巧
💡

了解应用场景

明确在实际项目中的应用场景和最佳实践

学习目标

  • 深入理解MyBatis动态SQL的概念和优势
  • 掌握if、choose、when、otherwise等条件标签
  • 学会使用where、set、trim等辅助标签
  • 熟练运用foreach标签进行批量操作
  • 掌握SQL片段的定义和复用技巧
  • 了解动态SQL的最佳实践和性能优化

动态SQL概述

动态SQL是MyBatis的强大特性之一,它允许我们根据不同的条件动态构建SQL语句,避免了在Java代码中拼接SQL字符串的复杂性和安全风险。

动态SQL的优势

  • 灵活性:根据条件动态生成SQL
  • 安全性:避免SQL注入风险
  • 可维护性:SQL逻辑集中管理
  • 性能优化:只查询需要的字段和条件

if标签

if标签是最基本的条件判断标签,用于根据条件决定是否包含某段SQL。

基本语法

<if test="条件表达式">
    SQL片段
</if>

实际应用

<select id="selectUsersByCondition" resultType="User">
    SELECT id, name, age, email FROM users
    WHERE 1=1
    <if test="name != null and name != ''">
        AND name LIKE CONCAT('%', #{name}, '%')
    </if>
    <if test="minAge != null">
        AND age >= #{minAge}
    </if>
    <if test="maxAge != null">
        AND age <= #{maxAge}
    </if>
</select>

条件表达式

  • name != null:判断参数是否为null
  • name != '':判断字符串是否为空
  • age > 0:数值比较
  • list != null and list.size() > 0:集合判断

choose/when/otherwise标签

类似于Java中的switch语句,用于多条件选择,只会执行其中一个分支。

基本语法

<choose>
    <when test="条件1">
        SQL片段1
    </when>
    <when test="条件2">
        SQL片段2
    </when>
    <otherwise>
        默认SQL片段
    </otherwise>
</choose>

实际应用

<select id="selectUsersByChoice" resultType="User">
    SELECT id, name, age, email FROM users
    WHERE 1=1
    <choose>
        <when test="name != null and name != ''">
            AND name = #{name}
        </when>
        <when test="age != null">
            AND age = #{age}
        </when>
        <when test="email != null and email != ''">
            AND email = #{email}
        </when>
        <otherwise>
            AND 1=1
        </otherwise>
    </choose>
</select>

where标签

where标签用于动态生成WHERE子句,自动处理AND和OR的前缀问题。

解决的问题

  • 自动添加WHERE关键字
  • 自动去除多余的AND或OR前缀
  • 当没有条件时,不生成WHERE子句

实际应用

<select id="selectUsersByMap" parameterType="map" resultType="User">
    SELECT id, name, age, email FROM users
    <where>
        <if test="name != null and name != ''">
            AND name LIKE CONCAT('%', #{name}, '%')
        </if>
        <if test="minAge != null">
            AND age >= #{minAge}
        </if>
        <if test="maxAge != null">
            AND age <= #{maxAge}
        </if>
        <if test="email != null and email != ''">
            AND email = #{email}
        </if>
    </where>
</select>

set标签

set标签用于动态生成SET子句,常用于UPDATE语句中的选择性更新。

解决的问题

  • 自动添加SET关键字
  • 自动去除多余的逗号
  • 只更新非空字段

实际应用

<update id="updateUserSelective" parameterType="User">
    UPDATE users
    <set>
        <if test="name != null and name != ''">
            name = #{name},
        </if>
        <if test="age != null">
            age = #{age},
        </if>
        <if test="email != null and email != ''">
            email = #{email},
        </if>
    </set>
    WHERE id = #{id}
</update>

foreach标签

foreach标签用于遍历集合,常用于IN查询、批量插入、批量删除等场景。

属性说明

  • collection:要遍历的集合
  • item:集合中每个元素的别名
  • index:当前元素的索引
  • open:开始字符
  • close:结束字符
  • separator:分隔符

IN查询示例

<select id="selectUsersByIds" resultType="User">
    SELECT id, name, age, email FROM users
    WHERE id IN
    <foreach collection="ids" item="id" open="(" close=")" separator=",">
        #{id}
    </foreach>
</select>

批量插入示例

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

批量删除示例

<delete id="batchDeleteUsers">
    DELETE FROM users
    WHERE id IN
    <foreach collection="ids" item="id" open="(" close=")" separator=",">
        #{id}
    </foreach>
</delete>

trim标签

trim标签是最灵活的标签,可以自定义前缀、后缀,以及要去除的前缀和后缀。

属性说明

  • prefix:添加的前缀
  • suffix:添加的后缀
  • prefixOverrides:要去除的前缀
  • suffixOverrides:要去除的后缀

实际应用

<select id="selectUsersByTrim" resultType="User">
    SELECT id, name, age, email FROM users
    <trim prefix="WHERE" prefixOverrides="AND |OR ">
        <if test="name != null and name != ''">
            AND name LIKE CONCAT('%', #{name}, '%')
        </if>
        <if test="age != null">
            AND age = #{age}
        </if>
    </trim>
</select>

等价关系

<!-- where标签等价于 -->
<trim prefix="WHERE" prefixOverrides="AND |OR ">
    ...
</trim>

<!-- set标签等价于 -->
<trim prefix="SET" suffixOverrides=",">
    ...
</trim>

SQL片段

SQL片段用于定义可重用的SQL代码块,提高代码复用性和维护性。

定义SQL片段

<!-- 定义常用的列名 -->
<sql id="userColumns">
    id, name, age, email
</sql>

<!-- 定义表名 -->
<sql id="userTable">
    users
</sql>

<!-- 定义常用的WHERE条件 -->
<sql id="userWhereCondition">
    <where>
        <if test="name != null and name != ''">
            AND name LIKE CONCAT('%', #{name}, '%')
        </if>
        <if test="age != null">
            AND age = #{age}
        </if>
    </where>
</sql>

引用SQL片段

<select id="selectUsersWithFragment" resultType="User">
    SELECT <include refid="userColumns"/> 
    FROM <include refid="userTable"/>
    <include refid="userWhereCondition"/>
    ORDER BY id
</select>

带参数的SQL片段

<sql id="orderBy">
    ORDER BY ${column} ${direction}
</sql>

<select id="selectUsersWithOrder" resultType="User">
    SELECT <include refid="userColumns"/> FROM <include refid="userTable"/>
    <include refid="orderBy">
        <property name="column" value="name"/>
        <property name="direction" value="ASC"/>
    </include>
</select>

动态SQL最佳实践

1. 性能优化

  • 避免在动态SQL中使用复杂的逻辑判断
  • 合理使用索引,确保动态生成的WHERE条件能够利用索引
  • 批量操作时控制批次大小,避免单次操作数据量过大
  • 使用合适的参数类型,避免不必要的类型转换

2. 安全考虑

  • 始终使用#{}进行参数绑定,避免SQL注入
  • 谨慎使用${},仅在必要时使用(如动态表名、列名)
  • 对用户输入进行验证和过滤
  • 避免在动态SQL中直接拼接用户输入

3. 代码组织

  • 合理使用SQL片段,提高代码复用性
  • 保持SQL语句的可读性,适当添加注释
  • 将复杂的动态SQL拆分为多个简单的片段
  • 统一命名规范,便于维护

4. 调试技巧

  • 开启SQL日志,查看实际生成的SQL语句
  • 使用单元测试验证各种条件分支
  • 在开发环境中打印SQL参数
  • 使用数据库工具验证SQL语句的正确性

常见问题与解决方案

Q: 动态SQL中的条件判断不生效?

A: 检查test表达式是否正确,注意参数名称和类型,确保传入的参数不为null。

Q: foreach标签遍历集合时出现异常?

A: 确保collection属性指向的集合不为null,检查item和index的命名是否正确。

Q: where标签没有自动去除AND前缀?

A: 确保在if条件中使用AND或OR作为前缀,where标签会自动处理这些前缀。

Q: SQL片段引用时找不到?

A: 检查sql片段的id是否正确,确保在同一个mapper文件中或者使用完整的命名空间引用。

Q: 动态SQL性能较差?

A: 检查生成的SQL是否能够利用索引,避免全表扫描,合理设计查询条件。

实践练习

练习1:条件查询

实现一个用户搜索功能,支持按姓名、年龄范围、邮箱进行模糊查询。

练习2:选择性更新

实现一个用户信息更新功能,只更新非空字段。

练习3:批量操作

实现批量插入用户和批量删除用户的功能。

练习4:复杂动态SQL

结合多种动态SQL标签,实现一个复杂的报表查询功能。

运行示例程序

步骤1:导入项目

将chapter-07-dynamic-sql.json导入到IntelliJ IDEA中。

步骤2:配置数据库

确保MySQL数据库运行正常,执行init.sql创建测试数据。

步骤3:运行程序

运行DynamicSqlDemo.java,观察各种动态SQL标签的执行效果。

步骤4:查看日志

观察控制台输出的SQL语句,理解动态SQL的生成过程。

步骤5:运行测试

执行DynamicSqlTest.java中的测试用例,验证各种动态SQL功能。
本章小结

📖 本章小结

本章我们学习了 动态SQL 的相关内容,包括:

  • 核心概念和基本原理
  • 配置方法和实践技巧
  • 应用场景和最佳实践
  • 常见问题和解决方案

下一章节 🎉

恭喜完成本章学习!

你已经掌握了 动态SQL 的核心知识
第08章结果映射继续学习 MyBatis 的更多特性和应用⏱️预计 30 分钟📊中等难度开始学习 → ← 第06章 📚 课程目录 第08章 →

动态SQL
https://clxhxhhr.top/posts/2849/
作者
clxstart
发布于
2026-09-19
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。