尧图精选

MyBatis-Plus自定义SQL实战:从Wrapper融合到复杂查询与性能优化

🕒 发布时间:2026/10/2 16:19:20 📁 来源:尧图网络
先说点实际的。MyBatis-Plus 用久了你会发现日常的 CRUD 它确实省心BaseMapper 一把梭条件构造器拼一拼单表查询基本不用写 SQL。但项目一旦复杂起来多表联查、复杂子查询、动态条件特别多的时候光靠 BaseMapper 和 Wrapper 硬拼代码会变得越来越拧巴可读性差、性能不好调还容易踩到 MyBatis-Plus 封装带来的各种“隐形坑”。这时候自定义 SQL 就是绕不开的出口。这篇文章不讲虚的全部基于我实际项目里踩过的坑和沉淀下来的写法。我尽量把自定义 SQL 的两种主流方式、和 Wrapper 配合的骚操作、复杂查询实战、批量操作优化、通用工具类设计、以及排查问题的思路一次讲透代码都能直接抄走改一改就用。1. 为什么有了 BaseMapper 还是绕不开自定义 SQL1.1 BaseMapper 的边界在哪里先明确一件事MyBatis-Plus 的 BaseMapper 提供的所有方法本质上都是围绕着单表和通用简单条件这两个前提设计的。selectById、selectList、selectPage 这些方法底层帮你封装好了“SELECT 字段 FROM 表 WHERE 条件”的模板你只需要往里填参数就行。但实际业务场景往往是这样的订单列表页需要 JOIN 用户表、JOIN 商品表还要按时间范围、状态、模糊搜索多个字段最后还要按某个字段排序并分页。报表统计场景需要 GROUP BY 按天聚合还要用 DATE_FORMAT、IFNULL、CASE WHEN 这类数据库函数。复杂子查询场景比如“查最近一个月内下单次数超过 5 次的用户列表”。某些特殊场景需要 FOR UPDATE 悲观锁、UNION 合并结果集、ON DUPLICATE KEY UPDATE 等数据库特性。以上任何一类场景用 BaseMapper 自带方法都搞不定硬用 Wrapper 去拼也不是不行但你会发现多表 JOIN 时 Wrapper 里的字段名需要手动加表别名前缀字符串拼接一多条件一复杂整条 SQL 完全没法看。可维护性极差。过一个月你再回来看那段代码得一行一行去猜这个条件拼的是什么。排查问题时你没办法把它粘到 Navicat 里直接跑还得自己去拼接变量。所以自定义 SQL 不是“会不会写”的问题而是“你必须会”的问题。它是 MyBatis-Plus 在复杂场景下的兜底方案也是性能调优的必经之路。1.2 自定义 SQL 到底“自定义”了什么MyBatis-Plus 允许你在 Mapper 接口里声明自己的方法然后用两种方式提供 SQL注解式和 XML 映射文件式。这两种方式的本质是一样的——把 SQL 的控制权交还给你同时又保留 MyBatis-Plus 的增强能力如分页插件、逻辑删除、多租户插件。这里有个常见的误区很多人认为自定义 SQL 就等于甩开 MyBatis-Plus 回到原生 MyBatis。实际上不完全对。你依然可以使用 MyBatis-Plus 的分页插件拦截你自定义的查询方法只要你方法里传入了 IPage 参数你依然可以在自定义 SQL 里使用 MyBatis-Plus 提供的一些便捷标签比如 ${ew.customSqlSegment} 直接拼条件构造器。我个人的经验判断标准很简单SQL 语句复杂程度高、动态逻辑多、需要 SQL 片段复用的优先 XML 方式。SQL 较短、条件固定、只有一两个参数的用 Select 注解就够了。这两种方式没有绝对的优劣取决于团队习惯和项目维护成本下面我分别展开讲。2. 自定义 SQL 的两种主流写法2.1 注解式 Select适合简单场景注解式写法的核心就是在 Mapper 接口方法上直接加 Select、Insert、Update、Delete 注解把 SQL 字符串写在注解里。参数通过 #{paramName} 引用方法参数。看一个最简单例子public interface OrderMapper extends BaseMapperOrder { Select(SELECT * FROM t_order WHERE order_no #{orderNo} AND status #{status}) Order selectByOrderNo(Param(orderNo) String orderNo, Param(status) Integer status); }注意这里有几个关键点方法参数如果超过一个必须用 Param 明确指定参数名否则 MyBatis 拿到的是 arg0、arg1 这种位置参数名很容易对应错。#{} 是预编译占位符最终会被替换成 ?由 PreparedStatement 绑定参数能有效防止 SQL 注入。真项目里永远不要用 ${} 直接拼接值参数。注解式写法最明显的缺点是SQL 长了以后字符串拼接换行符、转义符都写在 Java 代码里非常难阅读和维护。比如下面这种带动态条件的 SQL写在注解里就非常痛苦Select({ script, SELECT * FROM t_order, WHERE 1 1, if teststatus ! null, AND status #{status}, /if, if testkeyword ! null and keyword ! \\, AND (order_no LIKE CONCAT(%, #{keyword}, %) OR user_name LIKE CONCAT(%, #{keyword}, %)), /if, ORDER BY create_time DESC, /script }) ListOrder selectOrderList(Param(status) Integer status, Param(keyword) String keyword);你要强行用注解写动态 SQL 也不是不行MyBatis 支持在注解里写
上一篇/下一篇内容由系统自动关联 返回资讯列表 →