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

MyBatis 动态查询语句详解:让 SQL 更灵活可控

MyBatis 动态查询语句详解:让 SQL 更灵活可控

在日常的数据库操作中,我们经常会遇到需要根据不同条件拼接 SQL 语句的场景。比如查询用户时,可能需要根据姓名、年龄、性别等多个条件进行筛选,而这些条件往往是动态变化的 —— 有时需要按姓名查,有时需要按年龄查,有时又需要组合多个条件。如果手动拼接 SQL,不仅容易出错,还会让代码变得臃肿难维护。MyBatis 的动态查询语句正是为解决这一问题而生,它能根据参数的不同自动拼接 SQL 片段,让 SQL 编写更加灵活高效。

一、动态查询的核心价值

动态查询是 MyBatis 的核心特性之一,它允许我们在 XML 映射文件或注解中,通过一系列标签控制 SQL 片段的拼接逻辑。其核心价值体现在三个方面:

  • 减少冗余代码:无需为不同条件组合编写大量重复的 SQL 语句。

  • 避免 SQL 注入风险:MyBatis 的动态标签会自动处理参数拼接,比手动字符串拼接更安全。

  • 提升代码可读性:将条件判断逻辑与 SQL 语句分离,让业务逻辑更清晰。

举个简单的例子:查询用户列表时,可能需要根据姓名(name)和年龄(age)筛选。如果没有动态查询,我们可能需要编写 3 条 SQL(只查 name、只查 age、同时查 name 和 age);而有了动态查询,1 条 SQL 就能搞定所有场景。

二、常用动态查询标签详解

MyBatis 提供了一套完整的动态 SQL 标签,涵盖了大多数条件判断场景。下面介绍最常用的几个标签及其用法。

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

<if>标签是动态查询中最常用的标签,用于根据参数值决定是否拼接某个 SQL 片段。其语法如下:

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

示例:根据姓名和年龄查询用户

