五、条件构造器和常用接口

20 阅读4分钟

本章从 Wrapper 体系、QueryWrapper、UpdateWrapper、condition、LambdaWrapper 依次展开。


1. Wrapper 体系全景图:你要先记住这 6 个类

Wrapper 这一套的目的:用 Java 链式 API 拼出 SQL 的 where / order by / select / set 等片段

  • Wrapper:条件构造抽象父类
  • AbstractWrapper:封装查询条件、生成 SQL 的 where 条件
  • QueryWrapper:查询条件封装
  • UpdateWrapper:更新条件封装
  • LambdaQueryWrapper:Lambda 方式写查询条件(避免字符串字段名写错)
  • LambdaUpdateWrapper:Lambda 方式写更新条件

记忆口诀:Query 查、Update 改;Lambda 负责“字段安全”。


2. QueryWrapper:查询/排序/删除/优先级/指定列/子查询

2.1 组装查询条件(where)

典型写法:like + between + isNotNull(链式调用)

资料示例:查询“用户名包含 a,年龄 20~30,邮箱不为 null”的用户。

常用条件(按使用频率排序):

  • 等值:eq/ne
  • 范围:gt/ge/lt/lebetween
  • 模糊:like/likeLeft/likeRight
  • 集合:in/notIn
  • 空判断:isNull/isNotNull

2.2 组装排序条件(order by)

示例:年龄降序,年龄相同按 id 升序。
常用:orderByAsc/Desc,也可用 orderBy(true, isAsc, column) 做动态排序。

2.3 用 QueryWrapper 也能构建删除条件(delete where)

示例:删除 email 为空的用户:queryWrapper.isNull("email") 然后 mapper.delete(wrapper)

重点:Wrapper 不止能查,也能给 delete/update 当条件。

2.4 条件优先级(and/or 的括号问题)

你最容易踩坑的是:链式 or/and 默认按 SQL 规则结合,括号要自己控制

资料里给了两种 update 条件对比,其中第二种用 .and(i -> ...) 明确括号范围:
.like(...).and(i -> i.gt(...).or().isNull(...))

经验法则:只要你的条件里出现 “A and (B or C)” 这种结构,就优先用 and(consumer) / nested(consumer) 把括号写出来。

2.5 组装 select 子句(只查部分列)

示例:只查 usernameagequeryWrapper.select("username","age"),并配合 selectMaps 返回 Map 列表(避免实体里没查到的字段全是 null)。

2.6 子查询(inSql)

示例:inSql("id", "select id from t_user where id <= 3")
适用:你手里就是一段子查询 SQL,懒得再拆成条件 API。


3. UpdateWrapper:把 “set 子句” 和 “where 条件” 分开写

当你想写这种 SQL:

UPDATE t_user
SET age=18, email='xxx'
WHERE username LIKE '%a%' AND (age > 20 OR email IS NULL)

资料示例对应的链式写法是:先 .set(...) 再拼条件,再 update(...)

3.1 update(entity, wrapper) vs update(null, wrapper)

资料里特别强调:如果你用 UpdateWrapper.set() 来组装 set 子句,调用更新时可以 update(null, updateWrapper)

实战建议:

  • 只想用 wrapper 的 setmapper.update(null, updateWrapper)
  • 想用实体对象参与 set(非 null 字段会生效)mapper.update(entity, wrapper)

4. condition:解决“用户输入是可选条件”的最佳姿势

真实业务里,查询条件很多来自前端输入,可能为空;空条件绝对不能拼进 SQL,否则结果就错。资料里先给了 if 判断写法,再给“带 condition 参数的重载方法”,用于简化。

4.1 传统写法(if 判断)

示例:username 可能为 null,ageBegin/ageEnd 也可能为 null;用 if 决定是否拼条件。

4.2 推荐写法(condition 重载)

直接写成:

  • .like(StringUtils.isNotBlank(username), "username", "a")
  • .ge(ageBegin != null, "age", ageBegin)
  • .le(ageEnd != null, "age", ageEnd)

你之后写列表筛选页(用户名/年龄区间/状态/时间范围)基本就靠这一套,代码会干净很多。


5. LambdaQueryWrapper / LambdaUpdateWrapper:避免“字段名写错”的终极方案

Lambda 版的核心价值:字段用方法引用 User::getAge,不再手写字符串 "age" ,IDE 可重构、可编译期发现错误。

资料示例(LambdaQueryWrapper):

  • .like(..., User::getName, username)
  • .ge(..., User::getAge, ageBegin)
  • .le(..., User::getAge, ageEnd)

资料示例(LambdaUpdateWrapper):

  • .set(User::getAge, 18)
  • .like(User::getName, "a")
  • .and(i -> i.lt(User::getAge, 24).or().isNull(User::getEmail))

选择建议:新代码优先 LambdaWrapper,尤其是字段多、会重构的项目。


6. 常用接口(你写 CRUD 最常用的那一批)

在 Mapper 层你最常用的就是 BaseMapper<T>:里面自带 insert/delete/update/select 等通用 CRUD。资料里也明确了很多方法都带 Wrapper 参数:有条件就传 wrapper,没条件就传 null。-L324

你可以把它记成 3 组:

6.1 查(select*)

  • selectById(id)
  • selectList(wrapper):查列表(最常用)
  • selectOne(wrapper):期望一条(多条会异常)
  • selectCount(wrapper):计数
  • selectMaps(wrapper) / selectObjs(wrapper):只取部分字段时常用
  • selectPage(page, wrapper):分页(后面插件章会细讲)

6.2 改(update*)

  • updateById(entity)
  • update(entity, wrapper):实体 set + 条件
  • update(null, updateWrapper):wrapper set + 条件

6.3 删(delete*)

  • deleteById(id)
  • delete(wrapper)(用 QueryWrapper 拼条件)

7. 高频易错点(建议你马上避坑)

  1. 字段名到底写哪个?
    QueryWrapper.like("username", "a") 这种写法里传的是数据库字段(示例就是 username),不是实体属性别名。
    👉 所以更建议用 LambdaWrapper:User::getName
  2. or/and 没加括号导致逻辑错
    出现混合条件就用 .and(i -> ...) / .nested(i -> ...) 固化括号。
  3. 只查部分列却用实体接收,结果一堆 null
    只 select 部分列时优先 selectMaps
  4. 动态条件不要写一堆 if
    直接用 condition 重载,代码更短更稳。