1. 为什么需要MyBatis-Plus条件构造器速查表
在实际开发中,我们经常需要构建复杂的SQL查询条件。传统MyBatis需要手动编写XML或注解SQL,这种方式存在几个明显痛点:
- 代码可读性差:条件逻辑分散在XML和Java代码中
- 维护成本高:修改查询条件需要同时改动多个文件
- 容易出错:字符串拼接SQL容易产生语法错误和安全漏洞
- 开发效率低:简单查询也需要编写大量样板代码
MyBatis-Plus的条件构造器(Wrapper)通过链式API解决了这些问题。它提供了类型安全的查询条件构建方式,可以:
- 避免SQL注入风险
- 提高代码可读性
- 减少样板代码
- 支持Lambda表达式
但Wrapper的API方法繁多,不同场景下需要组合使用各种条件方法。开发时经常需要查阅文档,影响效率。这就是为什么我们需要一个结构清晰、分类明确的速查表。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心Wrapper类型与适用场景
2.1 QueryWrapper:基础查询构造器
QueryWrapper是最常用的条件构造器,适用于普通SELECT查询。它支持所有基础条件方法:
java复制// 基本使用示例
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "张三")
.between("age", 20, 30)
.like("email", "@example.com")
.orderByDesc("create_time");
特点:
- 支持所有比较操作:eq/ne/gt/ge/lt/le
- 支持模糊查询:like/notLike/likeLeft/likeRight
- 支持范围查询:between/notBetween/in/notIn
- 支持空值判断:isNull/isNotNull
- 支持分组排序:groupBy/orderByAsc/orderByDesc
2.2 UpdateWrapper:更新操作构造器
UpdateWrapper专为UPDATE操作设计,除了查询条件外,还能直接设置更新字段:
java复制UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("status", 0)
.set("status", 1)
.setSql("balance = balance + 100");
特殊方法:
- set():直接设置字段值
- setSql():设置SQL片段(可包含计算)
- lambda():获取LambdaUpdateWrapper
注意:UpdateWrapper的set()方法不会更新null值字段,如需更新字段为null,需使用setSql("field = null")
2.3 LambdaWrapper:类型安全构造器
LambdaWrapper通过方法引用避免硬编码字段名:
java复制LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.eq(User::getName, "张三")
.ge(User::getAge, 18)
.select(User::getId, User::getName);
优势:
- 编译时检查字段名
- IDE智能提示
- 重构友好
- 可读性更强
3. 条件方法分类速查
3.1 比较条件
| 方法名 | SQL等价 | 示例 | 说明 |
|---|---|---|---|
| eq | = | eq("name", "张三") | 等于 |
| ne | <> | ne("status", 0) | 不等于 |
| gt | > | gt("age", 18) | 大于 |
| ge | >= | ge("score", 60) | 大于等于 |
| lt | < | lt("price", 100) | 小于 |
| le | <= | le("count", 10) | 小于等于 |
3.2 模糊查询
| 方法名 | SQL等价 | 示例 | 说明 |
|---|---|---|---|
| like | LIKE '%值%' | like("name", "张") | 包含 |
| notLike | NOT LIKE '%值%' | notLike("name", "张") | 不包含 |
| likeLeft | LIKE '%值' | likeLeft("code", "001") | 左包含 |
| likeRight | LIKE '值%' | likeRight("name", "张") | 右包含 |
3.3 逻辑条件
| 方法名 | SQL等价 | 示例 | 说明 |
|---|---|---|---|
| and | AND | eq("a",1).and(i->i.eq("b",2)) | 与条件 |
| or | OR | eq("a",1).or().eq("b",2) | 或条件 |
| nested | ( ) | nested(i->i.eq("a",1).or().eq("b",2)) | 嵌套条件 |
| apply | 无 | apply("date_format(dateCol,'%Y-%m')={0}", "2023-01") | SQL片段 |
3.4 排序与分页
java复制// 排序示例
queryWrapper.orderByAsc("age", "create_time")
.orderByDesc("score");
// 分页示例(需配合Page对象)
Page<User> page = new Page<>(1, 10);
pageMapper.selectPage(page, queryWrapper);
4. 高级用法与实战技巧
4.1 动态条件构建
实际业务中,查询条件往往是动态的。推荐使用以下模式:
java复制public List<User> queryUsers(String name, Integer minAge, Integer maxAge) {
return lambdaQuery()
.eq(StringUtils.isNotBlank(name), User::getName, name)
.ge(minAge != null, User::getAge, minAge)
.le(maxAge != null, User::getAge, maxAge)
.list();
}
条件方法的第一个参数为boolean类型,为true时才会应用该条件。
4.2 子查询处理
MyBatis-Plus 3.x版本支持子查询:
java复制// 子查询示例
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("dept_id", "select id from dept where status = 1");
// EXISTS子查询
queryWrapper.exists("select 1 from user_role where user_id = t.id");
4.3 自定义SQL片段
复杂场景可以混合使用Wrapper和XML:
java复制// Mapper接口
@Select("select * from user ${ew.customSqlSegment}")
List<User> selectAll(@Param(Constants.WRAPPER) Wrapper<User> wrapper);
// 调用方式
queryWrapper.apply("create_time > '2023-01-01'")
.orderByDesc("id");
4.4 多租户实现
基于注解的多租户方案:
java复制// 实体类注解
@TableName(value = "user", autoResultMap = true)
public class User {
@TableField(tenantId = true)
private Long tenantId;
// 其他字段...
}
// 配置拦截器
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor());
return interceptor;
}
5. 常见问题解决方案
5.1 更新字段为null的问题
默认情况下,UpdateWrapper的set()方法会忽略null值。如需设置字段为null,有两种方案:
方案1:使用setSql()
java复制updateWrapper.setSql("name = null");
方案2:配置全局策略(application.yml)
yaml复制mybatis-plus:
global-config:
db-config:
logic-not-delete-value: 0
logic-delete-value: 1
insert-strategy: not_null
update-strategy: ignored # 改为允许更新null
5.2 性能优化建议
- 避免在循环中创建Wrapper
- 复杂查询考虑使用@Select注解
- 大数据量分页使用优化器:
java复制// 优化count查询
page.setOptimizeCountSql(false);
5.3 与XML的混合使用
推荐原则:
- 简单查询用Wrapper
- 复杂查询用XML
- 动态条件优先Wrapper
混合使用示例:
xml复制<!-- XML中引用Wrapper -->
<select id="selectByWrapper" resultType="User">
SELECT * FROM user ${ew.customSqlSegment}
</select>
6. 实际项目中的经验总结
-
字段命名规范:保持数据库字段名与实体属性名一致,避免Wrapper中频繁使用column参数。
-
Lambda优先原则:新项目尽量使用LambdaWrapper,老项目逐步迁移。
-
Wrapper复用:将常用查询条件封装为方法:
java复制public QueryWrapper<User> activeUsers() {
return new QueryWrapper<User>().eq("status", 1);
}
- 日志调试:开发环境开启SQL日志:
yaml复制mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
-
版本兼容性:注意MyBatis-Plus不同版本的API差异,特别是3.x与2.x的Wrapper变化。
-
复杂查询处理:对于多表关联查询,建议:
- 使用@Select注解
- 定义VO对象接收结果
- 或者使用MyBatis的原生XML映射
- 动态表名:对于分表场景,可以实现动态表名处理器:
java复制public class DynamicTableNameParser implements ITableNameHandler {
@Override
public String dynamicTableName(String sql, String tableName) {
// 根据业务逻辑返回实际表名
return tableName + "_2023";
}
}
