XML 写接口主要解决的是 Wrapper 搞不定的复杂场景,核心适用场景可以归纳为以下五类:
一、多表 JOIN 关联查询
一旦涉及多表关联,字段来源复杂,用 Wrapper 拼 JOIN 或子查询会极其难读且难调试,此时应回归 XML。
java
// Mapper 接口
List<UserDeptVO> selectUserWithDept(@Param("status") Integer status);
xml
<!-- XML -->
<select id="selectUserWithDept" resultType="com.example.vo.UserDeptVO">
SELECT u.id, u.name, d.dept_name AS deptName
FROM user u
LEFT JOIN department d ON u.dept_id = d.id
WHERE u.status = #{status}
</select>
二、复杂统计与报表查询
当查询目的不是获取原始数据,而是返回聚合结果(GROUP BY、计算字段、返回 VO 对象)时,本质是数据加工,用 XML 配合 ResultMap 更清晰。
xml
<select id="selectDeptStats" resultType="com.example.vo.DeptStatsVO">
SELECT d.dept_name,
COUNT(u.id) AS userCount,
AVG(u.age) AS avgAge
FROM department d
LEFT JOIN user u ON d.id = u.dept_id
GROUP BY d.dept_name
HAVING COUNT(u.id) > 5
</select>
三、需要深度 SQL 优化与索引控制
当你需要关心查询是否走索引、JOIN 顺序是否正确、执行计划是否合理时,手写 SQL 是必要的------可控性比省代码更重要。
xml
<!-- 明确指定索引、控制 JOIN 顺序 -->
<select id="selectOptimized" resultType="com.example.entity.Order">
SELECT /*+ INDEX(o idx_order_user_id) */ o.*
FROM `order` o FORCE INDEX (idx_create_time)
INNER JOIN user u ON o.user_id = u.id
WHERE o.create_time > #{startTime}
AND u.status = 1
ORDER BY o.create_time DESC
LIMIT 1000
</select>
四、大批量数据操作
对于海量数据的批量插入或更新,虽然 MP 提供了 saveBatch,但在极端性能要求下,手写基于 <foreach> 的批量 SQL 性能更好。
xml
<!-- 批量插入 -->
<insert id="insertBatch">
INSERT INTO user (uuid, username, age)
VALUES
<foreach collection="list" item="u" separator=",">
(#{u.uuid}, #{u.username}, #{u.age})
</foreach>
</insert>
<!-- 批量删除(IN 语句) -->
<delete id="deleteBatchIds">
DELETE FROM user
WHERE id IN
<foreach collection="idList" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</delete>
五、复杂动态条件拼接
虽然 Wrapper 的 condition 参数能处理大部分动态条件,但遇到**多条件分支(类似 switch-case)**的场景,XML 的 <choose>/<when> 标签更直观。
xml
<select id="selectUser" resultType="com.example.entity.User">
SELECT * FROM user
<where>
<choose>
<when test="id != null">
AND id = #{id}
</when>
<when test="name != null">
AND name = #{name}
</when>
<when test="age != null">
AND age = #{age}
</when>
<otherwise>
1 = 1
</otherwise>
</choose>
</where>
</select>
判断标准
一个非常实用的工程化判断标准是:如果这个 SQL,你愿意直接在数据库客户端里单独执行、调试、优化,那就手写 XML;如果这个 SQL 只是单表 CRUD 的一种表达,那就用 Mapper 自带的方法 + Wrapper。
在健康的项目架构中,通常遵循 "70% 通用 CRUD + 30% 自定义 SQL" 的混合模式。
需要我帮你整理一份 MyBatis-Plus 的分页插件配置和使用示例吗?