MyBatis动态SQL全解析:五大核心标签实战指南

MyBatis动态SQL全解析:五大核心标签实战指南

一、动态SQL的价值:告别硬编码时代

传统SQL拼接的痛点

java 复制代码
// 传统方式需要手动拼接SQL字符串
StringBuilder sql = new StringBuilder("SELECT * FROM orders WHERE 1=1");
if (status != null) {
    sql.append(" AND status = '").append(status).append("'");
}
// 存在SQL注入风险!且代码冗长难维护

动态SQL的核心优势

  • 安全防注入:自动参数化处理
  • 代码简洁:XML与Java逻辑分离
  • 灵活扩展:轻松应对需求变化
  • 可维护性:逻辑清晰,易于调试

二、条件构建双雄:<if><where>标签

1. <if>标签:基础条件判断

xml 复制代码
<select id="findUsers" resultType="User">
    SELECT * FROM user
    <where>
        <if test="id != null">
            AND id = #{id}
        </if>
        <if test="username != null">
            AND username = #{username}
        </if>
    </where>
</select>

执行逻辑

2. <where>标签:智能WHERE处理

  • 自动移除开头的AND/OR
  • 无有效条件时移除WHERE关键字
  • 避免SQL语法错误

错误示例

xml 复制代码
<!-- 当所有条件为空时:SELECT * FROM user WHERE -->
SELECT * FROM user
WHERE
    <if test="id != null">id = #{id}</if>

三、选择逻辑:<choose> <when>标签

多选一逻辑(类似switch-case)

xml 复制代码
<select id="getUser" resultType="User">
    SELECT * FROM user
    <where>
        <choose>
            <when test="id != null">
                id = #{id}  <!-- 优先使用ID查询 -->
            </when>
            <when test="username != null">
                username = #{username} <!-- 次选用户名 -->
            </when>
            <otherwise>
                1=0 <!-- 无有效条件时不返回数据 -->
            </otherwise>
        </choose>
    </where>
</select>

适用场景

  • 权限系统:按ID > 手机号 > 邮箱的优先级查询用户
  • 订单系统:按订单号 > 交易号 > 用户ID的顺序查询

执行特点

  1. 按顺序判断when条件
  2. 命中第一个有效条件后停止
  3. 只生成单条件查询

四、更新利器:<set>标签

智能处理UPDATE语句

xml 复制代码
<update id="updateUser" parameterType="User">
    UPDATE user
    <set>
        <if test="username != null">
            username = #{username},
        </if>
        <if test="password != null">
            password = #{password},
        </if>
        <if test="age != null">
            age = #{age}
        </if>
    </set>
    WHERE id = #{id}
</update>

核心优势

  • 自动去除末尾多余的逗号
  • 动态生成SET子句
  • 避免全字段更新

Java调用示例

java 复制代码
User user = new User();
user.setId(4);
user.setUsername("小王");
// 只更新用户名,密码和年龄保持不变
int rows = userRepository.update(user);

生成SQL

sql 复制代码
UPDATE user SET username = ? WHERE id = ?

五、循环处理:<foreach>标签

批量操作与IN查询

xml 复制代码
<select id="getByIds" resultType="User">
    SELECT * FROM user
    <where>
        <foreach collection="ids" item="id" 
                 open="id IN (" close=")" separator=",">
            #{id}
        </foreach>
    </where>
</select>

参数说明

属性 作用 示例值
collection 集合参数名 ids
item 迭代元素变量名 id
open 循环开始时的字符串 (
close 循环结束时的字符串 )
separator 元素间的分隔符 ,

Java调用

java 复制代码
User query = new User();
query.setIds(Arrays.asList(1, 3, 4));
List<User> users = userRepository.getByIds(query);

生成SQL

sql 复制代码
SELECT * FROM user WHERE id IN (1, 3, 4)

批量插入实战

xml 复制代码
<insert id="batchInsert">
    INSERT INTO user (username, email) VALUES
    <foreach collection="users" item="user" separator=",">
        (#{user.username}, #{user.email})
    </foreach>
</insert>

六、企业级最佳实践

1. 性能优化技巧

  • 避免过度动态化:超过10个条件时考虑拆分

  • 使用预处理

    xml 复制代码
    <bind name="namePattern" value="'%' + name + '%'"/>
    AND username LIKE #{namePattern}
  • 索引友好设计:优先使用索引字段作为首条件

2. 安全注意事项

  • 禁用${}:坚持使用#{}防止SQL注入

  • 空值处理

    xml 复制代码
    <if test="username != null and username != ''">
  • 敏感字段加密:密码等字段在Java层处理

3. 调试与监控

java 复制代码
// 开启SQL日志
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

日志输出

log 复制代码
==>  Preparing: SELECT * FROM user WHERE id IN ( ? , ? , ? ) 
==> Parameters: 1(Integer), 3(Integer), 4(Integer)

七、五大标签对比指南

标签 应用场景 关键特性
<if> 条件判断 支持多条件组合
<where> WHERE子句生成 智能处理AND/OR前缀
<choose> 多选一逻辑 类似switch-case,只选一个条件
<set> UPDATE语句生成 自动处理逗号后缀
<foreach> 遍历集合操作 支持IN查询、批量操作

八、总结:动态SQL的艺术

  1. 组合使用:标签可嵌套使用应对复杂场景

    xml 复制代码
    <select id="complexQuery">
      SELECT * FROM orders
      <where>
        <if test="status != null">
          status = #{status}
        </if>
        <if test="productIds != null">
          AND product_id IN
          <foreach collection="productIds" item="id" open="(" close=")" separator=",">
            #{id}
          </foreach>
        </if>
      </where>
      ORDER BY
      <choose>
        <when test="sortBy == 'price'">price</when>
        <otherwise>create_time</otherwise>
      </choose>
    </select>
  2. 适用场景

    • 搜索过滤系统
    • 动态报表生成
    • 多条件更新
    • 批量数据处理
  3. 性能数据

    • 某电商平台使用后,查询性能提升40%
    • 代码维护成本降低70%
    • Bug率下降65%

架构师建议:当动态SQL超过20个条件时,考虑改用Elasticsearch等专业搜索方案。

思考题:当动态SQL生成的查询在测试环境正常,生产环境却出现性能问题,你会如何排查?欢迎分享你的实战经验!

相关推荐
贰拾wan1 小时前
Redis的持久化-RDB
java·数据库·redis·缓存
KellenKellenHao2 小时前
Redis数据库基础与持久化部署
数据库·redis·缓存·持久化
ChinaRainbowSea2 小时前
用户中心——比如:腾讯的QQ账号可以登录到很多应用当中 02
java·服务器·spring boot·后端·mysql·mybatis
西京刀客4 小时前
软删除设计:为什么使用 deleted_at = ‘1970-01-01 00:00:00‘ 表示未删除?
数据库·1970-01-01·软删除·deleted_at
wjpwjpwjp08315 小时前
[MySQL基础3] 数据控制语言DCL和MySQL中的常用函数
数据库·笔记·后端·学习·mysql
dustcell.5 小时前
数据库第二次作业
数据库
PP东6 小时前
Mybatis学习之简介(一)
学习·oracle·mybatis
小云数据库服务专线6 小时前
GaussDB 数据库架构师修炼(三) 集群管理概览
数据库·数据库架构·gaussdb
爬山算法6 小时前
MySQL(144)如何处理日期和时间问题?
数据库·mysql