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

MyBatis 中动态 SQL 标签

在 MyBatis 中,动态 SQL 是构建灵活且高效的 SQL 语句的重要工具。通过使用动态 SQL 标签,开发者可以根据不同的条件动态生成 SQL 语句的各个部分,从而避免手动拼接字符串带来的复杂性和潜在的错误。MyBatis 提供了一系列动态 SQL 标签,下面将详细介绍这些标签及其相关语法。


1. <if> 标签

用途

根据条件判断是否包含某部分 SQL 片段。

属性

  • test:一个 OGNL 表达式,用于判断是否包含该部分 SQL。

语法示例

<select id="selectUsers" parameterType="map" resultType="com.example.User">
    SELECT id, username, email
    FROM users
    WHERE 1=1
    <if test="username != null">
        AND username = #{username}
    </if>
    <if test="email != null">
        AND email = #{email}
    </if>
</select>

解释

  • usernameemail 参数不为 null 时,相应的条件将被包含在 SQL 语句中。
  • 1=1 是一个常用的技巧,用于简化后续条件的拼接,避免首个条件前需要特殊处理。

2. <choose>, <when>, <otherwise> 标签

用途

类似于 Java 的 switch 语句,根据多个条件选择其中一个执行。

属性

  • <choose>:包含多个 <when> 和一个 <otherwise> 标签。
  • <when>:定义具体的条件,包含 test 属性。
  • <otherwise>:定义默认的 SQL 片段,当所有 <when> 条件都不满足时执行。

语法示例

<select id="selectUsersByCondition" parameterType="map" resultType="com.example.User">
    SELECT id, username, email
    FROM users
    WHERE
    <choose>
        <when test="username != null">
            username = #{username}
        </when>
        <when test="email != null">
            email = #{email}
        </when>
        <otherwise>
            1=1
        </otherwise>
    </choose>
</select>

解释

  • 如果 username 不为 null,则按 username 查询。
  • 否则,如果 email 不为 null,则按 email 查询。
  • 如果以上条件都不满足,则返回所有记录(1=1)。

3. <trim> 标签

用途

用于动态添加或删除 SQL 片段的前缀、后缀及指定的前缀/后缀。

属性

  • prefix:添加到修剪后的 SQL 片段前。
  • prefixOverrides:要去除的前缀(如 ANDOR)。
  • suffix:添加到修剪后的 SQL 片段后。
  • suffixOverrides:要去除的后缀。

语法示例

<select id="selectUsersWithTrim" parameterType="map" resultType="com.example.User">
    SELECT id, username, email
    FROM users
    <trim prefix="WHERE" prefixOverrides="AND |OR ">
        <if test="username != null">
            AND username = #{username}
        </if>
        <if test="email != null">
            OR email = #{email}
        </if>
    </trim>
</select>

解释

  • 自动添加 WHERE 关键字。
  • 去除多余的 AND OR 前缀,确保生成的 SQL 语句语法正确。

4. <where> 标签

用途

自动添加 WHERE 关键字,并去除多余的 ANDOR

属性

  • 无需特定属性,通常结合 <if> 标签使用。

语法示例

<select id="selectUsersWithWhere" parameterType="map" resultType="com.example.User">
    SELECT id, username, email
    FROM users
    <where>
        <if test="username != null">
            AND username = #{username}
        </if>
        <if test="email != null">
            AND email = #{email}
        </if>
    </where>
</select>

解释

  • 如果存在条件,自动添加 WHERE,并处理 AND 前缀。
  • 如果没有条件,WHERE 子句将被省略。

5. <set> 标签

用途

用于动态更新语句中,自动添加 SET 关键字并去除多余的逗号(,)。

属性

  • 无需特定属性,通常结合 <if> 标签使用。

语法示例

<update id="updateUserSelective" parameterType="com.example.User">
    UPDATE users
    <set>
        <if test="username != null">
            username = #{username},
        </if>
        <if test="email != null">
            email = #{email},
        </if>
        <if test="password != null">
            password = #{password}
        </if>
    </set>
    WHERE id = #{id}
</update>

解释

  • 自动添加 SET,并去除最后一个逗号,确保生成的 SQL 语句语法正确。

6. <foreach> 标签

用途

用于遍历集合,生成重复的 SQL 片段,如 IN 语句、批量插入等。

属性

  • collection:要遍历的集合(如 ListSet、数组)。
  • item:每次迭代的元素变量名。
  • index(可选):当前元素的索引或键。
  • separator(可选):元素之间的分隔符。
  • open(可选):生成的 SQL 片段前的字符串。
  • close(可选):生成的 SQL 片段后的字符串。

语法示例

<select id="selectUsersByIds" parameterType="list" resultType="com.example.User">
    SELECT id, username, email
    FROM users
    WHERE id IN
    <foreach collection="list" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

