MyBatis动态SQL进阶:复杂查询与性能优化实战

引言

在复杂业务场景中,SQL查询往往需要动态拼接条件、复用代码片段,并支持批量操作。MyBatis的动态SQL功能提供了强大的解决方案,本文将深入解析<choose>条件分支、<sql>片段复用、批量操作优化等核心技巧,助你写出高效、可维护的SQL映射。

一、条件分支:choose / when / otherwise标签

1.1 场景说明

假设需要实现一个商品查询接口,支持以下条件组合:

  • 按名称模糊查询
  • 按价格区间查询
  • 按状态精确查询
  • 若无条件则返回所有商品

1.2 动态SQL实现

xml 复制代码
<select id="selectGoods" resultType="Goods">
    SELECT * FROM goods
    <where>
        <choose>
            <when test="name != null">
                AND name LIKE CONCAT('%', #{name}, '%')
            </when>
            <when test="minPrice != null and maxPrice != null">
                AND price BETWEEN #{minPrice} AND #{maxPrice}
            </when>
            <when test="status != null">
                AND status = #{status}
            </when>
            <otherwise>
                AND is_deleted = 0  <!-- 默认未删除 -->
            </otherwise>
        </choose>
    </where>
</select>

1.3 执行流程解析

  1. <choose>标签内按顺序匹配第一个满足的<when>条件
  2. 若所有<when>均不满足,则执行<otherwise>
  3. <where>标签自动处理前缀AND/OR及空条件

二、SQL片段复用:sql标签与include标签

2.1 场景说明

多个查询需要复用以下内容:

  • 基础字段列表(id, name, price)
  • 公共过滤条件(未删除)

2.2 定义与引用

xml 复制代码
<!-- 定义SQL片段 -->
<sql id="Base_Column_List">
    id, name, price, status, create_time
</sql>

<sql id="Common_Where">
    AND is_deleted = 0
</sql>

<!-- 引用片段 -->
<select id="selectGoodsList" resultType="Goods">
    SELECT 
    <include refid="Base_Column_List"/>
    FROM goods
    <where>
        <include refid="Common_Where"/>
        <if test="categoryId != null">
            AND category_id = #{categoryId}
        </if>
    </where>
</select>

2.3 进阶用法:带参数的SQL片段

xml 复制代码
<sql id="Price_Filter">
    <if test="minPrice != null">
        AND price >= #{minPrice}
    </if>
    <if test="maxPrice != null">
        AND price <= #{maxPrice}
    </if>
</sql>

<!-- 使用时 -->
<select id="selectByPrice" resultType="Goods">
    SELECT * FROM goods
    <where>
        <include refid="Common_Where"/>
        <include refid="Price_Filter"/>
    </where>
</select>

三、批量操作优化

3.1 批量插入优化

传统单条插入(低效)
xml 复制代码
<insert id="insertGoods" parameterType="Goods">
    INSERT INTO goods (name, price) VALUES (#{name}, #{price})
</insert>

Java调用:

java 复制代码
for (Goods goods : list) {
    goodsMapper.insertGoods(goods);
}
批量插入(高效)
xml 复制代码
<insert id="batchInsert" parameterType="java.util.List">
    INSERT INTO goods (name, price) VALUES
    <foreach collection="list" item="item" separator=",">
        (#{item.name}, #{item.price})
    </foreach>
</insert>

3.2 批量更新优化

xml 复制代码
<update id="batchUpdatePrice" parameterType="java.util.List">
    <foreach collection="list" item="item" separator=";">
        UPDATE goods
        SET price = #{item.newPrice}
        WHERE id = #{item.id}
    </foreach>
</update>

3.3 性能关键配置

在JDBC URL中添加批处理参数:

properties 复制代码
jdbc.url=jdbc:mysql://localhost:3306/mydb?rewriteBatchedStatements=true

MyBatis全局配置启用批量模式:

xml 复制代码
<settings>
    <setting name="defaultExecutorType" value="BATCH"/>
</settings>

四、性能优化建议

  1. 减少动态SQL嵌套

    • 避免在<foreach>中嵌套其他动态标签
    • 复杂逻辑优先在Java层处理
  2. 合理使用缓存

    xml 复制代码
    <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
  3. 批量操作最佳实践

    java 复制代码
    try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
        GoodsMapper mapper = session.getMapper(GoodsMapper.class);
        for (Goods goods : list) {
            mapper.update(goods);
        }
        session.commit();
    }

五、完整示例代码

java 复制代码
// GoodsMapper.java
public interface GoodsMapper {
    List<Goods> selectGoods(@Param("name") String name,
                            @Param("minPrice") BigDecimal minPrice,
                            @Param("maxPrice") BigDecimal maxPrice,
                            @Param("status") Integer status);
    
    int batchInsert(@Param("list") List<Goods> goodsList);
}
xml 复制代码
<!-- GoodsMapper.xml -->
<mapper namespace="com.example.mapper.GoodsMapper">
    <sql id="Base_Column_List">
        id, name, price, status, create_time
    </sql>
    
    <select id="selectGoods" resultType="Goods">
        SELECT 
        <include refid="Base_Column_List"/>
        FROM goods
        <where>
            <choose>
                <when test="name != null">
                    name LIKE CONCAT('%', #{name}, '%')
                </when>
                <when test="minPrice != null and maxPrice != null">
                    price BETWEEN #{minPrice} AND #{maxPrice}
                </when>
                <otherwise>
                    status = 1
                </otherwise>
            </choose>
        </where>
    </select>
    
    <insert id="batchInsert" parameterType="list">
        INSERT INTO goods (name, price) VALUES
        <foreach collection="list" item="item" separator=",">
            (#{item.name}, #{item.price})
        </foreach>
    </insert>
</mapper>

六、总结

技术点 适用场景 性能影响 最佳实践
<choose> 多条件分支选择 优先处理高频条件
<sql>复用 字段/条件复用 避免过度抽象
批量插入 大数据量写入 配合JDBC批处理参数
动态SQL缓存 重复执行的动态查询 设置合理的flushInterval

通过灵活运用MyBatis的动态SQL特性,可显著提升复杂查询场景的开发效率和运行性能。实际开发中需根据数据量、查询复杂度、并发量等因素综合选择优化策略。

mybatis基础专栏就结束啦,期待下我下专栏mybatis进阶,感情到一定阶段了,可以深入了解一下了!🤔

相关推荐
我命由我123452 小时前
Kotlin 数据容器 - List(List 概述、创建 List、List 核心特性、List 元素访问、List 遍历)
java·开发语言·jvm·windows·java-ee·kotlin·list
武子康4 小时前
Java-80 深入浅出 RPC Dubbo 动态服务降级:从雪崩防护到配置中心秒级生效
java·分布式·后端·spring·微服务·rpc·dubbo
新world6 小时前
mybatis-plus从入门到入土(三):持久层接口之IService
mybatis
YuTaoShao7 小时前
【LeetCode 热题 100】131. 分割回文串——回溯
java·算法·leetcode·深度优先
五岁小孩7 小时前
实操使用 go pprof 对生产环境进行性能分析(问题定位及代码优化)
性能优化·golang·pprof
源码_V_saaskw7 小时前
JAVA图文短视频交友+自营商城系统源码支持小程序+Android+IOS+H5
java·微信小程序·小程序·uni-app·音视频·交友
超浪的晨7 小时前
Java UDP 通信详解:从基础到实战,彻底掌握无连接网络编程
java·开发语言·后端·学习·个人开发
双力臂4048 小时前
Spring Boot 单元测试进阶:JUnit5 + Mock测试与切片测试实战及覆盖率报告生成
java·spring boot·后端·单元测试
Edingbrugh.南空8 小时前
Aerospike与Redis深度对比:从架构到性能的全方位解析
java·开发语言·spring
QQ_4376643149 小时前
C++11 右值引用 Lambda 表达式
java·开发语言·c++