MyBatis-Plus 中 的动态SQL 片段(sqlSegment)讲解
以下是针对 MyBatis-Plus 中 通用 SQL 片段(sqlSegment) 的清晰详解,结合核心功能与实用场景,逐步说明其用法:
通用 SQL 片段(sqlSegment)
- 一、什么是 `sqlSegment`?
- 二、核心使用方式
- 1. **条件构造器(Wrapper)动态生成 SQL 片段**
- 2. **在 XML 中引用 Wrapper 的 SQL 片段**
- 三、关键语法解析
- 1. **`${ew.customSqlSegment}` 的作用**
- 2. **Lambda 表达式避免硬编码字段名**
- 3. **${ew.customSqlSegment} 与 ${ew.sqlSegment} 的区别对比**
- 四、实际应用场景
- 场景 1:动态条件查询
- 场景 2:复用公共 SQL 片段
- 场景 3:逻辑删除自动过滤
- 五、最佳实践与避坑指南
- 1. **优先使用 LambdaQueryWrapper**
- 2. **谨慎使用 `${}` 防止 SQL 注入**
- 3. **复杂 SQL 结合 XML 片段**
- 六、总结
一、什么是 sqlSegment
?
在 MyBatis-Plus 中,sqlSegment
通常指 动态生成的 SQL 代码块,例如由条件构造器(Wrapper
)自动生成的 WHERE
条件、ORDER BY
排序等。开发者可通过特定语法(如 ${ew.customSqlSegment}
或者${ew.sqlSegment}
)将这些片段插入到自定义 SQL 中,实现灵活组合。
二、核心使用方式
1. 条件构造器(Wrapper)动态生成 SQL 片段
通过 QueryWrapper
或 LambdaQueryWrapper
构建条件,自动生成 WHERE
后的条件语句。
// 创建 Wrapper
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("status", 1).like("username", "Tom").orderByDesc("create_time");// 执行查询(自动拼接条件到 SQL)
List<User> users = userMapper.selectList(wrapper);
生成 SQL:
SELECT * FROM user
WHERE status = 1 AND username LIKE '%Tom%'
ORDER BY create_time DESC
2. 在 XML 中引用 Wrapper 的 SQL 片段
使用 ${ew.customSqlSegment}
占位符,将 Wrapper
中的条件插入到自定义 SQL。
<!-- XML 映射文件 -->
<select id="selectByWrapper" resultType="User">SELECT * FROM userWHERE age > 18${ew.customSqlSegment} <!-- 插入 Wrapper 的条件 -->
</select>
Java 调用:
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.ne("email", null); // 添加条件:email IS NOT NULL
List<User> users = userMapper.selectByWrapper(wrapper);
生成 SQL:
SELECT * FROM user
WHERE age > 18AND email IS NOT NULL <!-- 来自 Wrapper -->
三、关键语法解析
1. ${ew.customSqlSegment}
的作用
ew
是 MyBatis-Plus 的固定参数名,代表Wrapper
对象。customSqlSegment
是Wrapper
中生成的 SQL 片段(如WHERE
条件、ORDER BY
)。- 注意:必须确保
Wrapper
对象作为参数传递到 XML 中(通常参数名为ew
)。
2. Lambda 表达式避免硬编码字段名
使用 LambdaQueryWrapper
提升代码安全性和可读性:
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.eq(User::getStatus, 1) // 方法引用代替字符串.ge(User::getAge, 18).orderByAsc(User::getCreateTime);
3. ${ew.customSqlSegment} 与 ${ew.sqlSegment} 的区别对比
1.${ew.customSqlSegment}:动态拼接完整 WHERE 条件:常用于无需手动编写 WHERE 的自定义 SQL 中
2.${ew.sqlSegment}:仅注入条件表达式:需手动拼接 WHERE,适用于需要精确控制 SQL 结构的场景
// Mapper 接口
@Select("SELECT * FROM user ${ew.customSqlSegment}")
List<User> selectPageCustom(@Param(Constants.WRAPPER) Wrapper<User> wrapper); @Select("SELECT * FROM user WHERE ${ew.sqlSegment}")
List<User> selectPageWhere(@Param(Constants.WRAPPER) Wrapper<User> wrapper);
四、实际应用场景
场景 1:动态条件查询
根据用户输入动态拼接查询条件,无需手动编写 if
判断。
public List<User> searchUsers(String name, Integer minAge) {LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();wrapper.like(StringUtils.isNotBlank(name), User::getUsername, name).ge(minAge != null, User::getAge, minAge);return userMapper.selectList(wrapper);
}
like
和ge
方法中的第一个参数为条件布尔值,自动决定是否拼接该条件。
场景 2:复用公共 SQL 片段
在 XML 中定义公共片段,结合 Wrapper 实现复用。
<!-- 定义公共的 WHERE 条件 -->
<sql id="activeUser">is_deleted = 0 AND status = 'ACTIVE'
</sql><!-- 在查询中复用 -->
<select id="selectActiveUsers" resultType="User">SELECT * FROM userWHERE <include refid="activeUser"/>${ew.customSqlSegment} <!-- 追加其他动态条件 -->
</select>
Java 调用:
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("username", "Admin");
List<User> users = userMapper.selectActiveUsers(wrapper);
生成 SQL:
SELECT * FROM user
WHERE is_deleted = 0 AND status = 'ACTIVE' <!-- 公共片段 -->AND username LIKE '%Admin%' <!-- 动态条件 -->
场景 3:逻辑删除自动过滤
配置 MyBatis-Plus 全局逻辑删除,自动在所有查询中注入 WHERE is_deleted=0
。
# application.yml
mybatis-plus:global-config:db-config:logic-delete-field: isDeleted # 实体类字段名logic-delete-value: 1 # 删除后的值logic-not-delete-value: 0 # 未删除的值
- 无需手动编写条件,删除操作自动变为
UPDATE
语句,查询自动过滤已删除数据。
五、最佳实践与避坑指南
1. 优先使用 LambdaQueryWrapper
- 优势:避免字段名硬编码,编译时检查,重构友好。
- 示例:
lambdaWrapper.eq(User::getStatus, 1) // 正确性由编译器保证
2. 谨慎使用 ${}
防止 SQL 注入
- 安全写法:
WHERE username = #{param} <!-- 使用 #{} 预编译 -->
- 风险写法:
ORDER BY ${orderBy} <!-- 直接拼接字符串,需手动过滤参数 -->
3. 复杂 SQL 结合 XML 片段
对于多表 JOIN 或复杂统计,仍可在 XML 中编写完整 SQL,利用 <include>
复用片段。
<select id="selectUserWithRole" resultType="map">SELECT u.*, r.role_name FROM user uLEFT JOIN role r ON u.role_id = r.id${ew.customSqlSegment}
</select>
六、总结
- 核心机制:通过
Wrapper
生成动态 SQL 片段,结合${ew.customSqlSegment}
嵌入自定义 SQL。 - 适用场景:动态条件查询、逻辑删除、多租户隔离、公共代码复用。
- 优势:减少重复代码、提升可维护性、保持 SQL 灵活性。
通过合理利用 MyBatis-Plus 的 SQL 片段功能,可显著简化开发流程,尤其适合快速迭代的中大型项目。