java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > MyBatis动态SQL应用

一文详解MyBatis中动态SQL的封装原理与常用标签实战应用

作者:身如柳絮随风扬

这篇文章主要为大家详细介绍了MyBatis中动态SQL的核心技术与及常用标签的应用,同时提供了最佳实践和常见问题解决方案,感兴趣的小伙伴可以跟随小编一起学习一下

告别繁琐的SQL拼接,让动态查询像搭积木一样简单

一、为什么需要动态SQL?

在实际开发中,我们经常会遇到需要根据不同的条件拼接SQL语句的场景。传统的做法是使用Java代码进行字符串拼接,但这种方式不仅代码冗余、易出错,还存在SQL注入的风险。MyBatis提供的动态SQL机制,允许我们在XML映射文件中使用标签来构建灵活的SQL语句,让代码更清晰、更安全。

核心优势:

二、动态SQL的执行流程

MyBatis在解析动态SQL时,会根据传入的参数动态生成最终的SQL语句。下图展示了从XML到预编译SQL的完整过程:

三、常用动态SQL标签详解

1.<if>– 条件判断

最简单的条件标签,当条件满足时拼接SQL片段。

语法:

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

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

<select id="findUsers" resultType="User">
    SELECT * FROM user WHERE 1=1
    <if test="name != null and name != ''">
        AND name = #{name}
    </if>
    <if test="age != null and age > 0">
        AND age = #{age}
    </if>
</select>

注意: WHERE 1=1 是为了防止所有条件都不满足时出现语法错误。更好的替代方案是使用 <where> 标签。

2.<where>– 智能处理查询条件

自动处理AND/OR关键字,并去除多余的连接词。

示例: 重构上面的查询

<select id="findUsers" resultType="User">
    SELECT * FROM user
    <where>
        <if test="name != null and name != ''">
            AND name = #{name}
        </if>
        <if test="age != null and age > 0">
            AND age = #{age}
        </if>
    </where>
</select>

name 有值而 agenull 时,生成的SQL为:

3.<set>– 动态更新字段

<update> 语句中智能处理逗号分隔符。

示例: 动态更新用户信息

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

<set> 会自动去掉末尾多余的逗号,并保证至少有一个字段被更新。

4.<foreach>– 遍历集合/数组

常用于 IN 查询、批量插入、批量删除。

属性说明:

属性作用
collection要遍历的集合/数组名称
item每次迭代的当前元素别名
index当前元素的索引(或map的key)
open遍历开始拼接的字符串
close遍历结束拼接的字符串
separator元素之间的分隔符

示例1: 批量查询(IN条件)

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

示例2: 批量插入

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

常见坑点: collection 默认支持 listarraymap。如果是自定义参数名,需要使用 @Param 注解指定。

5.<trim>– 自定义前缀/后缀处理

<where><set> 本质上都是 <trim> 的快捷方式,当需要更精细的控制时可直接使用 <trim>

属性:

等价关系:

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

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

自定义示例: 动态添加 ORDER BY 子句

<select id="selectWithOrder" resultType="User">
    SELECT * FROM user
    <trim prefix="ORDER BY" suffixOverrides=",">
        <if test="orderByName">name,</if>
        <if test="orderByAge">age,</if>
    </trim>
</select>

6.<choose> / <when> / <otherwise>– 分支选择

类似Java的 switch-case,只执行第一个满足条件的分支。

示例: 按优先级排序查询(姓名优先,否则年龄,否则默认)

<select id="findUsersWithPriority" resultType="User">
    SELECT * FROM user
    <where>
        <choose>
            <when test="name != null">
                AND name LIKE CONCAT('%', #{name}, '%')
            </when>
            <when test="age != null">
                AND age = #{age}
            </when>
            <otherwise>
                AND status = 'ACTIVE'
            </otherwise>
        </choose>
    </where>
</select>

四、综合实战:员工管理系统动态查询

下面是一个结合多个标签的完整示例:

<mapper namespace="com.example.EmployeeMapper">
    
    <!-- 动态查询员工 -->
    <select id="searchEmployees" resultType="Employee">
        SELECT * FROM employee
        <where>
            <if test="deptId != null">
                AND dept_id = #{deptId}
            </if>
            <if test="minSalary != null">
                AND salary >= #{minSalary}
            </if>
            <if test="maxSalary != null">
                AND salary &lt;= #{maxSalary}
            </if>
            <if test="keywords != null and keywords != ''">
                AND (name LIKE CONCAT('%', #MyBatis动态SQL封装,MyBatis动态SQL使用,MyBatis动态SQL,MyBatis SQL, '%')
                     OR position LIKE CONCAT('%', #MyBatis动态SQL封装,MyBatis动态SQL使用,MyBatis动态SQL,MyBatis SQL, '%'))
            </if>
            <choose>
                <when test="hireDateStart != null and hireDateEnd != null">
                    AND hire_date BETWEEN #{hireDateStart} AND #{hireDateEnd}
                </when>
                <when test="hireDateStart != null">
                    AND hire_date >= #{hireDateStart}
                </when>
                <when test="hireDateEnd != null">
                    AND hire_date &lt;= #{hireDateEnd}
                </when>
            </choose>
        </where>
        <if test="orderBy != null">
            <trim prefix="ORDER BY" suffixOverrides=",">
                <if test="orderBy.name">name,</if>
                <if test="orderBy.salary">salary,</if>
                <if test="orderBy.hireDate">hire_date,</if>
            </trim>
        </if>
    </select>
    
</mapper>

五、最佳实践与常见问题

推荐做法

常见错误

错误现象原因解决方案
动态SQL不生效忘记在<select>中嵌入动态标签检查标签是否正确嵌套
多余的AND/OR条件未全部满足时前缀残留使用<where><trim prefixOverrides>
批量插入性能差foreach生成超大SQL改用分批处理或JDBC batch
OGNL表达式异常使用了Java的&&/`

六、总结

MyBatis的动态SQL通过一套简洁的XML标签,完美解决了传统JDBC拼接SQL的痛点。掌握以下核心标签即可应对90%的动态场景:

标签核心用途
<if>简单条件判断
<where>智能查询条件组装
<set>智能更新字段组装
<foreach>遍历集合(IN、批量操作)
<trim>自定义前缀/后缀处理
<choose>多分支互斥选择

动态SQL的本质是在XML层面基于OGNL表达式进行零逻辑的SQL片段组合。理解这一思想后,你可以灵活组合这些标签,写出既优雅又安全的动态SQL。

延伸思考: 如果动态条件非常复杂(超过5个分支),建议考虑将部分逻辑迁移到Java层,使用@SelectProvider注解或构建查询对象模式,以保持XML的可读性。

到此这篇关于一文详解MyBatis中动态SQL的封装原理与常用标签实战应用的文章就介绍到这了,更多相关MyBatis动态SQL应用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文