mybatis-plus-xml
MyBatis-Plus XML 映射
总体原则
能用 MP 自带方法或条件构造器解决的,就绝不写 XML;遇到 MP 无法高效完成的复杂 SQL(如多表联查、复杂嵌套、特殊函数等),才考虑手写 XML。
MyBatis-Plus 的核心目标就是让你少写甚至不写 SQL,但它并不排斥手写 SQL —— 反而鼓励你当工具不够用时,回归 MyBatis 的原生方式(即 XML 或注解)去实现。
✅ 完全不需要写 XML 的情况(占日常开发的 80% 以上)
1. 单表的普通增删改查
- 插入一条记录:
insert(T entity) - 根据 ID 删除:
deleteById(Serializable id) - 根据 ID 修改:
updateById(T entity) - 根据 ID 查询:
selectById(Serializable id) - 查询全部:
selectList(null)
这些操作 MP 的 BaseMapper 已经完整提供,一行代码都不用写 XML。
2. 单表带条件的查询、更新、删除
使用 LambdaQueryWrapper 或 LambdaUpdateWrapper 即可完成任何单表条件操作。
例如:
- 条件查询:
selectList(wrapper) - 条件分页查询:
selectPage(page, wrapper) - 条件更新:
update(entity, wrapper) - 条件删除:
delete(wrapper)
示例:查询年龄大于 18 且名字包含“张”的用户,按年龄降序
java
1 | LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); |
完全不写 SQL,也不需要 XML。
3. 简单的统计聚合(单表)
- 统计记录数:
selectCount(wrapper) - 获取某个字段的最大值 / 最小值 / 总和 / 平均值(需要通过
selectObjs或自定义方法,但 MP 也提供了selectMaps配合 wrapper 的select子句实现)
虽然 MP 没有直接提供 selectSum 这样的方法,但可以通过 wrapper.select("SUM(age) as totalAge") 配合 selectMaps 实现,仍然不需要 XML。
4. 批量操作(简单版)
MP 的 IService 接口提供了 saveBatch、updateBatchById 等批量方法,底层虽然还是循环执行,但已经封装好了,日常足够使用。
5. 逻辑删除、乐观锁、自动填充等特性
这些通过注解和插件实现,完全无需写 SQL。
6. 大部分分页查询
MP 的分页插件配合 selectPage 方法即可完成物理分页,无需手写 limit 语句。
❌ 必须(或强烈建议)写 XML 的场景
1. 多表关联查询(join)
MP 的 Wrapper 是单表操作的,无法直接 join 其他表。
例如:查询用户信息并关联其订单表,一次性查出 User 和 Order 的部分字段。
这种情况只能用 XML 手写 SQL。
示例(XML 片段):
xml
1 | <select id="selectUserWithOrders" resultMap="userOrderMap"> |
2. 复杂的嵌套查询 / 子查询
虽然 Wrapper 可以支持简单的 inSql、exists 等,但遇到多层嵌套、带 ANY/ALL 或关联子查询时,手写 SQL 更清晰、更可靠。
3. 一对多、多对一、多对多的结果映射
当你要将一个查询结果映射成一个包含集合字段的复杂 Java 对象时(例如一个用户带多个订单),必须使用 XML 的 <collection> 标签,因为 MP 无法自动处理这种映射。
示例:
xml
1 | <resultMap id="userWithOrders" type="User"> |
4. 需要使用数据库特定函数或语法
- 全文索引(MATCH AGAINST)
- 窗口函数(ROW_NUMBER, RANK)
- 递归查询(WITH RECURSIVE)
- 特定数据库的 JSON 操作函数
- 存储过程调用
MP 的 Wrapper 可能支持有限,而 XML 可以直接写原生 SQL。
5. 复杂动态 SQL,分支条件非常多
虽然 Wrapper 支持动态条件(通过 condition 参数),但如果你的业务逻辑里需要根据不同的场景拼接完全不同的 SQL 片段(例如多表动态 join),XML 的 <if>、<choose>、<foreach> 等标签会更加直观和易于维护。
6. 性能要求极高的批量更新或插入
MP 的 saveBatch 本质是逐条执行,性能不如 XML 的 <foreach> 配合一条 insert ... values (...), (...), (...) 语句。对于批量插入几万条数据,手写 XML 并使用 JDBC 的 rewriteBatchedStatements 更优。
7. 多表联合更新 / 删除
MP 的 update 和 delete 方法只作用于单表。如果需要 UPDATE table1 JOIN table2 ON ... 或者 DELETE t1 FROM t1 LEFT JOIN t2 ...,必须手写 SQL。
8. 需要复用复杂 SQL 片段
XML 可以使用 <sql> 和 <include> 来复用 SQL 片段,避免重复代码,MP 的 Wrapper 无法做到。
9. 自定义返回结果类型(非实体类)
例如,你需要返回一个统计报表 Map<String, Object> 或者一个 VO 类,且字段来自多张表,XML 可以直接定义 resultType 或 resultMap,使用方便。
📌 混合使用的建议方案
MP 允许你在同一个 Mapper 接口中,既继承 BaseMapper,又添加自定义方法。自定义方法的 SQL 写在对应的 XML 文件中。
java
1 | @Mapper |
然后在 UserMapper.xml 中实现该方法。
其他简单操作(如 insert、selectById)直接通过继承的 BaseMapper 使用,无需在 XML 中重复定义。
🧠 快速判断法则(一句话)
只要你的 SQL 只涉及一张表,且不需要特殊的结果映射或数据库专有语法,就优先用 MP 的 Wrapper;一旦出现多表、复杂子查询、特殊函数或一对多集合映射,就果断切换到 XML。
总结表格
| 操作类型 | 是否需要 XML | 推荐方式 |
|---|---|---|
| 单表增删改查(根据 ID) | 不需要 | BaseMapper 自带方法 |
| 单表条件查询 / 更新 / 删除(包括分页、排序、逻辑删除) | 不需要 | LambdaQueryWrapper + BaseMapper |
| 单表统计(count、sum、avg 等) | 可选(推荐用 wrapper) | wrapper.select("SUM(age)") + selectMaps |
| 多表关联查询(JOIN) | 必须 | 自定义 XML 方法 + <resultMap> |
| 多表联合更新 / 删除 | 必须 | 手写 SQL 在 XML 中 |
| 一对多 / 多对一 结果映射 | 必须 | XML <collection> / <association> |
| 复杂子查询 / 嵌套查询 | 视复杂度而定 | 简单可尝试 inSql,复杂时写 XML |
| 使用数据库特有函数(窗口函数、递归 CTE、JSON 函数等) | 必须 | 手写 SQL |
| 高批量插入(几万条) | 建议 | XML + <foreach> 或 使用 rewriteBatchedStatements |
| 存储过程调用 | 必须 | XML 中写 {call proc_name(...)} |
| 动态 SQL 分支非常复杂(多表、可选 join) | 建议 | XML 的 <if>、<choose> 更清晰 |




