<sql>定义可复用SQL片段,<include>引用它;作用是减少重复、统一维护、提升可读性,id与refid须一致,支持动态标签和参数绑定。

在 MyBatis 中,可以通过 <sql> 标签定义可复用的 SQL 片段,再用 <include> 标签引用它,从而避免重复编写 WHERE 条件、字段列表或 JOIN 逻辑。
定义公共 SQL 片段(<sql>)
把重复出现的 SQL 内容(如常用字段、通用查询条件)提取到 <sql> 标签中,并通过 id 属性唯一标识。支持使用 <bind> 或参数占位符(#{})动态传值。
- 必须放在
<mapper>内,作用域为当前 mapper 文件 - 可以嵌套其他动态标签(如
<if>、<where>) - 推荐命名清晰,例如
base_columns、user_condition
示例:
<sql id="base_columns">
id, username, email, status, create_time
</sql>
<sql id="user_condition">
<where>
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="status != null">
AND status = #{status}
</if>
</where>
</sql>
引用 SQL 片段(<include>)
在 <select>、<update>、<delete> 等语句中,用 <include refid="xxx"> 插入已定义的片段。若片段中含参数(如 #{username}),直接使用即可,MyBatis 会自动绑定当前方法传入的参数对象或 Map。
立即学习“Java免费学习笔记(深入)”;
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
-
refid值必须与<sql>的id完全一致 - 跨 namespace 引用需写成
namespace.id,例如com.example.UserMapper.base_columns - 不支持传递新参数给
<sql>片段(MyBatis 原生不支持参数化<sql>),但可通过外部参数 + 动态标签间接实现
示例:
<select id="selectUsers" resultType="User"> SELECT <include refid="base_columns"/> FROM user <include refid="user_condition"/> </select> <select id="countUsers" resultType="int"> SELECT COUNT(*) FROM user <include refid="user_condition"/> </select>
注意事项和常见问题
虽然 <sql> + <include> 很实用,但要注意几个易错点:
-
<sql>标签不能包含顶层动态标签(如单独写一个<where>没问题,但不能只写<if>而不包裹在<where>或<set>中) - SQL 片段内不要以逗号、AND、OR 开头或结尾,否则拼接后易出语法错误;建议用
<where>/<set>封装条件类片段 - 多个
<include>可混合使用,顺序即 SQL 拼接顺序 - IDE(如 IntelliJ)可能无法高亮或校验被 include 的 SQL,需手动确保语法正确
替代方案:使用视图或数据库函数(进阶)
当 SQL 片段逻辑复杂、跨多个 Mapper 共享,或涉及大量计算时,可考虑在数据库层抽象:
- 创建数据库视图封装常用字段+JOIN
- 封装为存储过程或自定义函数(适合强业务逻辑)
- 结合 MyBatis 的
@SelectProvider注解,用 Java 构建动态 SQL(更灵活,但失去 XML 可读性)
不过对大多数场景,<sql> + <include> 已足够简洁高效。

