<select id="selectUser" resultType="User">SELECT * FROM userWHERE 1=1<if test="name != null and name != ''">AND name LIKE CONCAT('%', #{name}, '%')</if><if test="age != null">AND age = #{age}</if>
</select>

这里的WHERE 1=1是为了避免当所有<if>条件都不满足时,SQL 出现WHERE后无内容的语法错误。当name不为空时,会拼接AND name LIKE ...;当age不为空时,会拼接AND age = ...

2. <choose>+<when>+<otherwise>:多条件分支选择

当需要实现 “多条件选其一” 的逻辑时,可以使用<choose>标签(类似 Java 中的switch),配合<when>(类似case)和<otherwise>(类似default)标签使用。

示例:优先按姓名查询,若姓名为空则按年龄查询,否则查询所有

<select id="selectUserByCondition" resultType="User">SELECT * FROM user<where><choose><when test="name != null and name != ''">name LIKE CONCAT('%', #{name}, '%')</when><when test="age != null">age = #{age}</when><otherwise>1=1  <!-- 当所有条件都不满足时,查询所有数据 --></otherwise></choose></where>
</select>

<where>标签会自动处理 SQL 片段开头的ANDOR,避免语法错误。比如当第一个<when>条件满足时,拼接的name LIKE ...前不会有多余的AND

3. <trim>:自定义 SQL 片段拼接规则

<trim>标签可以通过属性自定义 SQL 片段的前缀、后缀,以及需要去掉的前缀或后缀字符,比<where>更灵活。常用属性:

  • prefix:给拼接的 SQL 片段添加前缀

  • suffix:给拼接的 SQL 片段添加后缀

  • prefixOverrides:去掉片段开头的指定字符

  • suffixOverrides:去掉片段结尾的指定字符

示例:用<trim>实现<where>的功能

<select id="selectUser" resultType="User">SELECT * FROM user<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>

<trim>内部有内容时,会添加WHERE前缀,并去掉开头的ANDOR,效果与<where>一致。

4. <foreach>:遍历集合参数

当需要处理批量操作(如IN查询、批量插入)时,<foreach>标签非常有用。它可以遍历数组或集合,将元素拼接成 SQL 片段。常用属性:

  • collection:指定要遍历的集合参数名(如listarray@Param定义的名称)

  • item:遍历过程中每个元素的别名

  • index:遍历索引(可选)

  • open:片段开头的字符

  • close:片段结尾的字符

  • separator:元素之间的分隔符

示例 1:批量查询(IN 语句)

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

ids[1,2,3]时,会拼接成WHERE id IN (1,2,3)

示例 2:批量插入

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

list包含 3 个 User 对象时,会拼接成INSERT INTO user (name, age) VALUES (?,?), (?,?), (?,?)

5. <set>:更新语句的动态处理

在更新操作中,<set>标签用于动态拼接SET子句,会自动去掉多余的逗号。

示例:动态更新用户信息

<update id="updateUser">UPDATE user<set><if test="name != null and name != ''">name = #{name},</if><if test="age != null">age = #{age},</if></set>WHERE id = #{id}
</update>

nameage都不为空时,会拼接成UPDATE user SET name = ?, age = ? WHERE id = ?,自动去掉age = ?后的逗号。

三、实战案例:综合运用动态标签

下面通过一个复杂案例,展示如何综合运用上述标签实现多条件组合查询。

需求:查询商品列表,支持按分类(category)、价格范围(minPrice、maxPrice)、是否库存(hasStock)筛选,且支持排序(sortField、sortType)。

Mapper 接口

List<Product> selectProducts(@Param("category") String category,@Param("minPrice") BigDecimal minPrice,@Param("maxPrice") BigDecimal maxPrice,@Param("hasStock") Boolean hasStock,@Param("sortField") String sortField,@Param("sortType") String sortType
);

XML 映射文件

<select id="selectProducts" resultType="Product">SELECT * FROM product<trim prefix="WHERE" prefixOverrides="AND | OR"><if test="category != null and category != ''">AND category = #{category}</if><if test="minPrice != null">AND price >= #{minPrice}</if><if test="maxPrice != null">AND price <= #{maxPrice}</if><if test="hasStock != null">AND stock > 0</if></trim><if test="sortField != null and sortField != '' and sortType != null">ORDER BY ${sortField} ${sortType}  <!-- 排序字段用${},注意SQL注入风险 --></if>
</select>

说明

  • <trim>处理查询条件,避免多余的AND

  • <if>判断每个筛选条件是否生效

  • 排序部分通过<if>控制是否拼接ORDER BY,注意排序字段使用${}(因为#{}会加引号,导致语法错误),但需确保sortFieldsortType是可信参数,避免 SQL 注入。

四、注意事项与最佳实践

  1. 参数判断的细节
  • 字符串判断:test="name != null and name != ''"(先判断非空,再判断非空字符串)

  • 数字判断:test="age != null"(无需判断空字符串)

  • 布尔值判断:test="hasStock != null and hasStock"(直接用参数名判断 true/false)

  1. SQL 注入风险
  • 动态 SQL 中,#{}会自动加引号,适合参数值;${}直接拼接 SQL,适合表名、字段名等,但需严格校验参数,避免注入。

  • 排序、分组等场景需用${}时,建议限制可选值(如sortField只能是idprice等已知字段)。

  1. 代码可读性
  • 复杂动态 SQL 建议拆分到 XML 文件,而非注解中

  • 给参数添加@Param注解,避免使用arg0arg1

  • 合理使用换行和缩进,保持 SQL 结构清晰

  1. 性能优化
  • 避免过度复杂的动态 SQL,否则可能影响数据库优化器生成执行计划

  • 频繁执行的动态 SQL,建议通过 MyBatis 的二级缓存缓存结果

五、总结

MyBatis 的动态查询语句通过<if><choose><foreach>等标签,完美解决了 SQL 条件动态拼接的问题,让开发者能更专注于业务逻辑而非 SQL 语法细节。掌握这些标签的用法,不仅能减少代码量,还能提升系统的灵活性和可维护性。

在实际开发中,建议结合具体业务场景选择合适的标签,同时注意参数校验和 SQL 注入风险,让动态 SQL 成为提升开发效率的利器而非隐患。

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

相关文章:

  • 前端调用阿里云接口语音合成演示
  • 20人团队文件共享选哪款?群晖DS925+ 和 DS1525+深度对比
  • 反射基础知识初入(up晚上回家再补完剩下的)
  • Anomalib:在Linux服务器上安装使用Anomalib 2.1.0
  • 生意参谋-市场竞争分析-提升商品成长效率
  • PostgreSQL 中的金钱计算处理
  • C语言第十章内存函数
  • 《SQLAlchemy 2 In Practice》读后感
  • win与ubuntu双系统安装笔记
  • 小波函数多尺度变换的 Curvelet 变换
  • vue3项目,使用vue2方式来写,可以吗
  • 【嵌入式】CAN通信
  • 基于XGBoost算法的数据回归预测 极限梯度提升算法 XGBoost
  • 虚拟机部署HDFS集群
  • JDK 工具
  • IDEA(十四) IntelliJ Idea 常用快捷键(Mac)
  • 会计人员职业发展框架:核心能力构建与进阶路径
  • ROADS落地的架构蓝图
  • Java 通过 m3u8 链接下载所有 ts 视频切片并合并转换为 mp4 格式
  • Odoo 18 通用图片导入工具:从零到一的企业级开发实战
  • 记录一次ubuntu系统下ovito无法调用显卡驱动报错
  • keepalived的配置
  • Java内置注解
  • 区块链技术:重塑未来互联网的伟大动力
  • 中金所股指期货交易规则
  • c++之指针和引用
  • 第三十三天(信号量)
  • 大模型—— DeepSeek V3.1 Base / Instruct 发布
  • Mqtt — 使用详解EMQX,MQTTX
  • Annexin V应用指南--多领域应用与实验陷阱规避