MyBatis—动态 SQL

MyBatis---动态 SQL

一、动态 SQL 的核心作用

动态 SQL 主要解决以下问题:

  1. 灵活性:根据不同的输入参数生成不同的 SQL 语句(如条件查询、批量操作)。

  2. 可维护性:减少重复代码,通过标签化逻辑提高 SQL 可读性。

  3. 安全性:自动处理参数绑定,防止 SQL 注入。

二、常用动态 SQL 标签

MyBatis 提供了以下标签来实现动态 SQL:

1. <if>:条件判断
  • 根据参数值是否满足条件,决定是否包含 SQL 片段。
  • 示例:
xml 复制代码
<select id="findUser" resultType="User">
  SELECT * FROM users
  <where>
    <if test="name != null and name != ''">
      AND name = #{name}
    </if>
    <if test="age != null">
      AND age = #{age}
    </if>
  </where>
</select>
  • 说明 :如果 nameage 为空,则对应的条件不会被包含。
2. <choose>/<when>/<otherwise>:多条件选择
  • 类似于 Java 的 switch-case,按顺序判断条件。

  • 示例:

    xml 复制代码
    <select id="findUserByCondition" resultType="User">
      SELECT * FROM users
      <where>
        <choose>
          <when test="name != null">
            AND name = #{name}
          </when>
          <when test="email != null">
            AND email = #{email}
          </when>
          <otherwise>
            AND status = 'active'
          </otherwise>
        </choose>
      </where>
    </select>
  • 说明 :按顺序判断 nameemail,若都不满足则执行默认条件。

3. <where>:智能处理 WHERE 子句
  • 自动去除多余的 ANDOR,并自动添加 WHERE 关键字。

  • 示例:

    xml 复制代码
    <select id="findUser" resultType="User">
      SELECT * FROM users
      <where>
        <if test="name != null">
          AND name = #{name}
        </if>
        <if test="age != null">
          AND age = #{age}
        </if>
      </where>
    </select>
    • 结果 :若 nameage 都为空,则生成 SELECT * FROM users(无 WHERE 子句);若 name 不为空,则生成 WHERE name = ?
4. <set>:动态更新字段
  • 用于 UPDATE 语句,自动去除末尾逗号。

  • 示例:

    xml 复制代码
    <update id="updateUser">
      UPDATE users
      <set>
        <if test="name != null">
          name = #{name},
        </if>
        <if test="email != null">
          email = #{email},
        </if>
      </set>
      WHERE id = #{id}
    </update>
    • 结果 :若 nameemail 都不为空,生成 SET name = ?, email = ?;若只有一个字段不为空,自动去除末尾逗号。
5. <foreach>:遍历集合
  • 用于批量操作(如 IN 子句、批量插入)。

  • 示例:

    xml 复制代码
    <select id="findUsersByIds" resultType="User">
      SELECT * FROM users
      WHERE id IN
      <foreach item="id" collection="list" open="(" separator="," close=")">
        #{id}
      </foreach>
    </select>
    • 结果 :若传入的 list[1, 2, 3],生成 WHERE id IN (1, 2, 3)
6. <trim>:灵活拼接 SQL
  • 手动控制 SQL 片段的前缀和后缀,常用于复杂逻辑。

  • 示例:

    xml 复制代码
    <trim prefix="WHERE" prefixOverrides="AND |OR ">
      <if test="name != null">
        AND name = #{name}
      </if>
    </trim>
    • 说明 :若 name 为空,则不生成 WHERE;若 name 不为空,生成 WHERE name = ?

三、动态 SQL 的实现原理

  1. XML 解析:MyBatis 启动时加载 Mapper XML 文件,解析动态 SQL 标签。
  2. SQL 拼接:运行时根据传入参数动态生成 SQL 片段。
  3. 参数绑定 :使用 #{} 绑定参数,防止 SQL 注入。
  4. 预编译 :最终生成的 SQL 被发送给数据库驱动,创建 PreparedStatement

四、动态 SQL 的应用场景

  1. 条件查询:根据用户输入动态过滤条件。
  2. 批量操作:批量插入、更新、删除。
  3. 多表关联:根据业务需求动态关联不同表。
  4. 复杂业务逻辑:如动态排序、分页等。

