查询构建器
MyBatis-Plus Wrapper 体系
MyBatis-Plus 提供了一套完整的条件封装,对应 PHP 中 DB::table()->where()->orderBy()->get() 的链式调用。
条件筛选
java
// === 比较操作 ===
wrapper.eq("name", "foo"); // = where name = 'foo'
wrapper.ne("name", "foo"); // != where name != 'foo'
wrapper.gt("age", 18); // > where age > 18
wrapper.ge("age", 18); // >= where age >= 18
wrapper.lt("age", 30); // < where age < 30
wrapper.le("age", 30); // <= where age <= 30
// === 模糊查询 ===
wrapper.like("name", "张"); // LIKE where name like '%张%'
wrapper.notLike("name", "测试"); // NOT LIKE where name not like '%测试%'
wrapper.likeLeft("name", "张"); // LIKE where name like '%张'
wrapper.likeRight("name", "张"); // LIKE where name like '张%'
// === 范围查询 ===
wrapper.between("age", 18, 30); // BETWEEN where age between 18 and 30
wrapper.notBetween("age", 18, 30); // NOT BETWEEN
wrapper.in("status", 1, 2, 3); // IN where status in (1,2,3)
wrapper.notIn("status", 0); // NOT IN
// === NULL 判断 ===
wrapper.isNull("deleted_at"); // IS NULL
wrapper.isNotNull("email"); // IS NOT NULL排序与分页
java
// 排序
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.orderByAsc("age"); // order by age asc
wrapper.orderByDesc("created_at"); // order by created_at desc
wrapper.orderBy(true, false, "status"); // order by status desc
// ⬆ true=asc, false=desc
// 分页(需要 PaginationInnerInterceptor 插件)
Page<User> page = new Page<>(1, 20); // 第1页,每页20条
Page<User> result = userMapper.selectPage(page, wrapper);
result.getRecords(); // 当前页数据 → collection
result.getTotal(); // 总记录数 → total
result.getCurrent(); // 当前页码 → current_page
result.getSize(); // 每页大小 → per_page
result.getPages(); // 总页数 → last_pageLambda 写法(避免硬编码字段名)
java
// 字符串写法(不推荐,字段名改了不会报错)
QueryWrapper<User> w = new QueryWrapper<>();
w.eq("email", "foo@bar.com"); // 如果字段改名叫 mailbox,这里静默失效
// Lambda 写法 ✅ 推荐(编译期检查)
LambdaQueryWrapper<User> w = new LambdaQueryWrapper<>();
w.eq(User::getEmail, "foo@bar.com"); // 用方法引用,安全
w.ge(User::getAge, 18);
w.like(User::getName, "张");逻辑组合(AND / OR)
java
// 默认是 AND
wrapper.eq("status", 1).eq("age", 18);
// → where status = 1 and age = 18
// OR
wrapper.eq("status", 1).or().eq("status", 2);
// → where status = 1 or status = 2
// 复杂嵌套(类比 Eloquent 的 where 闭包)
wrapper.and(w -> w.eq("age", 18).eq("city", "北京"))
.or(w -> w.eq("age", 20).eq("city", "上海"));
// → where (age = 18 and city = '北京') or (age = 20 and city = '上海')
// 对应 PHP
// ->where(function($q) { $q->where('age', 18)->where('city', '北京'); })
// ->orWhere(function($q) { $q->where('age', 20)->where('city', '上海'); })指定字段
java
// 只查询特定字段
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("id", "name", "email");
// 排除字段
wrapper.select(User.class, info -> !info.getColumn().equals("password"));
// 聚合查询
wrapper.select("count(*) as count", "status");
wrapper.groupBy("status");
// select count(*) as count, status from users group by status⚠️ 常见坑
1. Wrapper 每次都要 new,不能复用
java
// ❌ 错误:Wrapper 有状态,不能复用
private QueryWrapper<User> baseWrapper = new QueryWrapper<>();
baseWrapper.eq("deleted", 0);
// ✅ 正确:每次查询都 new
public List<User> search(String name) {
QueryWrapper<User> w = new QueryWrapper<>(); // 每次都新建
w.eq("deleted", 0);
if (name != null) w.like("name", name);
return userMapper.selectList(w);
}2. 条件为空时不追加
MyBatis-Plus 会自动判断:如果传入的值为 null,该条件不会追加到 SQL 中。
java
wrapper.eq(name != null, "name", name); // 第二个参数为 boolean,控制是否加这个条件3. 字符串字段的数字比较
java
// age 字段在数据库中是 varchar 类型
wrapper.eq("age", 18); // → where age = '18'(自动转字符串)
// 如果传 String 给数字字段,也能工作
wrapper.eq("age", "18"); // → where age = 18(自动转数字)4. selectOne 返回多条时抛异常
java
// 如果查询出多条记录,selectOne 会抛异常
User user = userMapper.selectOne(wrapper); // 多条时 → TooManyResultsException
// 确实可能存在多条的情况,用 selectList 替代
List<User> users = userMapper.selectList(wrapper);