解释

  • 假设传入的 list[1, 2, 3],生成的 SQL 为 WHERE id IN (1,2,3)

更多示例

批量插入
<insert id="insertUsers" parameterType="list">
    INSERT INTO users (username, email, password)
    VALUES
    <foreach collection="list" item="user" separator=",">
        (#{user.username}, #{user.email}, #{user.password})
    </foreach>
</insert>
动态生成多个条件
<select id="selectUsersDynamic" parameterType="map" resultType="com.example.User">
    SELECT id, username, email
    FROM users
    <where>
        <foreach collection="conditions" item="condition" separator=" AND ">
            ${condition}
        </foreach>
    </where>
</select>

注意:使用 ${} 可能导致 SQL 注入风险,需谨慎使用。


7. <bind> 标签

用途

在 SQL 语句中绑定一个变量,通常用于复杂的表达式或计算。

属性

  • name:绑定的变量名。
  • value:绑定的值,可以是一个表达式。

语法示例

<select id="selectUsersWithBind" parameterType="map" resultType="com.example.User">
    <bind name="pattern" value="'%' + name + '%'" />
    SELECT id, username, email
    FROM users
    WHERE username LIKE #{pattern}
</select>

解释

  • 绑定一个新的变量 pattern,其值为 %name%,用于 LIKE 查询。

8. <include><sql> 标签

用途

用于定义和包含可重用的 SQL 片段,促进 SQL 代码的复用。

属性

  • <sql> 标签:
    • id:唯一标识符,用于引用。
  • <include> 标签:
    • refid:引用的 <sql> 标签的 id

语法示例

<sql id="userColumns">
    id, username, email, password
</sql>

<select id="selectAllUsers" resultType="com.example.User">
    SELECT
    <include refid="userColumns" />
    FROM users
</select>

解释

  • <sql id="userColumns"> 定义了一个可重用的 SQL 片段,包含列名称。
  • <include refid="userColumns" /> 将该 SQL 片段包含到 SELECT 语句中。

9. <selectKey> 标签

用途

用于在执行插入操作前或后获取数据库生成的键(如主键)。

属性

  • keyProperty:Java 对象中用于存储生成键的属性名。
  • resultType:键的类型。
  • orderBEFOREAFTER,指示何时执行 selectKey

语法示例

<insert id="insertUser" parameterType="com.example.User">
    <selectKey keyProperty="id" resultType="int" order="BEFORE">
        SELECT nextval('user_seq')
    </selectKey>
    INSERT INTO users (id, username, email, password)
    VALUES (#{id}, #{username}, #{email}, #{password})
</insert>

解释

  • 在插入用户之前,通过 SELECT nextval('user_seq') 获取序列的下一个值,并将其设置到 User 对象的 id 属性。

10. 综合示例

场景

假设需要根据多个条件查询用户,并且根据传入的用户 ID 列表批量删除用户。

Java 类

package com.example;

public class User {
    private Integer id;
    private String username;
    private String email;
    private String password;
    // Getter 和 Setter 方法
}

MyBatis 映射器接口

package com.example;

import java.util.List;
import java.util.Map;

public interface UserMapper {
    List<User> selectUsers(Map<String, Object> params);
    int deleteUsersByIds(List<Integer> ids);
}

MyBatis 映射器 XML(UserMapper.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.example.UserMapper">

    <!-- 可重用的 SQL 片段 -->
    <sql id="userColumns">
        id, username, email, password
    </sql>

    <!-- 结果映射 -->
    <resultMap id="UserResultMap" type="com.example.User">
        <id property="id" column="id" />
        <result property="username" column="username" />
        <result property="email" column="email" />
        <result property="password" column="password" />
    </resultMap>

    <!-- 根据条件查询用户 -->
    <select id="selectUsers" parameterType="map" resultMap="UserResultMap">
        SELECT
        <include refid="userColumns" />
        FROM users
        <where>
            <if test="username != null">
                AND username = #{username}
            </if>
            <if test="email != null">
                AND email = #{email}
            </if>
        </where>
    </select>

    <!-- 批量删除用户 -->
    <delete id="deleteUsersByIds" parameterType="list">
        DELETE FROM users
        WHERE id IN
        <foreach collection="list" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </delete>

</mapper>

调用示例

package com.example;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Arrays;

public class MyBatisDynamicSqlExample {
    public static void main(String[] args) {
        SqlSessionFactory sqlSessionFactory = MyBatisUtil.getSqlSessionFactory(); // 假设已配置好 MyBatis 工厂
        try (SqlSession session = sqlSessionFactory.openSession()) {
            UserMapper mapper = session.getMapper(UserMapper.class);

            // 查询用户
            HashMap<String, Object> params = new HashMap<>();
            params.put("username", "john_doe");
            List<User> users = mapper.selectUsers(params);
            users.forEach(user -> System.out.println(user.getUsername()));

            // 批量删除用户
            List<Integer> idsToDelete = Arrays.asList(1, 2, 3);
            int deleted = mapper.deleteUsersByIds(idsToDelete);
            System.out.println("Deleted " + deleted + " users.");

            session.commit();
        }
    }
}

生成的 SQL 语句

查询用户(当 username = "john_doe"
SELECT id, username, email, password
FROM users
WHERE username = 'john_doe'
批量删除用户(idsToDelete = [1, 2, 3]
DELETE FROM users
WHERE id IN (1,2,3)

11. 注意事项与最佳实践

1. 使用 #{} 避免 SQL 注入

  • 始终使用 #{} 来引用参数,MyBatis 会自动进行预编译,防止 SQL 注入风险。
  • 避免使用 ${},因为它会直接拼接字符串,存在安全风险。

2. 合理使用动态 SQL 标签

  • 不要过度复杂化 SQL:虽然动态 SQL 提供了强大的灵活性,但过于复杂的动态 SQL 可能导致维护困难。
  • 分解复杂查询:对于复杂的查询,考虑将其分解为多个简单的查询或使用子查询。

3. 预设集合大小

  • 在创建 ListSet 时预设初始容量,减少扩容次数,提高性能。

4. 使用 <sql><include> 复用 SQL 片段

  • 定义可重用的列、条件或其他 SQL 片段,提高代码的可维护性和复用性。

5. 配置缓存

  • 利用 MyBatis 的一级缓存(默认)和二级缓存(需要手动配置),提高查询性能。
  • 根据应用场景选择合适的缓存策略和实现类。

6. 使用结果映射 <resultMap>

  • 对于字段名称不一致、一对一或一对多的关系,使用 <resultMap> 进行详细的结果映射。

7. 事务管理

  • 确保在需要的操作后提交事务(如 session.commit()),避免数据不一致。

8. 避免在动态 SQL 中使用 ${}

  • 除非确实需要动态拼接 SQL(如动态表名),否则优先使用 #{},以防 SQL 注入。

12. 总结

MyBatis 的动态 SQL 标签为开发者提供了构建灵活且高效 SQL 语句的强大工具。通过合理使用这些标签,可以根据不同的条件动态生成复杂的 SQL 语句,提升代码的可维护性和复用性。以下是关键要点:

  • 主要动态 SQL 标签

    • <if>:根据条件包含或排除 SQL 片段。
    • <choose>, <when>, <otherwise>:条件选择,类似于 switch 语句。
    • <trim>:修剪 SQL 片段的前后缀。
    • <where>:自动添加 WHERE 并处理条件前缀。
    • <set>:自动添加 SET 并处理逗号。
    • <foreach>:遍历集合,生成重复的 SQL 片段。
    • <bind>:绑定变量或表达式。
    • <include><sql>:复用 SQL 片段。
    • <selectKey>:获取数据库生成的键(如主键)。
  • 最佳实践

    • 始终使用 #{} 绑定参数,避免 SQL 注入。
    • 利用 <sql><include> 复用常用的 SQL 片段。
    • 根据具体需求选择合适的动态 SQL 标签,避免过度复杂化。
    • 配置和使用缓存,提高查询性能。
    • 使用 <resultMap> 进行复杂的结果集映射。

http://www.kler.cn/a/505051.html

相关文章:

  • 小结:路由器和交换机的指令对比
  • DNS解析域名简记
  • C++复习
  • 详情页 路由传值
  • 【汇编】x86汇编编程寄存器资源心中有数
  • what?ngify 比 axios 更好用,更强大?
  • 后端技术选型 sa-token校验学习 中 文档学习
  • 庖丁解java(一篇文章学java)
  • 浅谈PHP之线程锁
  • 【实践】操作系统智能助手OS Copilot新功能测评
  • C语言初阶习题【30】字符串左旋
  • ECharts 折线图隐藏标点
  • Maven 配置本地仓库
  • 矩阵碰一碰发视频之视频剪辑功能开发全解析,支持OEM
  • 音频语言模型与多模态体系结构
  • redis监控会不会统计lua里面执行的命令次数
  • Docker save load 镜像 tag 为 <none>
  • 学习threejs,使用RollControls相机控制器
  • JavaScript-正则表达式方法(RegExp)
  • ref useRef React.createRef React.forwardRef
  • PostgreSQL 语法
  • MySQL数据库基本操作命令
  • gitlab runner正常连接 提示 作业挂起中,等待进入队列 解决办法
  • 基于当前最前沿的前端(Vue3 + Vite + Antdv)和后台(Spring boot)实现的低代码开发平台
  • 代码随想录算法训练营day02| 977.有序数组的平方、209.长度最小的子数组、59.螺旋矩阵II
  • 裸金属服务器和虚拟机之间的区别