MyBatis中的SQL理解

发布于:2025-06-28 ⋅ 阅读:(17) ⋅ 点赞:(0)

一、MyBatis 动态 SQL 标签详解

标签 作用 核心属性 使用场景 示例
<if> 条件判断 test="表达式" 非必填字段处理 xml <if test="gender != null">gender=#{gender}</if>
<trim> 智能去除多余字符 prefix/suffix
prefixOverrides/suffixOverrides
多字段动态插入/更新 [见下方完整示例]
<where> 动态生成 WHERE 子句,自动去除开头 AND/OR 条件查询 xml <where><if test="age!=null">AND age=#{age}</if></where>
<set> 动态生成 SET 子句,自动去除尾部逗号 更新操作 xml <set><if test="name!=null">name=#{name},</if></set>
<foreach> 遍历集合 collection/item
open/close/separator
批量删除/IN 查询 xml <foreach collection="ids" item="id" open="(" close=")" separator=",">#{id}</foreach>
<include> 引用公共 SQL 片段 refid 消除重复 SQL xml <include refid="baseColumn"/>
<sql> 定义可重用 SQL 片段 id 抽取公共字段列表 xml <sql id="baseColumn">id,name,age</sql>
<trim> 完整示例(多字段插入)
<insert id="insertUser">
  INSERT INTO user
  <trim prefix="(" suffix=")" suffixOverrides=",">
    <if test="username != null">username,</if>
    <if test="password != null">password,</if>
  </trim>
  VALUES
  <trim prefix="(" suffix=")" suffixOverrides=",">
    <if test="username != null">#{username},</if>
    <if test="password != null">#{password},</if>
  </trim>
</insert>

二、XML vs 注解方式对比

特性 XML 方式 注解方式 推荐场景
动态SQL 原生支持,结构清晰 需用<script>包裹,可读性差 复杂业务首选 XML
可维护性 SQL 与 Java 代码分离 SQL 嵌入代码中 中大型项目用 XML
多表关联 支持强大的 ResultMap 关联映射能力有限 复杂关联查询用 XML
IDE 支持 语法高亮、自动提示 无 SQL 校验 所有场景
批量操作 完整支持 拼接复杂 批量操作强制 XML
注解方式示例(不推荐)
@Update("<script>" +
        "UPDATE user " +
        "<set>" +
        "   <if test='name!=null'>name=#{name},</if>" +
        "</set>" +
        "WHERE id=#{id}" +
        "</script>")
void updateUser(User user);

三、Mapper XML 实践

1. 基础结构规范
<?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.mapper.UserMapper">
  
  <!-- 1. 公共SQL片段 -->
  <sql id="baseColumn">id,username,email</sql>
  
  <!-- 2. ResultMap映射 -->
  <resultMap id="userMap" type="User">
    <id column="id" property="id"/>
    <result column="create_time" property="createTime"/>
  </resultMap>
  
  <!-- 3. SQL语句 -->
  <select id="selectById" resultMap="userMap">
    SELECT <include refid="baseColumn"/>
    FROM user WHERE id = #{id}
  </select>
</mapper>
2. 动态 SQL 技巧
  • 避免空条件陷阱:用<where>替代WHERE 1=1

  • 批量插入优化

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

  • 安全更新:配合<set>防止全表更新

    <update id="update">
      UPDATE user
      <set>
        <if test="name != null">name=#{name}</if>
      </set>
      WHERE id = #{id} AND status = 1 <!-- 双重保险 -->
    </update>

四、项目实战:图书管理系统

1. 假设他需要我这个分页的字段在xml中
<!-- BookInfoMapper.xml -->
<select id="queryBookListByPage" resultType="BookInfo">
  SELECT id, book_name, author, count, price, publish, status
  FROM book_info
  WHERE status != 0
  ORDER BY id DESC
  LIMIT #{offset}, #{pageSize}
</select>

<select id="count" resultType="int">
  SELECT COUNT(1) FROM book_info WHERE status != 0
</select>
2. 逻辑删除 vs 物理删除
类型 实现方式 SQL 示例 优点
逻辑删除 更新状态字段 UPDATE book SET status=0 WHERE id=#{id} 数据可恢复,安全
物理删除 真实删除数据 DELETE FROM book WHERE id=#{id} 节省存储空间
3. 条件更新(动态 SQL 典范)
<update id="updateBook">
  UPDATE book_info
  <set>
    <if test="bookName != null">book_name = #{bookName},</if>
    <if test="author != null">author = #{author},</if>
    <if test="status != null">status = #{status},</if>
  </set>
  WHERE id = #{id} AND status != 0 <!-- 防止更新已删除数据 -->
</update>

五、MyBatis 高级特性

1. 结果集自动映射
# application.yml
mybatis:
  configuration:
    map-underscore-to-camel-case: true # 开启驼峰转换
    auto-mapping-behavior: full # 自动映射未知字段
2. 枚举类型处理
// 枚举转换器
public enum BookStatus {
  NORMAL(1, "可借阅"),
  FORBIDDEN(2, "不可借阅");

  private final int code;
  private final String name;
  
  // 通过code获取枚举
  public static BookStatus fromCode(int code) { ... }
}

// Mapper中使用
<result column="status" property="status" 
        typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/>
3. 二级缓存配置
<!-- Mapper.xml 开启缓存 -->
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>

<!-- 特定语句禁用缓存 -->
<select id="selectLatest" useCache="false"> ... </select>

六、避坑指南

  1. 参数引用问题

    • #{field} 防止 SQL 注入

    • ${field} 直接拼接 SQL(慎用!)

  2. 动态 SQL 陷阱

    <!-- 错误示例:逗号问题 -->
    UPDATE user 
    <set>
      <if test="name!=null">name=#{name},</if> <!-- 多余逗号会导致语法错误 -->
      age=#{age}
    </set>
    
    <!-- 正确:用<set>智能处理 -->
  3. 批量操作优化

    // 分批提交(每1000条提交一次)
    @Transactional
    void batchInsert(List<User> users) {
      SqlSession batchSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
      UserMapper mapper = batchSession.getMapper(UserMapper.class);
      for (int i = 0; i < users.size(); i++) {
        mapper.insert(users.get(i));
        if (i % 1000 == 0) batchSession.flushStatements();
      }
      batchSession.commit();
    }

七、典型应用场景解决方案

场景 解决方案
多条件模糊查询 <where> + <if test="name!=null and name!=''">AND name LIKE CONCAT('%',#{name},'%')</if>
批量逻辑删除 <update> + <foreach>
主从表关联查询 使用<resultMap><collection>标签
动态列排序 ORDER BY ${sortField} ${sortOrder} (注意注入风险)
唯一约束冲突处理 ON DUPLICATE KEY UPDATE + 动态<set>

总结

  1. 动态 SQL 优先 XML:复杂业务场景使用 XML 配置,保持代码清晰度

  2. 安全第一原则

    • 更新/删除操作必须带 WHERE 条件

    • 批量操作使用 @Transactional 保证事务

  3. 性能优化关键

    • 分页查询先查总数再查数据

    • 批量操作使用 BATCH 执行器

  4. 可维护性实践

    • 使用 <sql> 片段复用 SQL

    • 字段枚举值统一管理

通过图书管理系统实战案例,掌握 MyBatis 动态 SQL 在 CRUD 操作中的灵活应用,特别关注分页查询、条件更新、批量删除等高频场景的实现细节。


网站公告

今日签到

点亮在社区的每一天
去签到