MyBatis-Plus查询条件
# MyBatis-Plus查询条件
# 1. 查询方式概述
MyBatis-Plus提供了丰富的查询方式,可以满足各种复杂业务场景的需求。
# 2. MyBatis-Plus查询方法总览
MyBatis-Plus提供了丰富的查询方法,从简单到复杂,从单表到多表,几乎涵盖了所有常见的查询场景。下表列出了BaseMapper中提供的主要查询方法:
方法名 | 功能描述 | 参数说明 | 返回值 |
---|---|---|---|
selectById | 根据ID查询单条记录 | 主键ID | 实体对象 |
selectBatchIds | 根据ID集合批量查询 | ID集合 | 实体对象列表 |
selectByMap | 根据Map条件查询 | 列名-值映射的Map | 实体对象列表 |
selectOne | 根据条件查询单条记录 | 条件构造器 | 实体对象 |
selectList | 根据条件查询多条记录 | 条件构造器 | 实体对象列表 |
selectCount | 根据条件查询记录数 | 条件构造器 | 记录数 |
selectPage | 根据条件分页查询 | 分页参数, 条件构造器 | 分页结果 |
# 3. 基础查询方法
# 3.1 根据ID查询
最简单的查询方式是根据主键ID查询单条记录:
/**
* 根据ID查询单条记录
* @param id 主键ID,必须是数据库表中的主键
* @return 返回查询到的实体对象,如果未找到返回null
*/
@Test
public void testSelectById() {
// 根据ID查询单条记录
User user = userMapper.selectById(1L);
// 输出查询结果
System.out.println("查询结果: " + user);
}
2
3
4
5
6
7
8
9
10
11
12
13
# 3.2 批量ID查询
当需要根据多个ID查询多条记录时,可以使用批量ID查询方法:
/**
* 批量ID查询示例
* @param idList 主键ID集合,要查询的多个ID
* @return 返回匹配ID的实体对象列表
*/
@Test
public void testSelectBatchIds() {
// 创建要查询的ID列表
List<Long> idList = Arrays.asList(1L, 2L, 3L);
// 执行批量查询
List<User> userList = userMapper.selectBatchIds(idList);
// 输出查询结果
System.out.println("共查询到 " + userList.size() + " 条记录");
userList.forEach(System.out::println);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 3.3 使用Map条件查询
MyBatis-Plus支持使用Map构建简单的查询条件:
/**
* 使用Map构建查询条件
* Map中的key为数据库列名,value为查询条件的值
* 多个条件之间是AND关系
*/
@Test
public void testSelectByMap() {
// 创建查询条件Map
Map<String, Object> columnMap = new HashMap<>();
// 添加条件:age=25 AND name='张三'
columnMap.put("age", 25);
columnMap.put("name", "张三");
// 执行查询
List<User> userList = userMapper.selectByMap(columnMap);
// 输出查询结果
System.out.println("共查询到 " + userList.size() + " 条记录");
userList.forEach(System.out::println);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
注意事项:
- Map中的key必须是数据库表中的列名,而不是实体类的属性名
- Map条件之间是AND关系,无法实现OR等复杂查询
- 仅支持等值查询,不支持大于、小于、LIKE等复杂条件
# 3.4 全表查询
在某些场景下,可能需要查询表中的所有数据:
/**
* 全表查询示例
* 查询表中的所有记录,适用于数据量较小的表
* 数据量大时应避免使用此方法,改用分页查询
*/
@Test
public void testSelectAll() {
// 传入null表示没有条件,即查询所有记录
List<User> userList = userMapper.selectList(null);
// 输出查询结果
System.out.println("总记录数: " + userList.size());
userList.forEach(System.out::println);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 3.5 查询单条记录
当需要查询满足条件的单条记录时,可以使用selectOne方法:
/**
* 查询单条记录示例
* 当查询结果多于一条时会抛出异常
*/
@Test
public void testSelectOne() {
// 创建查询条件
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", "admin"); // 查询用户名为admin的记录
// 执行查询
User user = userMapper.selectOne(queryWrapper);
// 输出查询结果
System.out.println("查询结果: " + user);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
注意事项:
- 当查询结果为多条时,会抛出TooManyResultsException异常
- 建议在唯一索引字段上使用此方法
- 可以使用last方法限制结果数量:
queryWrapper.last("LIMIT 1")
# 4. 使用不同类型对象作为查询条件
MyBatis-Plus提供了多种方式使用不同类型的对象作为查询条件,每种方式都有其适用场景。
# 4.1 使用实体对象查询
可以直接使用实体对象作为查询条件,非空属性将作为等值查询条件:
/**
* 使用实体对象作为查询条件
* 实体类中的非空属性将作为等值查询条件,多个条件之间是AND关系
*/
@Test
public void testEntityQuery() {
// 创建实体对象并设置查询条件
User queryUser = new User();
queryUser.setAge(25); // 年龄=25
queryUser.setGender("男"); // 性别=男
// 执行查询(两种方式)
// 方式1:使用实体对象构造QueryWrapper
List<User> list1 = userMapper.selectList(new QueryWrapper<>(queryUser));
// 方式2:使用实体对象直接作为allEq的参数
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.allEq(BeanUtil.beanToMap(queryUser));
List<User> list2 = userMapper.selectList(queryWrapper);
// 输出查询结果
System.out.println("查询结果数量: " + list1.size());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
注意事项:
- 只有非空属性会被作为查询条件
- 实体对象查询仅支持等值查询,不支持其他比较操作
- 使用的是属性名而非列名,会自动进行驼峰转换
# 4.2 使用Map对象查询
使用Map构建查询条件更加灵活:
/**
* 使用Map构建查询条件示例
* Map的key为列名,value为查询值
*/
@Test
public void testMapQuery() {
// 创建查询条件Map
Map<String, Object> columnMap = new HashMap<>();
columnMap.put("age", 25); // 年龄=25
columnMap.put("gender", "男"); // 性别=男
// 执行查询
List<User> userList = userMapper.selectByMap(columnMap);
// 输出查询结果
System.out.println("查询结果数量: " + userList.size());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
注意事项:
- Map中使用的key是数据库列名而非属性名
- 支持在key中使用列的别名(如果查询时指定了别名)
- 不支持复杂条件,只能是等值查询
# 4.3 使用自定义查询对象
在复杂业务场景中,通常需要使用自定义查询对象来封装查询条件:
/**
* 自定义查询对象
* 用于封装复杂查询条件,提高代码可读性和可维护性
*/
@Data
public class UserQuery {
private String name; // 用户名(模糊查询)
private Integer minAge; // 最小年龄
private Integer maxAge; // 最大年龄
private List<String> statusList; // 状态列表
private Date startDate; // 开始日期
private Date endDate; // 结束日期
private Boolean sortByAge; // 是否按年龄排序
}
/**
* 使用自定义查询对象构建查询条件示例
*/
@Test
public void testCustomQueryObject() {
// 创建自定义查询对象
UserQuery query = new UserQuery();
query.setName("张"); // 用户名包含"张"
query.setMinAge(20); // 年龄>=20
query.setMaxAge(30); // 年龄<=30
query.setStatusList(Arrays.asList("active", "pending")); // 状态in ('active','pending')
query.setStartDate(DateUtil.parseDate("2023-01-01")); // 创建日期>=2023-01-01
query.setEndDate(DateUtil.parseDate("2023-12-31")); // 创建日期<=2023-12-31
query.setSortByAge(true); // 按年龄排序
// 创建QueryWrapper并设置查询条件
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 动态添加查询条件
if (StringUtils.isNotBlank(query.getName())) {
queryWrapper.like("name", query.getName());
}
if (query.getMinAge() != null) {
queryWrapper.ge("age", query.getMinAge());
}
if (query.getMaxAge() != null) {
queryWrapper.le("age", query.getMaxAge());
}
if (CollectionUtils.isNotEmpty(query.getStatusList())) {
queryWrapper.in("status", query.getStatusList());
}
if (query.getStartDate() != null) {
queryWrapper.ge("create_time", query.getStartDate());
}
if (query.getEndDate() != null) {
queryWrapper.le("create_time", query.getEndDate());
}
// 动态排序
if (Boolean.TRUE.equals(query.getSortByAge())) {
queryWrapper.orderByAsc("age");
} else {
queryWrapper.orderByDesc("create_time");
}
// 执行查询
List<User> userList = userMapper.selectList(queryWrapper);
// 输出查询结果
System.out.println("查询结果数量: " + userList.size());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
优势:
- 代码结构清晰,查询条件集中管理
- 支持复杂的组合条件和动态查询
- 可重用,多个位置可使用相同的查询对象
- 可以添加业务验证逻辑
# 5. 条件构造器详解
条件构造器是MyBatis-Plus最强大的特性之一,它提供了丰富的API来构建复杂的查询条件。
# 5.1 QueryWrapper基础用法
QueryWrapper用于构建查询条件,支持各种比较操作和复杂条件组合:
/**
* QueryWrapper基础用法示例
* 展示常用的条件构建方法
*/
@Test
public void testQueryWrapperBasic() {
// 创建QueryWrapper对象
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 1. 等值条件
queryWrapper.eq("status", "active"); // status = 'active'
// 2. 不等条件
queryWrapper.ne("gender", "女"); // gender != '女'
// 3. 大于、小于条件
queryWrapper.gt("age", 18); // age > 18
queryWrapper.lt("age", 60); // age < 60
// 4. 大于等于、小于等于条件
queryWrapper.ge("credit_score", 700); // credit_score >= 700
queryWrapper.le("login_attempts", 5); // login_attempts <= 5
// 5. LIKE条件
queryWrapper.like("name", "张"); // name LIKE '%张%'
queryWrapper.likeLeft("email", "gmail.com"); // email LIKE '%gmail.com'
queryWrapper.likeRight("username", "admin"); // username LIKE 'admin%'
// 6. 范围条件
queryWrapper.between("age", 20, 30); // age BETWEEN 20 AND 30
queryWrapper.notBetween("login_time", "09:00", "18:00"); // login_time NOT BETWEEN '09:00' AND '18:00'
// 7. IN条件
queryWrapper.in("status", Arrays.asList("active", "pending")); // status IN ('active', 'pending')
queryWrapper.notIn("role", Arrays.asList("guest", "temp")); // role NOT IN ('guest', 'temp')
// 8. NULL条件
queryWrapper.isNull("delete_time"); // delete_time IS NULL
queryWrapper.isNotNull("email"); // email IS NOT NULL
// 9. 排序
queryWrapper.orderByDesc("create_time"); // ORDER BY create_time DESC
queryWrapper.orderByAsc("age"); // ORDER BY age ASC
// 执行查询
List<User> userList = userMapper.selectList(queryWrapper);
// 输出查询结果
System.out.println("查询结果数量: " + userList.size());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 5.2 条件优先级和分组
在复杂查询中,可能需要控制条件的优先级和分组:
/**
* 条件优先级和分组示例
* 使用嵌套条件构建复杂查询
*/
@Test
public void testQueryWrapperGroup() {
// 创建QueryWrapper对象
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 构建条件: (status = 'active' OR status = 'pending') AND (age > 18 AND gender = '男')
queryWrapper
// 第一组条件,使用OR连接
.and(wrapper -> wrapper
.eq("status", "active")
.or()
.eq("status", "pending")
)
// 第二组条件,AND连接
.and(wrapper -> wrapper
.gt("age", 18)
.eq("gender", "男")
);
// 执行查询
List<User> userList = userMapper.selectList(queryWrapper);
// 输出查询结果
System.out.println("查询结果数量: " + userList.size());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 5.3 动态条件查询
在实际应用中,查询条件通常是由用户输入决定的,需要动态构建:
/**
* 动态条件查询示例
* 根据用户输入动态构建查询条件
*/
@Test
public void testDynamicCondition() {
// 模拟用户输入的查询参数(可能为null)
String name = "张"; // 可能为null
Integer minAge = 20; // 可能为null
Integer maxAge = 30; // 可能为null
String status = null; // 用户未选择状态条件
// 创建QueryWrapper对象
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 使用condition参数控制条件是否加入查询
queryWrapper
.like(StringUtils.isNotBlank(name), "name", name) // 当name不为空时,添加LIKE条件
.ge(minAge != null, "age", minAge) // 当minAge不为空时,添加大于等于条件
.le(maxAge != null, "age", maxAge) // 当maxAge不为空时,添加小于等于条件
.eq(StringUtils.isNotBlank(status), "status", status); // 当status不为空时,添加等值条件
// 执行查询
List<User> userList = userMapper.selectList(queryWrapper);
// 输出查询结果
System.out.println("查询结果数量: " + userList.size());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 5.4 子查询
MyBatis-Plus支持构建子查询:
/**
* 子查询示例
* 使用子查询构建更复杂的查询条件
*/
@Test
public void testSubQuery() {
// 创建QueryWrapper对象
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 使用IN子查询:查询部门ID在活跃部门列表中的用户
queryWrapper.inSql("department_id", "SELECT id FROM department WHERE status = 'active'");
// 使用EXISTS子查询:查询有订单记录的用户
queryWrapper.exists("SELECT 1 FROM order WHERE order.user_id = user.id AND order.status = 'completed'");
// 使用自定义条件:查询消费金额大于平均值的用户
queryWrapper.apply("consume_amount > (SELECT AVG(consume_amount) FROM user)");
// 执行查询
List<User> userList = userMapper.selectList(queryWrapper);
// 输出查询结果
System.out.println("查询结果数量: " + userList.size());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 5.5 指定查询字段
可以使用select方法指定要查询的字段,减少数据传输量:
/**
* 指定查询字段示例
* 只查询需要的字段,提高查询效率
*/
@Test
public void testSelectFields() {
// 创建QueryWrapper对象
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 指定要查询的字段
queryWrapper.select("id", "name", "age", "email");
// 添加查询条件
queryWrapper.gt("age", 18);
// 执行查询
List<User> userList = userMapper.selectList(queryWrapper);
// 输出查询结果
System.out.println("查询结果数量: " + userList.size());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 6. LambdaQueryWrapper:类型安全的查询构造器
LambdaQueryWrapper是QueryWrapper的增强版,它使用Lambda表达式引用实体类的方法,避免硬编码字段名称,从而提供类型安全的查询:
/**
* LambdaQueryWrapper基础用法示例
* 使用Lambda表达式引用实体类字段,避免字符串硬编码
*/
@Test
public void testLambdaQueryWrapper() {
// 创建LambdaQueryWrapper对象
LambdaQueryWrapper<User> lambdaQuery = new LambdaQueryWrapper<>();
// 构建查询条件
lambdaQuery
.eq(User::getStatus, "active") // status = 'active'
.ge(User::getAge, 18) // age >= 18
.like(User::getName, "张") // name LIKE '%张%'
.orderByDesc(User::getCreateTime); // ORDER BY create_time DESC
// 执行查询
List<User> userList = userMapper.selectList(lambdaQuery);
// 输出查询结果
System.out.println("查询结果数量: " + userList.size());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 6.1 LambdaQueryWrapper动态条件查询
LambdaQueryWrapper同样支持动态条件查询:
/**
* LambdaQueryWrapper动态条件查询示例
* 结合Lambda表达式和condition参数实现动态查询
*/
@Test
public void testLambdaQueryWrapperDynamic() {
// 模拟用户输入的查询参数
String name = "张"; // 可能为null
Integer minAge = 20; // 可能为null
Integer maxAge = 30; // 可能为null
// 创建LambdaQueryWrapper对象
LambdaQueryWrapper<User> lambdaQuery = new LambdaQueryWrapper<>();
// 构建动态查询条件
lambdaQuery
.like(StringUtils.isNotBlank(name), User::getName, name) // 当name不为空时,添加LIKE条件
.ge(minAge != null, User::getAge, minAge) // 当minAge不为空时,添加大于等于条件
.le(maxAge != null, User::getAge, maxAge) // 当maxAge不为空时,添加小于等于条件
.orderByDesc(User::getCreateTime); // 按创建时间降序排序
// 执行查询
List<User> userList = userMapper.selectList(lambdaQuery);
// 输出查询结果
System.out.println("查询结果数量: " + userList.size());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 6.2 LambdaQueryChainWrapper:链式调用增强
MyBatis-Plus提供了更加便捷的链式调用封装:
/**
* LambdaQueryChainWrapper示例
* 进一步简化链式调用
*/
@Test
public void testLambdaQueryChainWrapper() {
// 使用链式调用直接执行查询
List<User> userList = new LambdaQueryChainWrapper<User>(userMapper)
.like(User::getName, "张")
.gt(User::getAge, 18)
.orderByDesc(User::getCreateTime)
.list(); // 直接获取结果列表
// 输出查询结果
System.out.println("查询结果数量: " + userList.size());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 7. 分页查询
MyBatis-Plus提供了强大的分页查询功能,可以轻松实现数据分页:
/**
* 基本分页查询示例
* 演示如何使用MyBatis-Plus的分页功能
*/
@Test
public void testPageQuery() {
// 创建分页对象,指定当前页和每页大小
// 参数1:当前页码(从1开始)
// 参数2:每页记录数
Page<User> page = new Page<>(1, 10);
// 创建查询条件
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age", 18); // 年龄大于18
// 执行分页查询
Page<User> userPage = userMapper.selectPage(page, queryWrapper);
// 获取分页结果
List<User> records = userPage.getRecords(); // 当前页数据列表
long total = userPage.getTotal(); // 总记录数
long pages = userPage.getPages(); // 总页数
long current = userPage.getCurrent(); // 当前页码
long size = userPage.getSize(); // 每页大小
// 输出分页信息
System.out.println("总记录数: " + total);
System.out.println("总页数: " + pages);
System.out.println("当前页: " + current);
System.out.println("每页记录数: " + size);
System.out.println("当前页数据: ");
records.forEach(System.out::println);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 7.1 带排序的分页查询
可以在分页查询中添加排序条件:
/**
* 带排序的分页查询示例
* 在分页查询中添加排序条件
*/
@Test
public void testPageWithSort() {
// 创建分页对象
Page<User> page = new Page<>(1, 10);
// 设置排序
page.addOrder(OrderItem.desc("create_time")); // 按创建时间降序
page.addOrder(OrderItem.asc("age")); // 再按年龄升序
// 创建查询条件
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.gt(User::getAge, 18);
// 执行分页查询
Page<User> userPage = userMapper.selectPage(page, queryWrapper);
// 输出分页结果
userPage.getRecords().forEach(System.out::println);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 7.2 自定义分页查询
在复杂业务场景中,可能需要自定义分页查询SQL:
/**
* 自定义SQL的分页查询
* 在Mapper接口中定义
*/
public interface UserMapper extends BaseMapper<User> {
/**
* 自定义分页查询
* 注意:Page对象必须是第一个参数
* @param page 分页参数
* @param status 状态条件
* @param minAge 最小年龄
* @return 分页结果
*/
Page<UserVO> selectUserVOPage(Page<UserVO> page, @Param("status") String status, @Param("minAge") Integer minAge);
}
/**
* UserMapper.xml中定义对应的SQL
*/
/* XML配置
<select id="selectUserVOPage" resultType="com.example.vo.UserVO">
SELECT
u.id, u.name, u.age, u.email, d.dept_name
FROM
user u
LEFT JOIN
department d ON u.dept_id = d.id
<where>
<if test="status != null and status != ''">
AND u.status = #{status}
</if>
<if test="minAge != null">
AND u.age >= #{minAge}
</if>
</where>
ORDER BY u.id DESC
</select>
*/
/**
* 调用自定义分页查询
*/
@Test
public void testCustomPageQuery() {
// 创建分页对象
Page<UserVO> page = new Page<>(1, 10);
// 设置查询参数
String status = "active";
Integer minAge = 18;
// 执行自定义分页查询
Page<UserVO> userVOPage = userMapper.selectUserVOPage(page, status, minAge);
// 输出分页结果
System.out.println("总记录数: " + userVOPage.getTotal());
System.out.println("当前页数据: ");
userVOPage.getRecords().forEach(System.out::println);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 8. 多表关联查询
使用自定义SQL实现多表查询
MyBatis-Plus主要针对单表操作,复杂的多表查询一般通过自定义SQL实现:
/**
* 用户Mapper接口
*/
public interface UserMapper extends BaseMapper<User> {
/**
* 关联查询用户及其部门信息
* @param userId 用户ID
* @return 用户信息(包含部门信息)
*/
@Select("SELECT u.*, d.name as dept_name, d.code as dept_code " +
"FROM user u " +
"LEFT JOIN department d ON u.dept_id = d.id " +
"WHERE u.id = #{userId}")
UserDeptVO getUserWithDept(@Param("userId") Long userId);
/**
* 查询指定部门的所有用户
* @param deptId 部门ID
* @return 用户列表
*/
List<User> getUsersByDeptId(@Param("deptId") Long deptId);
}
/* XML配置
<select id="getUsersByDeptId" resultType="com.example.entity.User">
SELECT
u.*
FROM
user u
INNER JOIN
user_dept ud ON u.id = ud.user_id
WHERE
ud.dept_id = #{deptId}
ORDER BY
u.id
</select>
*/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 9. 常见查询场景示例
# 9.1 组合查询条件
/**
* 复杂组合查询条件示例
*/
@Test
public void testComplexQuery() {
// 创建查询条件
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
// 构建复杂查询条件:
// (status = 'active' OR status = 'pending')
// AND (age > 18 AND gender = '男')
// AND (create_time BETWEEN '2023-01-01' AND '2023-12-31')
queryWrapper
// 第一组条件(OR关系)
.and(wrapper -> wrapper
.eq(User::getStatus, "active")
.or()
.eq(User::getStatus, "pending")
)
// 第二组条件(AND关系)
.and(wrapper -> wrapper
.gt(User::getAge, 18)
.eq(User::getGender, "男")
)
// 第三组条件
.between(User::getCreateTime,
DateUtil.parseDate("2023-01-01"),
DateUtil.parseDate("2023-12-31")
)
// 排序
.orderByDesc(User::getCreateTime);
// 执行查询
List<User> userList = userMapper.selectList(queryWrapper);
// 输出查询结果
System.out.println("查询结果数量: " + userList.size());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 9.2 模糊查询与精确查询结合
/**
* 模糊查询与精确查询结合示例
*/
@Test
public void testMixedQuery() {
// 模拟搜索参数
String keyword = "技术"; // 关键字
String department = "研发部"; // 部门
// 创建查询条件
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
// 构建查询条件:
// 姓名或职位中包含关键字
// AND 部门等于指定部门
queryWrapper
.and(wrapper -> wrapper
.like(User::getName, keyword)
.or()
.like(User::getPosition, keyword)
)
.eq(User::getDepartment, department)
.orderByDesc(User::getCreateTime);
// 执行查询
List<User> userList = userMapper.selectList(queryWrapper);
// 输出查询结果
System.out.println("查询结果数量: " + userList.size());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 9.3 区间查询
/**
* 区间查询示例
*/
@Test
public void testRangeQuery() {
// 模拟查询参数
Integer minAge = 20;
Integer maxAge = 30;
Date startDate = DateUtil.parseDate("2023-01-01");
Date endDate = DateUtil.parseDate("2023-06-30");
// 创建查询条件
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
// 构建查询条件:
// 年龄在指定范围内
// 创建时间在指定日期范围内
queryWrapper
.between(User::getAge, minAge, maxAge)
.between(User::getCreateTime, startDate, endDate)
.orderByAsc(User::getAge);
// 执行查询
List<User> userList = userMapper.selectList(queryWrapper);
// 输出查询结果
System.out.println("查询结果数量: " + userList.size());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 10. 查询结果的映射规则
MyBatis-Plus对于结果映射有两种基本情况:
# 1. 基础映射规则:只能映射到泛型指定的实体类
使用BaseMapper内置方法(如selectById、selectList等)时,只能映射为Mapper接口中指定的泛型类型T:
// UserMapper接口定义
public interface UserMapper extends BaseMapper<User> {
// 所有内置方法返回的都是User或User集合
}
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public void example() {
// 返回User对象
User user = userMapper.selectById(1L);
// 返回User列表
List<User> users = userMapper.selectList(null);
// 错误:无法直接返回UserVO
// UserVO userVO = userMapper.selectById(1L); // 编译错误或运行时异常
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 2. 要使用VO/DTO等其他类型接收结果,必须自定义方法
正确的做法有以下几种:
方法1:自定义SQL方法(最常用)
public interface UserMapper extends BaseMapper<User> {
// 必须自定义SQL来返回非User类型
@Select("SELECT id, name as userName, age, email FROM user WHERE id = #{id}")
UserVO getUserVOById(@Param("id") Long id);
// 返回VO列表
@Select("SELECT id, name as userName, age, dept_name as deptName FROM user u " +
"LEFT JOIN department d ON u.dept_id = d.id")
List<UserVO> listUserVOs();
}
2
3
4
5
6
7
8
9
10
方法2:手动转换对象(代码中处理)
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
// 在Service层手动转换
public UserVO getUserVOById(Long id) {
// 先查询实体
User user = userMapper.selectById(id);
if (user == null) {
return null;
}
// 手动转换为VO
UserVO vo = new UserVO();
vo.setId(user.getId());
vo.setUserName(user.getName());
vo.setAge(user.getAge());
// 设置其他字段...
return vo;
}
// 使用工具类或框架批量转换
public List<UserVO> listUserVOs() {
List<User> users = userMapper.selectList(null);
// 可以使用BeanUtils、MapStruct或ModelMapper等工具
return users.stream()
.map(user -> {
UserVO vo = new UserVO();
BeanUtils.copyProperties(user, vo);
return vo;
})
.collect(Collectors.toList());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
方法3:使用继承关系(不推荐)
// VO继承实体类(不推荐,会造成紧耦合)
public class UserVO extends User {
private String deptName; // 额外字段
// getter/setter...
}
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
// 利用继承关系,可能会有类型安全问题
@SuppressWarnings("unchecked")
public List<UserVO> getUserVOsUnsafe() {
// 这种方式不类型安全,不推荐
return (List<UserVO>)(List<?>)userMapper.selectList(null);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
总结
- MyBatis-Plus的BaseMapper内置方法只能映射为泛型指定的实体类
- 如果需要使用VO/DTO/其他自定义类型接收,必须创建自定义方法
- 有三种方式处理自定义类型返回:
- 自定义SQL方法(最推荐)
- 手动对象转换(适合逻辑复杂情况)
- 继承关系转换(不推荐)
实际开发中,当需要自定义返回类型时,最常用和推荐的方式是编写自定义SQL方法,这样既保证类型安全,又可以优化查询性能。
# 11. 查询条件构建最佳实践
# 11.1 使用条件构造器的推荐方式
/**
* 条件构造器使用的最佳实践
*/
public List<User> getUserList(UserQuery query) {
// 1. 使用LambdaQueryWrapper,避免字段名硬编码
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
// 2. 使用condition条件参数,优雅处理动态条件
queryWrapper
.like(StringUtils.isNotBlank(query.getName()), User::getName, query.getName())
.ge(query.getMinAge() != null, User::getAge, query.getMinAge())
.le(query.getMaxAge() != null, User::getAge, query.getMaxAge())
.eq(StringUtils.isNotBlank(query.getStatus()), User::getStatus, query.getStatus());
// 3. 默认添加一个排序条件,避免结果不稳定
queryWrapper.orderByDesc(User::getCreateTime);
// 4. 对于大表,只查询需要的字段,减少数据传输量
queryWrapper.select(User::getId, User::getName, User::getAge);
// 5. 执行查询
List<User> userList = userMapper.selectList(queryWrapper);
return userList;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 11.2 条件构造器性能优化
/**
* 查询条件性能优化最佳实践
*/
public List<User> getOptimizedUserList(UserQuery query) {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
// 1. 添加索引字段条件优先
// 优先使用索引字段作为查询条件,提高查询效率
queryWrapper.eq(StringUtils.isNotBlank(query.getStatus()), User::getStatus, query.getStatus());
// 2. 避免使用左模糊查询,会导致索引失效
// 不推荐:queryWrapper.likeLeft(User::getName, "张")
// 推荐:使用右模糊或全模糊
queryWrapper.likeRight(StringUtils.isNotBlank(query.getName()), User::getName, query.getName());
// 3. 使用带索引的范围查询
queryWrapper.ge(query.getMinAge() != null, User::getAge, query.getMinAge());
// 4. 只查询必要的字段,避免大字段传输
queryWrapper.select(User::getId, User::getName, User::getAge);
// 5. 对于大数据量查询,使用分页
if (query.needPagination()) {
Page<User> page = new Page<>(query.getPageNum(), query.getPageSize());
return userMapper.selectPage(page, queryWrapper).getRecords();
}
// 对于一般查询,限制最大返回数量
queryWrapper.last("LIMIT 1000");
return userMapper.selectList(queryWrapper);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 11.3 集中管理查询条件
对于复杂系统,推荐将查询条件逻辑集中到专门的条件构造器类中:
/**
* 用户查询条件构造器
* 集中管理用户相关的查询条件构建逻辑
*/
public class UserQueryBuilder {
/**
* 构建基础查询条件
*/
public static LambdaQueryWrapper<User> buildBaseQuery(UserQuery query) {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
// 添加基础查询条件
queryWrapper
.like(StringUtils.isNotBlank(query.getName()), User::getName, query.getName())
.ge(query.getMinAge() != null, User::getAge, query.getMinAge())
.le(query.getMaxAge() != null, User::getAge, query.getMaxAge())
.eq(StringUtils.isNotBlank(query.getStatus()), User::getStatus, query.getStatus());
return queryWrapper;
}
/**
* 构建活跃用户查询条件
*/
public static LambdaQueryWrapper<User> buildActiveUserQuery(Date sinceDate) {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper
.eq(User::getStatus, "active")
.ge(sinceDate != null, User::getLastLoginTime, sinceDate)
.orderByDesc(User::getLastLoginTime);
return queryWrapper;
}
/**
* 构建新注册用户查询条件
*/
public static LambdaQueryWrapper<User> buildNewUserQuery(Integer days) {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
// 计算N天前的日期
Date startDate = DateUtils.addDays(new Date(), -days);
queryWrapper
.ge(User::getCreateTime, startDate)
.orderByDesc(User::getCreateTime);
return queryWrapper;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 12. 常见查询问题与解决方案
# 12.1 处理大数据量查询
当需要查询大量数据时,应避免一次性加载所有数据,可以采用以下方法:
/**
* 大数据量分页查询示例
* 避免一次性加载过多数据
*/
@Test
public void testLargeDataQuery() {
// 查询条件
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.gt(User::getAge, 18);
// 分页参数
long pageSize = 100; // 每页数量
long pageNum = 1; // 起始页码
boolean hasMore = true;
// 分批次查询处理
while (hasMore) {
Page<User> page = new Page<>(pageNum, pageSize);
page = userMapper.selectPage(page, queryWrapper);
List<User> records = page.getRecords();
if (records.isEmpty()) {
hasMore = false;
} else {
// 处理当前页数据
processUserData(records);
// 判断是否还有更多数据
hasMore = page.getCurrent() < page.getPages();
pageNum++;
}
}
}
private void processUserData(List<User> users) {
// 处理用户数据的逻辑
System.out.println("处理用户数据:" + users.size() + "条");
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 12.2 处理动态排序
有时候需要根据用户选择的字段进行排序:
/**
* 动态排序示例
* 根据用户选择的字段和顺序进行排序
*/
public List<User> getUsersWithDynamicSort(String sortField, boolean isAsc) {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
// 设置查询条件
queryWrapper.eq(User::getStatus, "active");
// 动态排序
switch (sortField) {
case "age":
queryWrapper.orderBy(true, isAsc, User::getAge);
break;
case "createTime":
queryWrapper.orderBy(true, isAsc, User::getCreateTime);
break;
case "name":
queryWrapper.orderBy(true, isAsc, User::getName);
break;
default:
// 默认按创建时间降序
queryWrapper.orderByDesc(User::getCreateTime);
}
return userMapper.selectList(queryWrapper);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 12.3 查询结果映射问题
有时候需要将查询结果映射到不同的对象:
/**
* 查询结果映射示例
* 将查询结果映射到DTO对象
*/
public List<UserDTO> getUserDTOs() {
// 1. 先查询实体对象
List<User> users = userMapper.selectList(new LambdaQueryWrapper<User>()
.eq(User::getStatus, "active"));
// 2. 转换为DTO对象
return users.stream()
.map(user -> {
UserDTO dto = new UserDTO();
dto.setId(user.getId());
dto.setName(user.getName());
dto.setAge(user.getAge());
dto.setStatus(user.getStatus());
// 设置其他属性...
return dto;
})
.collect(Collectors.toList());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 13. 高级查询技巧
# 13.1 使用嵌套查询
/**
* 嵌套查询示例
* 构建复杂的嵌套条件
*/
@Test
public void testNestedQuery() {
// 创建查询条件
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
// 构建复杂条件:
// (age < 30 AND (gender = '男' OR status = 'active'))
// OR (age >= 30 AND gender = '女')
queryWrapper
.and(wrapper -> wrapper
.lt(User::getAge, 30)
.and(innerWrapper -> innerWrapper
.eq(User::getGender, "男")
.or()
.eq(User::getStatus, "active")
)
)
.or(wrapper -> wrapper
.ge(User::getAge, 30)
.eq(User::getGender, "女")
);
// 执行查询
List<User> userList = userMapper.selectList(queryWrapper);
// 输出查询结果
System.out.println("查询结果数量: " + userList.size());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 13.2 使用SQL函数和表达式
/**
* SQL函数和表达式示例
* 在查询条件中使用数据库函数
*/
@Test
public void testSqlFunctions() {
// 创建QueryWrapper对象
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 1. 使用日期函数
queryWrapper.apply("DATE_FORMAT(create_time, '%Y-%m') = {0}", "2023-08");
// 2. 使用字符串函数
queryWrapper.apply("LOWER(name) = {0}", "john");
// 3. 使用数学计算
queryWrapper.apply("age * 2 > {0}", 50);
// 执行查询
List<User> userList = userMapper.selectList(queryWrapper);
// 输出查询结果
System.out.println("查询结果数量: " + userList.size());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 13.3 使用EXISTS和NOT EXISTS
/**
* EXISTS和NOT EXISTS示例
* 使用子查询检查关联数据存在性
*/
@Test
public void testExistsQuery() {
// 创建QueryWrapper对象
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 使用EXISTS:查询有订单的用户
queryWrapper.exists("SELECT 1 FROM order o WHERE o.user_id = user.id");
// 使用NOT EXISTS:查询没有订单的用户
// queryWrapper.notExists("SELECT 1 FROM order o WHERE o.user_id = user.id");
// 执行查询
List<User> userList = userMapper.selectList(queryWrapper);
// 输出查询结果
System.out.println("查询结果数量: " + userList.size());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21