# MyBatis-Plus Wrapper条件构造器详解
## 📌 Wrapper 核心概念
**Wrapper** 是 MyBatis-Plus 提供的**动态 SQL 条件构造工具**,用于以面向对象的方式构建查询条件,替代传统的 XML 动态 SQL 和手写 SQL 字符串。
---
## 🏗️ Wrapper 体系结构
### 1. **Wrapper 类继承关系**
```
AbstractWrapper (抽象类)
├── QueryWrapper (普通查询包装器)
├── UpdateWrapper (更新条件包装器)
└── AbstractLambdaWrapper (抽象Lambda包装器)
├── LambdaQueryWrapper (Lambda查询包装器)
└── LambdaUpdateWrapper (Lambda更新包装器)
```
### 2. **Wrapper 类型对比**
| Wrapper 类型 | 使用方式 | 优点 | 缺点 | 适用场景 |
|-------------|---------|------|------|----------|
| **QueryWrapper** | 字符串字段名 | 灵活,支持函数式条件 | 类型不安全,字段名硬编码 | 简单查询,动态字段名 |
| **LambdaQueryWrapper** | Lambda表达式 | 类型安全,编译时检查,重构友好 | 不支持数据库函数嵌套 | 大多数业务查询 |
| **UpdateWrapper** | 字符串字段名 | 支持更新条件构造 | 类型不安全 | 带条件的更新操作 |
| **LambdaUpdateWrapper** | Lambda表达式 | 类型安全的更新条件 | 不支持复杂函数 | 带条件的类型安全更新 |
---
## 🔧 核心功能详解
### 1. **基础条件方法**
| 方法 | 说明 | SQL等价 | 示例 |
|------|------|---------|------|
| **eq** | 等于 = | `column = value` | `eq("name", "张三")` |
| **ne** | 不等于 != | `column != value` | `ne("status", 0)` |
| **gt** | 大于 > | `column > value` | `gt("age", 18)` |
| **ge** | 大于等于 >= | `column >= value` | `ge("score", 60)` |
| **lt** | 小于 < | `column < value` | `lt("price", 100)` |
| **le** | 小于等于 <= | `column <= value` | `le("stock", 0)` |
| **between** | BETWEEN | `column BETWEEN v1 AND v2` | `between("age", 18, 30)` |
| **notBetween** | NOT BETWEEN | `column NOT BETWEEN v1 AND v2` | `notBetween("price", 10, 100)` |
| **like** | LIKE '%值%' | `column LIKE '%value%'` | `like("name", "张")` |
| **notLike** | NOT LIKE | `column NOT LIKE '%value%'` | `notLike("title", "test")` |
| **likeLeft** | LIKE '%值' | `column LIKE '%value'` | `likeLeft("code", "001")` |
| **likeRight** | LIKE '值%' | `column LIKE 'value%'` | `likeRight("name", "张")` |
| **isNull** | IS NULL | `column IS NULL` | `isNull("email")` |
| **isNotNull** | IS NOT NULL | `column IS NOT NULL` | `isNotNull("phone")` |
| **in** | IN 列表 | `column IN (v1, v2, ...)` | `in("role", Arrays.asList(1, 2, 3))` |
| **notIn** | NOT IN | `column NOT IN (v1, v2, ...)` | `notIn("id", ids)` |
| **inSql** | IN SQL子查询 | `column IN (sql)` | `inSql("dept_id", "SELECT id FROM dept")` |
| **notInSql** | NOT IN SQL | `column NOT IN (sql)` | `notInSql("userId", "SELECT user_id FROM blacklist")` |
### 2. **查询条件构建示例**
#### **QueryWrapper 示例**
```java
// 基础条件构建
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("status", 1)
.ge("age", 18)
.le("age", 60)
.like("name", "王")
.isNotNull("email")
.orderByDesc("create_time")
.orderByAsc("id");
List<User> users = userMapper.selectList(wrapper);
```
#### **LambdaQueryWrapper 示例**
```java
// Lambda表达式(推荐)
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.eq(User::getStatus, 1)
.ge(User::getAge, 18)
.le(User::getAge, 60)
.like(User::getName, "王")
.isNotNull(User::getEmail)
.orderByDesc(User::getCreateTime)
.orderByAsc(User::getId);
List<User> users = userMapper.selectList(lambdaWrapper);
```
---
## 🎯 高级功能
### 1. **链式调用与条件组合**
```java
// AND 条件(默认)
wrapper.eq("dept_id", 1).gt("salary", 5000);
// OR 条件
wrapper.eq("role", "admin").or().eq("role", "super_admin");
// 嵌套条件(括号分组)
wrapper.and(w -> w.eq("status", 1).or().eq("status", 2))
.or(w -> w.gt("age", 18).lt("age", 65));
// 条件选择(动态构建)
if (StringUtils.isNotBlank(name)) {
wrapper.like("name", name);
}
if (minAge != null) {
wrapper.ge("age", minAge);
}
```
### 2. **复杂查询示例**
```java
// 多表关联查询条件
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.select(User::getId, User::getName, User::getEmail) // 指定查询字段
.eq(User::getStatus, 1)
.in(User::getRoleId, Arrays.asList(1, 2, 3))
.likeRight(User::getName, "张")
.between(User::getCreateTime, startDate, endDate)
.notIn(User::getId, blacklistIds)
.and(w -> w.isNotNull(User::getPhone).or().isNotNull(User::getEmail))
.groupBy(User::getDeptId)
.having("COUNT(*) > {0}", 5)
.orderByDesc(User::getCreateTime)
.last("LIMIT 10"); // 自定义SQL片段
// 使用 EXISTS 子查询
wrapper.exists("SELECT 1 FROM user_role ur WHERE ur.user_id = user.id AND ur.role_id = 1");
// 使用 NOT EXISTS 子查询
wrapper.notExists("SELECT 1 FROM blacklist b WHERE b.user_id = user.id");
```
### 3. **UpdateWrapper 更新操作**
```java
// 更新条件构建
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("status", 0) // 更新条件
.set("status", 1) // 设置更新字段
.set("update_time", new Date())
.setSql("login_count = login_count + 1"); // SQL表达式更新
// 执行更新
userMapper.update(null, updateWrapper);
// LambdaUpdateWrapper(类型安全)
LambdaUpdateWrapper<User> lambdaUpdate = new LambdaUpdateWrapper<>();
lambdaUpdate.eq(User::getStatus, 0)
.set(User::getStatus, 1)
.set(User::getUpdateTime, new Date())
.setSql("login_count = login_count + 1");
userMapper.update(null, lambdaUpdate);
```
### 4. **条件优先级与括号**
```java
// 复杂的条件优先级控制
wrapper.eq("type", "A")
.and(w -> w.eq("status", 1).or().eq("status", 2)) // (status = 1 OR status = 2)
.or(w -> w.eq("type", "B").ne("level", 0)); // OR (type = 'B' AND level != 0)
// 生成的SQL:
// WHERE type = 'A' AND (status = 1 OR status = 2) OR (type = 'B' AND level != 0)
```
### 5. **自定义 SQL 片段**
```java
// 应用自定义SQL片段
wrapper.apply("date_format(create_time,'%Y-%m-%d') = {0}", "2023-10-01");
// 使用数据库函数
wrapper.apply("LENGTH(name) > 10");
// 表别名支持
wrapper.eq("u.status", 1) // 带表别名
.apply("u.id = r.user_id"); // JOIN条件
```
---
## 🚀 实际应用场景
### 场景1:**动态搜索功能**
```java
public List<User> searchUsers(UserQuery query) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// 动态条件构建
if (StringUtils.isNotBlank(query.getName())) {
wrapper.like(User::getName, query.getName());
}
if (query.getMinAge() != null && query.getMaxAge() != null) {
wrapper.between(User::getAge, query.getMinAge(), query.getMaxAge());
}
if (CollectionUtils.isNotEmpty(query.getRoleIds())) {
wrapper.in(User::getRoleId, query.getRoleIds());
}
if (query.getStatus() != null) {
wrapper.eq(User::getStatus, query.getStatus());
}
// 排序
if ("createTime".equals(query.getSortBy())) {
wrapper.orderByDesc(User::getCreateTime);
} else if ("name".equals(query.getSortBy())) {
wrapper.orderByAsc(User::getName);
}
return userMapper.selectList(wrapper);
}
```
### 场景2:**带条件的更新**
```java
public int updateUserStatus(Long userId, Integer newStatus) {
LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(User::getId, userId)
.eq(User::getStatus, 0) // 只有状态为0的才能更新
.set(User::getStatus, newStatus)
.set(User::getUpdateTime, LocalDateTime.now());
return userMapper.update(null, wrapper);
}
```
### 场景3:**分页查询**
```java
public Page<User> queryByPage(UserQuery query, Page<User> page) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// 构建条件
wrapper.eq(User::getDeleted, 0)
.like(StringUtils.isNotBlank(query.getKeyword()),
User::getName, query.getKeyword())
.ge(query.getStartTime() != null,
User::getCreateTime, query.getStartTime())
.le(query.getEndTime() != null,
User::getCreateTime, query.getEndTime());
// 执行分页查询
return userMapper.selectPage(page, wrapper);
}
```
---
## ⚡ 性能优化与最佳实践
### 1. **性能优化建议**
| 场景 | 建议 |
|------|------|
| **索引字段查询** | 优先使用等值查询(eq)避免全表扫描 |
| **大数据量分页** | 结合分页插件,避免内存溢出 |
| **关联查询** | 尽量在Wrapper中完成条件过滤,减少内存数据 |
| **动态字段** | 使用`select()`指定字段,避免`SELECT *` |
| **IN查询** | IN列表元素不宜过多(建议<1000) |
### 2. **最佳实践代码**
```java
// ✅ 推荐写法:使用Lambda,类型安全
LambdaQueryWrapper<User> wrapper = Wrappers.<User>lambdaQuery()
.select(User::getId, User::getName, User::getEmail) // 明确字段
.eq(User::getDeleted, 0)
.and(q -> q.eq(User::getStatus, 1).or().eq(User::getStatus, 2))
.orderByDesc(User::getCreateTime)
.last("LIMIT 100");
// ❌ 避免:字段名硬编码
QueryWrapper<User> badWrapper = new QueryWrapper<>();
badWrapper.eq("deleted", 0) // 字段名易错,重构困难
.eq("status", 1);
```
### 3. **Wrapper 与 Service 层结合**
```java
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
implements UserService {
@Override
public List<User> findActiveUsers() {
// Service层直接使用LambdaWrapper
return list(new LambdaQueryWrapper<User>()
.eq(User::getStatus, 1)
.eq(User::getDeleted, 0)
.orderByDesc(User::getLastLoginTime));
}
@Override
public boolean updateEmail(Long userId, String email) {
// 带条件更新
return update(new LambdaUpdateWrapper<User>()
.eq(User::getId, userId)
.eq(User::getVerified, 1) // 只有验证用户可更新邮箱
.set(User::getEmail, email)
.set(User::getUpdateTime, LocalDateTime.now()));
}
}
```
---
## 🚨 常见问题与解决方案
### 问题1:**条件顺序问题**
```java
// ❌ 错误的顺序:先or后and
wrapper.eq("A", 1).or().eq("B", 2).eq("C", 3);
// 生成的SQL: A = 1 OR B = 2 AND C = 3 (可能不符合预期)
// ✅ 正确的顺序:使用括号明确优先级
wrapper.eq("A", 1).or(w -> w.eq("B", 2).eq("C", 3));
// 生成的SQL: A = 1 OR (B = 2 AND C = 3)
```
### 问题2:**Null值处理**
```java
// 使用condition参数优雅处理null
wrapper.eq(StringUtils.isNotBlank(name), "name", name)
.ge(minAge != null, "age", minAge)
.le(maxAge != null, "age", maxAge);
// LambdaWrapper的condition写法
lambdaWrapper.eq(name != null, User::getName, name)
.between(start != null && end != null,
User::getCreateTime, start, end);
```
### 问题3:**自定义SQL注入安全**
```java
// ❌ 危险:直接拼接字符串
wrapper.apply("date = " + dateStr); // SQL注入风险
// ✅ 安全:使用占位符
wrapper.apply("date_format(create_time,'%Y-%m-%d') = {0}", dateStr);
// ✅ 更安全:使用实体字段
wrapper.apply("date_column = {0}", dateParam);
```
---
## 📊 Wrapper 选择指南
| 选择标准 | 推荐方案 | 理由 |
|---------|----------|------|
| **类型安全** | LambdaQueryWrapper/LambdaUpdateWrapper | 编译时检查,重构友好 |
| **动态字段名** | QueryWrapper/UpdateWrapper | 支持运行时确定字段名 |
| **简单查询** | 任意,根据团队规范 | 简单场景差异不大 |
| **团队协作** | Lambda系列(推荐) | 统一规范,减少错误 |
| **性能敏感** | 直接SQL或XML | 极端性能场景 |
---
## 💡 总结
MyBatis-Plus 的 Wrapper 条件构造器提供了强大而灵活的查询条件构建能力:
1. **类型安全优先**:尽量使用 LambdaWrapper 避免字段名错误
2. **链式调用**:保持代码简洁和可读性
3. **条件组合**:合理使用 and()、or() 和括号控制条件优先级
4. **动态条件**:利用 condition 参数优雅处理 null 值
5. **性能考虑**:结合数据库索引设计查询条件
通过合理使用 Wrapper,可以大大减少手写 SQL 的工作量,提高开发效率,同时保持代码的清晰度和可维护性。
```
- 环境配置
- window怎么配置java环境变量?
- SQL学习
- 字段类型
- 1、 所有可声明的字段类型
- 2、常用的可声明的字段类型
- 创建表和表内字段
- 1、整个创建表语句
- 2、设置id主键
- 3、设置业务主键,唯一索引
- 4、设置业务字段
- 5、普通索引
- 6、唯一索引与普通索引的区别
- 7、表的引擎、字符集、排序规则和注释的设置
- Java基础语法
- 数据类型
- Java中的数据类型
- LocalDate
- 常用的数据类型
- Java 常用数据类型方法
- Java中数组、list、Map、HashMap
- 如何用Map来优化那些复杂的“双重 for 循环”查询?
- Java 8 中steam()操作流
- Java中HashMap和JSON
- Java中的JSONObject
- 工具类
- Java 常用工具类
- Arrays工具类
- Java 时间工具类
- 泛型
- Java中泛型概念
- Java中的泛型容器
- 泛型参数与函数参数的区别
- 推断出泛型实参
- Lambda表达式
- 数据分层
- 异常处理
- Java8 异常处理类总结表
- MyBatis-Plus 常用异常类总结表
- Java高级特性
- Maven
- jib-maven-plugin
- 什么是Spring Boot 的 parent pom?
- maven中各个生命周期的含义
- Spring Boot
- maven与spring boot 的关系
- Java中的连接池
- Spring JDBC
- Spring JDBC的概念
- JdbcTemplate常用的方法
- Spring中Bean的概念
- Spring中的抽象,通俗解释一下
- Spring中的事物
- Spring中的事物,通俗解释一下
- Spring中的事物抽象,常见的有哪些,列举一下
- Spring中常用的事物场景有哪些,列举一下
- Spring事务管理有哪些注解?
- Spring中使用事物处理订单的案例,列举说明一下
- Spring中声明式事务、分布式事务以及编程式事务的区别,列举一下
- 配置文件
- application-properties配置文件
- Spring Boot 的启动
- spring boot项目如何启动?
- 列举一下Spring Boot的启动过程
- SpringApplication.run方法
- Spring Boot 启动时有哪些接口?
- CommandLineRunner
- Spring Boot 的常用注解
- 系统注解
- 表格:系统注解
- @Override
- @Deprecated
- @SuppressWarnnings
- 使用在类名上的注解
- 表格:使用在类名上的注解
- @RestController
- @Controller
- @Service
- @Repository
- @Component
- @Configuration
- @Resource
- @Autowired
- @RequestMapping
- @GetMapping
- @PostMapping
- @Transactional
- @Qualifier
- 使用在方法上的注解
- 表格:使用在方法上的注解
- @RequestBody
- @PathVariable
- @Bean
- @ResponseBody
- @PreAuthorize
- 其他常用注解
- 表格:其他常用注解
- @EnableAutoConfiguration
- @SpringBootApplication
- @EnableScheduling
- @EnableAsync
- @ComponentScan
- @Aspec
- @ControllerAdvice
- @ExceptionHandler
- @Value
- @ConfigurationProperties
- @EnableConfigurationProperties
- @MapperScan
- @ApiOperation
- @Produces
- Validator验证的常用注解
- spring IoC容器
- Spring IoC容器依赖注入实现方式
- 常用依赖
- RESTEasy
- resteasy简介
- RESTEasy框架(依赖)的功能和常用注解
- MyBatis
- 简介
- paginationInterceptor
- @TableName
- @TableId
- @Param
- MyBatis-Plus
- MyBatis-Plus简介
- MyBatis-Plus的工具类
- Mybatis-Plus扩展的工具类和方法
- MyBatis-Plus中最常用的工具类方法
- MyBatis-Plus 中最常用的 4 大核心工具类
- Wrapper条件构造器
- Wrapper条件构造器详解
- Wrapper条件构造器eq等方法的参数说明
- LambdaQueryWrapper与QueryWrapper
- 日期格式是否必须转换
- Lombok
- Lombok作用详解
- @Data
- @Slf4j
- @Builder
- @EqualsAndHashCode
- @Accessors
- Jackson
- Jackson简介
- @JsonFormat
- Jackson高效地在 HashMap 和 JSON 字符串之间进行相互转换
- Hutool
- Hutool简介
- hutool依赖常用的方法
- fastjson2
- fastjson2简介
- UrlBasedCorsConfigurationSource
- 生态相关
- JBoss 社区
- 支付系统
- 1. 初始化mysql数据库流程
- 2. 初始化redis数据库的流程
- 3. 初始化rabbitmq服务
- 环球置业
- 1.模块目录结构分析
- 2. DTO(数据传输层)的核心作用
- 3. VO(视图对象层)
- 4. VO(视图对象层)和 DTO 数据传输层 的区别
