Mybatis-Plus:实现自定义SQL
介绍 Mybatis-Plus 实现自定义 SQL 的适用场景、Mapper 注解写法、XML 配置、DTO 映射和参数绑定注意事项,兼顾简化开发与复杂查询维护。
Mybatis-Plus 的价值在于简化常见 CRUD,减少重复 Mapper 和 SQL。但在真实项目中,复杂联表、统计报表、窗口函数、数据库特有语法或精细化性能优化,往往仍需要自定义 SQL。合理使用自定义 SQL,可以保留 Mybatis-Plus 的便利,同时满足复杂业务查询。
什么时候需要自定义 SQL
如果只是按主键查询、分页列表、简单条件过滤,优先使用 Mybatis-Plus 的 Service、Mapper 和 Wrapper。自定义 SQL 更适合以下场景:多表关联较多;返回字段不是实体完整结构;需要聚合、分组、排序规则;需要使用数据库函数;需要通过 explain 优化执行计划。
避免为了“看起来灵活”把所有查询都写成自定义 SQL。这样会削弱 Mybatis-Plus 的维护优势,也更容易出现字段变更后 SQL 不同步的问题。
Mapper 注解方式
简单 SQL 可以直接写在 Mapper 方法注解上。适合字段少、逻辑短、不需要动态拼接的查询。
@Select("select id, name from user where status = #{status}")
List<User> selectByStatus(@Param("status") Integer status);这种方式直观,但当 SQL 变长后可读性会下降。尤其是包含动态条件、复杂排序或数据库函数时,建议放到 XML 中维护。
XML 方式更适合复杂查询
XML 是 MyBatis 传统能力,Mybatis-Plus 项目同样可以使用。通常在 Mapper 接口中声明方法,在对应 XML 中写 select、update 等语句。返回值可以是实体类、DTO,也可以是 Map,但项目中最好统一风格。
<select id="selectUserStats" resultType="com.example.UserStatsDTO">
select dept_id, count(*) as user_count
from user
where deleted = 0
group by dept_id
</select>如果查询结果不是实体表结构,使用 DTO 会更清晰。字段别名要与 DTO 属性保持一致,减少映射失败或空值问题。
参数绑定与安全
自定义 SQL 中要格外注意 #{} 和 ${} 的区别。#{} 会走预编译参数绑定,适合大多数变量;${} 是字符串拼接,可能引入 SQL 注入风险。只有在动态表名、字段名等无法预编译的场景下才谨慎使用,并且必须做白名单校验。
分页场景也要注意插件与自定义 SQL 的配合。复杂查询如果包含 group by、union 或嵌套子查询,分页 count SQL 可能需要单独优化,避免生成低效统计语句。
保持边界清晰
Mybatis-Plus 实现自定义 SQL 的关键不是把 SQL 写出来,而是保持查询职责清楚。Mapper 负责数据访问,Service 负责业务编排,DTO 负责承载查询结果。复杂 SQL 上线前建议执行 explain,确认索引命中、扫描行数和排序代价,避免把性能问题隐藏在“自定义”之下。