五、最佳实践

  1. 避免复杂嵌套:过多嵌套会降低可读性,建议拆分逻辑。
  2. 合理使用 <where><set>:简化 SQL 片段。
  3. 测试动态 SQL:通过日志查看生成的 SQL,确保逻辑正确。
  4. 参数校验:在业务层校验参数,避免无效条件。

六、示例:综合使用动态 SQL(实战)

比如我们已经写完了controller层,entity层,mapper层,service层等Impl。

动态SQL实现:修改mapper层:(注释掉SQL注解)

java 复制代码
@Mapper
public interface UsersMapper {

    @Select("insert into users(account,password,uname,gender,age,phone,email,address,avatar,regtime,uflag) values(#{account},#{password},#{uname},#{gender},#{age},#{phone},#{email},#{address},#{avatar},#{regtime},#{uflag})")
    void addUser(Users users);

    @Select("select * from users where account=#{account}")
    Users getUserByAccount(String account);

//    @Select("select * from users where uflag = #{uflag} order by regtime desc")
    List<Users> queryUsersByUflag(Users users);

    //根据id查询用户
    @Select("select * from users where account = #{id}")
    Users queryUsersById(String id);

    //审核通过
//    @Update("update users set uflag = #{uflag} where account = #{account}")
    void updateUser(Users users);

    //删除家长
    @Delete("delete from users where account = #{id}")
    void delUsers(String id);
}

这些SQL注解我们写进resources/mapper/UsersMapper.xml这里。

