AI写作智能体 自主规划任务,支持联网查询和网页读取,多模态高效创作各类分析报告、商业计划、营销方案、教学内容等。 广告
# 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 的工作量,提高开发效率,同时保持代码的清晰度和可维护性。 ```