一、MyBatis-Plus查询方法体系概览
MyBatis-Plus作为MyBatis的增强工具,在保留原生功能基础上提供了丰富的查询接口。其核心查询方法可分为三大类:
- 基础查询:基于主键的精确查询
- 条件查询:通过Wrapper构建动态SQL
- 分页查询:集成物理分页与逻辑分页能力
这些方法通过统一的Service接口暴露,开发者无需编写XML映射文件即可完成复杂查询。以User实体为例,其对应的Mapper接口继承BaseMapper<User>后自动获得这些能力。
二、基础查询方法详解
2.1 selectById方法
// 根据主键查询单条记录User user = userMapper.selectById(1L);
该方法适用于已知主键值的精确查询场景,其内部实现通过<select resultMap="BaseResultMap">标签生成SQL,支持所有主键类型(Long/String/UUID等)。当查询结果为空时返回null,而非空集合。
2.2 selectOne方法
// 根据条件查询单条记录LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();wrapper.eq(User::getAge, 25).last("LIMIT 1"); // 显式限制返回条数User user = userMapper.selectOne(wrapper);
该方法要求查询条件必须唯一,当返回多条记录时会抛出TooManyResultsException。典型应用场景包括:
- 唯一性校验(如用户名查重)
- 精确匹配查询
- 结合
@TableId注解的主键查询替代方案
三、条件查询方法解析
3.1 Wrapper体系架构
MyBatis-Plus提供四种Wrapper实现:
QueryWrapper:基础条件构造器LambdaQueryWrapper:支持Lambda表达式的类型安全构造器UpdateWrapper:更新条件构造器LambdaUpdateWrapper:Lambda风格的更新构造器
3.2 链式调用示例
// 复杂条件查询示例List<User> users = userMapper.selectList(Wrappers.<User>lambdaQuery().between(User::getAge, 20, 30).like(User::getName, "张").isNotNull(User::getEmail).orderByDesc(User::getCreateTime));
该查询等价于以下SQL:
SELECT * FROM userWHERE age BETWEEN 20 AND 30AND name LIKE '%张%'AND email IS NOT NULLORDER BY create_time DESC
3.3 高级查询技巧
-
嵌套条件:使用
nested()方法实现括号逻辑wrapper.nested(w -> w.eq("role", "admin").or().eq("role", "super_admin")).eq("status", 1);
-
SQL注入防护:自动对参数进行预编译处理
- 自定义SQL片段:通过
apply()方法插入原生SQLwrapper.apply("date_format(create_time,'%Y-%m-%d') = {0}", "2023-01-01");
四、分页查询实现方案
4.1 基础分页配置
首先需要配置分页插件:
@Configurationpublic class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor());return interceptor;}}
4.2 分页查询示例
// 创建分页对象Page<User> page = new Page<>(1, 10); // 当前页,每页大小// 构建查询条件LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();wrapper.ge(User::getAge, 18);// 执行分页查询IPage<User> userPage = userMapper.selectPage(page, wrapper);// 获取分页信息long total = userPage.getTotal(); // 总记录数List<User> records = userPage.getRecords(); // 当前页数据long pages = userPage.getPages(); // 总页数
4.3 分页参数优化
- 性能优化:大数据量分页时建议使用
last("LIMIT {0},{1}", offset, size)替代Page对象 - 计数优化:对于已知总量的场景,可通过
optimizeJoin参数关闭关联查询的COUNT优化 - 自定义COUNT:重写
selectCount方法实现复杂计数逻辑
4.4 多表分页查询
对于关联查询的分页,推荐两种方案:
- 先查询主表ID:分页查询主表ID后,再通过IN查询详情
```java
// 分页查询ID
Page idPage = new Page<>(1, 100);
IPage ids = userMapper.selectObjsPage(idPage,
Wrappers.lambdaQuery().select(User::getId));
// 批量查询详情
List users = userMapper.selectBatchIds(ids.getRecords());
2. **使用存储过程**:将复杂分页逻辑封装到数据库存储过程# 五、最佳实践建议1. **Wrapper复用**:对于频繁使用的查询条件,建议封装为静态方法```javapublic class UserWrappers {public static LambdaQueryWrapper<User> activeUser() {return Wrappers.lambdaQuery().eq(User::getStatus, 1).gt(User::getCreateTime, LocalDateTime.now().minusYears(1));}}
- 查询缓存:对不常变动的查询结果使用本地缓存
-
字段过滤:使用
select()方法指定返回字段,减少数据传输量wrapper.select(User::getId, User::getName);
-
动态表名:通过
TableNameHandler实现分表查询 - SQL监控:集成日志框架监控慢查询
六、常见问题处理
- 分页总数不准确:检查是否有触发
optimizeJoin优化的条件 - Wrapper条件失效:确认是否在Service层手动调用了
clear()方法 - 多数据源分页:确保每个数据源都配置了分页插件
- Oracle分页:需额外配置
DialectModel为oracle分页模式
通过系统掌握这些查询方法,开发者可以显著提升数据访问层的开发效率,构建出高性能、易维护的持久层代码。在实际项目中,建议结合业务场景选择合适的查询方式,并注意SQL性能优化和安全防护。