例如:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bkty.turtorsystem.mapper.UsersMapper">

    <!--根据动态的条件查询用户-->
    <select id="queryUsersByUflag"  resultType="Users"
            parameterType="Users">
        select * from users
        <where>
            <if test="uflag != null and uflag != ''">
                uflag = #{uflag}
            </if>
            <if test="account != null and account != ''">
                and account = #{account}
            </if>
            <if test="uname != null and uname != ''">
                and uname = #{uname}
            </if>
            <if test="password != null and password != ''">
                and password = #{password}
            </if>
            <if test="email != null and email != ''">
                email = #{email}
            </if>
            <if test="phone != null and phone != ''">
                and phone like concat('%',#{phone},'%')
            </if>
            <if test="gender != null and gender != ''">
                and gender = #{gender}
            </if>
            <if test="age != null and age != ''">
                and age = #{age}
            </if>
            <if test="address != null and address != ''">
                address = #{address}
            </if>
            <if test="condition != null and condition != ''">
                ${condition}
            </if>
        </where>
        order by regtime desc
    </select>

    <update id="updateUser" parameterType="Users">
            update users
            <set>
                <if test="account != null and account != ''">
                    account = #{account},
                </if>
                <if test="password != null and password != ''">
                    password = #{password},
                </if>
                <if test="uname != null and uname != ''">
                    uname = #{uname},
                </if>
                <if test="gender != null and gender != ''">
                    gender = #{gender},
                </if>
                <if test="age != null and age != ''">
                    age = #{age},
                </if>
                <if test="phone != null and phone != ''">
                    phone = #{phone},
                </if>
                <if test="email != null and email != ''">
                    email = #{email},
                </if>
                <if test="address != null and address != ''">
                    address = #{address},
                </if>
                <if test="avatar != null and avatar != ''">
                    avatar = #{avatar},
                </if>
                <if test="regtime != null and regtime != ''">
                    regtime = #{regtime},
                </if>
                <if test="uflag != null and uflag != ''">
                    uflag =#{uflag},
                </if>
            </set>
            where account = #{account}
    </update>
</mapper>
java 复制代码
<mapper namespace="com.bkty.turtorsystem.mapper.UsersMapper">

这个namespace=指向我的mapper,相当于注解SQL,把注解改成了xml。

1. 查询语句:queryUsersByUflag

xml 复制代码
<select id="queryUsersByUflag" resultType="Users" parameterType="Users">
    select * from users
    <where>
        <if test="uflag != null and uflag != ''">
            uflag = #{uflag}
        </if>
        <if test="account != null and account != ''">
            and account = #{account}
        </if>
        <!-- 其他字段的 <if> 条件 -->
        <if test="condition != null and condition != ''">
            ${condition}
        </if>
    </where>
    order by regtime desc
</select>
功能说明
  • 作用 :根据传入的 Users 对象中的字段动态生成 SQL 查询条件,查询 users 表中的记录。
  • 动态条件:
    • 使用 <where> 标签包裹所有条件,MyBatis 会自动处理 ANDOR 的冗余问题(例如,如果第一个条件不成立,WHERE 关键字不会被输出)。
    • 每个 <if> 标签检查字段是否非空,若非空则添加对应的查询条件。
    • 特殊字段 condition 使用 ${condition} 直接拼接 SQL(需注意 SQL 注入风险)。
关键点
  1. 字段条件:
    • 所有字段(如 uflag, account, uname 等)都通过 <if> 动态判断是否添加到查询条件中。
    • 注意:第一个条件(uflag)没有加 AND,但 <where> 标签会自动处理这种情况,避免语法错误。
  2. condition 字段:
    • 使用 ${condition} 直接拼接原始 SQL 片段(例如 1=1status='active')。
    • 风险${} 不会进行参数绑定,存在 SQL 注入风险,需确保传入值的安全性。
  3. 排序:
    • 固定按 regtime 降序排列。
示例

假设传入的 Users 对象包含 uflag="1"account="test123",生成的 SQL 为:

sql 复制代码
SELECT * FROM users 
WHERE uflag = '1' AND account = 'test123' 
ORDER BY regtime DESC;

2. 更新语句:updateUser

xml 复制代码
<update id="updateUser" parameterType="Users">
    update users
    <set>
        <if test="account != null and account != ''">
            account = #{account},
        </if>
        <!-- 其他字段的 <if> 条件 -->
        <if test="uflag != null and uflag != ''">
            uflag =#{uflag},
        </if>
    </set>
    where account = #{account}
</update>
功能说明
  • 作用 :根据传入的 Users 对象中的字段动态更新 users 表中的记录。
  • 动态更新字段:
    • 使用 <set> 标签包裹所有字段更新逻辑,MyBatis 会自动去除末尾多余的逗号。
    • 每个 <if> 标签判断字段是否非空,若非空则更新对应字段。
  • 更新条件:
    • 使用 account = #{account} 作为更新条件(需确保 account 是唯一标识字段)。
关键点
  1. 字段更新:
    • 所有字段(如 account, password, uname 等)都通过 <if> 动态判断是否更新。
    • 注意:每个字段条件后都有逗号 ,,但 <set> 会自动去除最后一个字段的逗号。
  2. 更新条件:
    • 使用 account = #{account} 作为更新条件,需确保 account 是唯一值(否则可能更新多条记录)。
  3. 潜在问题:
    • 如果 account 不唯一,可能会导致意外更新多条记录。
    • 更推荐使用主键(如 id)作为更新条件。
示例

假设传入的 Users 对象包含 account="test123"uname="NewName",生成的 SQL 为:

sql 复制代码
UPDATE users 
SET account = 'test123', uname = 'NewName' 
WHERE account = 'test123';

七、总结

MyBatis 的动态 SQL 通过标签化逻辑,解决了传统 SQL 硬编码的问题,使代码更简洁、安全且灵活。合理使用 <if><where><foreach> 等标签,可以大幅提升开发效率和代码质量。

相关推荐
付出不多18 分钟前
Linux——mysql主从复制与读写分离
数据库·mysql
初次见面我叫泰隆19 分钟前
MySQL——1、数据库基础
数据库·adb
Chasing__Dreams24 分钟前
Redis--基础知识点--26--过期删除策略 与 淘汰策略
数据库·redis·缓存
源码云商32 分钟前
【带文档】网上点餐系统 springboot + vue 全栈项目实战(源码+数据库+万字说明文档)
数据库·vue.js·spring boot
C4程序员43 分钟前
Java百度身份证识别接口实现【配置即用】
java·开发语言
源远流长jerry1 小时前
MySQL的缓存策略
数据库·mysql·缓存
炒空心菜菜1 小时前
MapReduce 实现 WordCount
java·开发语言·ide·后端·spark·eclipse·mapreduce
纯纯沙口1 小时前
Qt—用SQLite实现简单的注册登录界面
数据库·sqlite
未来之窗软件服务1 小时前
医院药品展示大屏:开启多维度服务与管理新窗口—仙盟创梦IDE
ide·智慧大屏幕·信发系统·仙盟创梦ide
zy happy1 小时前
搭建运行若依微服务版本ruoyi-cloud最新教程
java·spring boot·spring cloud·微服务·ruoyi