QueryWrapper的使用

QueryWrapperMyBatis-Plus (MP) 中最常用的条件构造器。它允许你通过 Java 代码链式调用来构建 SQL 的 WHERE 子句,而无需手写 XML 或 SQL 字符串。

它的核心语法结构是:

java 复制代码
new QueryWrapper<Entity>()
    .方法名("字段名", 值)
    .方法名("字段名", 值)
    ...;

常用条件构造方法速查表

假设你的实体类是 User,对应数据库表字段为 name, age, email, create_time。

SQL 条件 MP 方法 示例代码 生成的 SQL 片段
等于 (=) eq .eq("name", "张三") WHERE name = '张三'
不等于 (<>) ne .ne("status", 0) WHERE status <> 0
大于 (>) gt .gt("age", 18) WHERE age > 18
大于等于 (>=) ge .ge("age", 18) WHERE age >= 18
小于 (<) lt .lt("age", 60) WHERE age < 60
小于等于 (<=) le .le("age", 60) WHERE age <= 60
模糊查询 (LIKE) like .like("name", "张") WHERE name LIKE '%张%'
左模糊 (LIKE LEFT) likeLeft .likeLeft("name", "三") WHERE name LIKE '%三'
右模糊 (LIKE RIGHT) likeRight .likeRight("name", "张") WHERE name LIKE '张%'
在范围内 (IN) in .in("id", 1, 2, 3) WHERE id IN (1, 2, 3)
不在范围内 (NOT IN) notIn .notIn("id", 1, 2) WHERE id NOT IN (1, 2)
为空 (IS NULL) isNull .isNull("email") WHERE email IS NULL
不为空 (IS NOT NULL) isNotNull .isNotNull("email") WHERE email IS NOT NULL
排序 (ORDER BY) orderByAsc/Desc .orderByDesc("age") ORDER BY age DESC
分组 (GROUP BY) groupBy .groupBy("name") GROUP BY name

实战场景演示:

场景 1:简单组合查询

需求:查询年龄大于 18 岁,且名字中包含"张"的用户,按年龄降序排列。

java 复制代码
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.gt("age", 18)          // age > 18
       .like("name", "张")     // AND name LIKE '%张%'
       .orderByDesc("age");    // ORDER BY age DESC

List<User> users = userMapper.selectList(wrapper);

生成 SQL:

sql 复制代码
SELECT * FROM user WHERE age > 18 AND name LIKE '%张%' ORDER BY age DESC

场景 2:动态条件(最常用!

需求:前端可能传 name,也可能传 age,也可能都不传。只有当参数不为空时,才加入查询条件。

java 复制代码
public List<User> searchUsers(String name, Integer age) {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    
    // 如果 name 不为空且不是空字符串,则添加 like 条件
    if (StringUtils.isNotBlank(name)) {
        wrapper.like("name", name);
    }
    
    // 如果 age 不为 null,则添加 eq 条件
    if (age != null) {
        wrapper.eq("age", age);
    }
    
    return userMapper.selectList(wrapper);
}

场景 3:使用 Lambda 表达式(推荐 ✅)

优点:防止字段名写错(如 "nmae" 写成 "name"),IDE 有自动提示,重构更安全。

需要使用 LambdaQueryWrapper:

java 复制代码
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

// ...

LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getName, "张三")      // 对应 name = '张三'
       .gt(User::getAge, 18)           // 对应 age > 18
       .orderByDesc(User::getCreateTime); // 对应 ORDER BY create_time DESC

List<User> users = userMapper.selectList(wrapper);

注意:User::getName 是方法引用,MP 会自动将其解析为数据库字段名 name。这要求你的实体类遵循 JavaBean 规范(有 getter/setter)

高级用法:嵌套逻辑 (AND / OR)

需求:(name LIKE '张' OR email LIKE 'zhang') AND age > 18

java 复制代码
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.and(w -> w.like("name", "张")
                  .or()
                  .like("email", "zhang"))
       .gt("age", 18);

生成SQL:

sql 复制代码
WHERE (name LIKE '%张%' OR email LIKE '%zhang%') AND age > 18

❗ 常见坑点与注意事项:

1.字段名匹配:

如果使用 QueryWrapper,传入的字符串必须是数据库列名(如 user_name),而不是 Java 属性名(如 userName),除非你配置了驼峰转换。

如果使用 LambdaQueryWrapper,传入的是Java 属性方法引用(如 User::getUserName),MP 会自动转换为数据库列名。强烈推荐后者!

2.空值处理:

eq("name", null) 会生成 name IS NULL。

eq("name", "") 会生成 name = ''。

如果希望值为 null 或空字符串时忽略该条件,可以使用 MP 提供的重载方法:

java

java 复制代码
// 只有当 name 不为 null 且不为空时,才添加 eq 条件
wrapper.eq(StringUtils.isNotBlank(name), "name", name);

3.性能问题:

避免在大表上使用 like "%xxx%"(全表扫描)。

尽量使用索引字段进行查询。

总结:

  • 简单查询:直接用 QueryWrapper 或 LambdaQueryWrapper。
  • 复杂动态查询:优先使用LambdaQueryWrapper + 条件判断,安全且易维护。
  • 超复杂联表查询:建议手写 XML SQL,因为 Wrapper主要服务于单表操作。

使用:

java 复制代码
package com.example.demo.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; // 👈 导入 Lambda 版本
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.UserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Override
    public IPage<User> getUserPage(int pageNum, int pageSize) {
        // 1. 创建 Page 对象 (当前页, 每页大小)
        Page<User> page = new Page<>(pageNum, pageSize);

        // 2. 构建查询条件 (使用 LambdaQueryWrapper)
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();

        // 👇 示例条件:名字不为空 且 按年龄降序排列
        // 注意:User::getName 对应数据库字段 name,User::getAge 对应 age
        wrapper.isNotNull(User::getName)
                .orderByDesc(User::getAge);

        // 3. 调用 MP 的分页方法
        // baseMapper.selectPage(page, wrapper) 会自动生成 LIMIT 语句
        IPage<User> userPage = this.baseMapper.selectPage(page, wrapper);

        return userPage;
    }
}
相关推荐
吃饱了得干活15 小时前
Spring Cloud Gateway 微服务网关:路由、断言、过滤器
java·spring cloud
lwx5728017 小时前
探秘InnoDB:搞懂它的内存、线程、磁盘与日志刷盘策略
java·后端
Flynt18 小时前
从Spring Boot 4.0升到4.1,我在Maven和gRPC上栽了跟头
java·spring boot·后端
plainGeekDev19 小时前
Activity 间传值 → Navigation 参数
android·java·kotlin
plainGeekDev19 小时前
onActivityResult → ActivityResult API
android·java·kotlin
Sunia19 小时前
《AgentX 专栏》10-生产部署:3台2C4G云服务器把企业级Agent真正跑起来的完整方案
java·架构
ZhengEnCi20 小时前
J7A-高级Java工程师面试三道灵魂拷问-深度广度与工程素养的终极检验
java·后端
狼爷2 天前
吃透 Java Function 接口,搞定 99% 的 Stream 场景
java·函数式编程
祎雪双十Gy2 天前
从 DataX 的配置加载说起:我用 FastJson2 做了一个轻量级动态配置管理库
java·后端
小锋java12342 天前
分享一套锋哥原创的SpringBoot4+Vue3宠物领养网站系统
java