详细梳理 MyBatis-Plus 的 QueryWrapper 和 LambdaQueryWrapper的入门到精通
文章目录
- 一、QueryWrapper?
- 二、LambdaQueryWrapper
- 三、常用条件方法总结
- 四、动态条件(推荐写法)
- 五、分页查询
- 六、复杂查询案例
- 6.1. 多条件 OR
- 6.2. GROUP BY + HAVING
- 七、最佳实践
- 八、完整示例
- 8.1. pom.xml 依赖
- 8.2. 实体类 User.java
- 8.3. Mapper 接口 UserMapper.java
- 8.4. Service 层 UserService.java
- 8.5. Controller 层 UserController.java
- 8.6. CSV 工具类 CsvUtil.java
什么是 QueryWrapper 和 LambdaQueryWrapper?
- QueryWrapper:基于字符串的字段名构造查询条件,简单直观,适合快速开发,但需要手动输入字段名,容易因拼写错误导致问题。
- LambdaQueryWrapper:基于 Lambda 表达式,通过实体类的 getter 方法引用字段,类型安全,避免硬编码,提高代码可读性和维护性。
两者功能类似,但 LambdaQueryWrapper 更适合现代化 Java 开发(Java 8+)。
一、QueryWrapper?
QueryWrapper 是 MyBatis-Plus 提供的 条件构造器,用于拼接 SQL 的 WHERE、ORDER BY、GROUP BY 等语句,不需要自己写 SQL。
基本用法:
QueryWrapper<User> query = new QueryWrapper<>();
query.eq("name", "张三") // WHERE name = '张三'.ge("age", 18) // AND age >= 18.like("email", "qq"); // AND email LIKE '%qq%'// 查询
List<User> users = userMapper.selectList(query);
生成 SQL:
SELECT * FROM user WHERE name = '张三' AND age >= 18 AND email LIKE '%qq%';
二、LambdaQueryWrapper
推荐使用 LambdaQueryWrapper —— 避免硬编码字段名,用 Lambda 表达式 替代,保证字段变更时编译报错,避免运行时出错。
基本用法:
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getName, "张三").ge(User::getAge, 18).like(User::getEmail, "qq");// 查询
List<User> users = userMapper.selectList(wrapper);
生成 SQL(同上):
SELECT * FROM user WHERE name = '张三' AND age >= 18 AND email LIKE '%qq%';
User::getName 自动对应实体类字段 name,不怕写错。
三、常用条件方法总结
方法 | 说明 | 示例 |
---|---|---|
eq | 等于 | eq(“name”, “张三”) → name = ‘张三’ |
ne | 不等于 | ne(“status”, 1) |
gt | 大于 | gt(“age”, 20) |
ge | 大于等于 | ge(“age”, 18) |
lt | 小于 | lt(“age”, 60) |
le | 小于等于 | le(“age”, 60) |
between | 区间 | between(“age”, 18, 30) |
like | 模糊匹配 | like(“name”, “三”) |
likeLeft | 左模糊 | likeLeft(“name”, “三”) → name LIKE ‘%三’ |
likeRight | 右模糊 | likeRight(“name”, “三”) → name LIKE ‘三%’ |
in IN | 查询 | in(“id”, Arrays.asList(1,2,3)) |
notIn | NOT IN | notIn(“id”, list) |
orderByAsc | 升序 | orderByAsc(“age”) |
orderByDesc | 降序 | orderByDesc(“create_time”) |
isNull | 为空 | isNull(“email”) |
isNotNull | 不为空 | isNotNull(“email”) |
or | OR 条件 | eq(“status”, 1).or().eq(“status”, 2) |
nested | 嵌套 | nested(w -> w.eq(“status”, 1).or().eq(“status”, 2)) |
四、动态条件(推荐写法)
实际开发中,经常需要 参数为空时不加条件:
String name = "张三";
Integer age = null;LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(name != null, User::getName, name).ge(age != null, User::getAge, age);
只会拼接 name = ‘张三’,不会拼接 age >= ?。
五、分页查询
Page<User> page = new Page<>(1, 10); // 第1页, 每页10条
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.ge(User::getAge, 18);IPage<User> result = userMapper.selectPage(page, wrapper);System.out.println("总数: " + result.getTotal());
System.out.println("数据: " + result.getRecords());
六、复杂查询案例
6.1. 多条件 OR
wrapper.eq(User::getStatus, 1).or(w -> w.eq(User::getAge, 20).like(User::getName, "李"));
SQL:
WHERE status = 1 OR (age = 20 AND name LIKE '%李%')
6.2. GROUP BY + HAVING
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("dept_id, COUNT(*) as cnt").groupBy("dept_id").having("cnt > {0}", 10);
七、最佳实践
-
能用 LambdaQueryWrapper 就不用 QueryWrapper,避免硬编码。
-
条件要动态拼接,利用 (condition, column, value) 三元参数。
-
分页时用 selectPage,大数据量避免 selectList。
-
多表场景:推荐用 MyBatis-Plus 配合 自定义 SQL + Wrapper,或用 联表插件。
八、完整示例
以下是完整的 Spring Boot + MyBatis-Plus + LambdaQueryWrapper + CSV 导出示例,包含以下内容:
-
依赖配置(Maven)
-
实体类(User)
-
Mapper 接口
-
Service 层(使用 LambdaQueryWrapper 查询)
-
Controller 层(导出为 CSV)
-
CSV 工具类
8.1. pom.xml 依赖
<dependencies><!-- Spring Boot Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- MyBatis-Plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.5</version></dependency><!-- MySQL 驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><!-- CSV 工具:OpenCSV --><dependency><groupId>com.opencsv</groupId><artifactId>opencsv</artifactId><version>5.9</version></dependency>
</dependencies>
8.2. 实体类 User.java
package com.example.demo.entity;import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;@Data
@TableName("user")
public class User {@TableIdprivate Long id;private String name;private Integer age;private String email;
}
8.3. Mapper 接口 UserMapper.java
package com.example.demo.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface UserMapper extends BaseMapper<User> {
}
8.4. Service 层 UserService.java
package com.example.demo.service;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;import java.util.List;@Service
@RequiredArgsConstructor
public class UserService {private final UserMapper userMapper;public List<User> findUsers(Integer minAge) {return userMapper.selectList(new LambdaQueryWrapper<User>().ge(User::getAge, minAge) // 年龄大于等于.orderByAsc(User::getAge) // 按年龄排序);}
}
8.5. Controller 层 UserController.java
package com.example.demo.controller;import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import com.example.demo.util.CsvUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;@RestController
@RequiredArgsConstructor
public class UserController {private final UserService userService;@GetMapping("/export/users")public void exportUsers(HttpServletResponse response) throws IOException {List<User> users = userService.findUsers(18); // 查询年龄 >= 18// 设置响应头response.setContentType("text/csv");response.setCharacterEncoding("UTF-8");response.setHeader("Content-Disposition", "attachment; filename=users.csv");// 写入 CSVCsvUtil.writeUsersToCsv(users, response.getWriter());}
}
8.6. CSV 工具类 CsvUtil.java
package com.example.demo.util;import com.example.demo.entity.User;
import com.opencsv.CSVWriter;import java.io.Writer;
import java.util.List;public class CsvUtil {public static void writeUsersToCsv(List<User> users, Writer writer) {try (CSVWriter csvWriter = new CSVWriter(writer)) {// 写表头String[] header = {"ID", "姓名", "年龄", "邮箱"};csvWriter.writeNext(header);// 写数据for (User user : users) {String[] data = {String.valueOf(user.getId()),user.getName(),String.valueOf(user.getAge()),user.getEmail()};csvWriter.writeNext(data);}} catch (Exception e) {throw new RuntimeException("写 CSV 文件失败", e);}}
}
启动项目后,访问:
http://localhost:8080/export/users
会自动下载 users.csv,里面是查询出的用户数据。
“人的一生会经历很多痛苦,但回头想想,都是传奇”。