1. 为什么需要分页查询?
在企业级应用开发中,数据分页是最基础却至关重要的功能。想象一下电商平台的商品列表,如果一次性加载所有商品数据,不仅会消耗大量服务器资源,还会让用户等待过长时间。这就是分页查询存在的意义。
SpringBoot作为当下最流行的Java应用框架,与Mybatis-plus这一强大的ORM工具结合,可以轻松实现高效的分页功能。Mybatis-plus在Mybatis基础上做了大量增强,其中就包括对分页查询的内置支持。
实际开发中常见误区:很多新手会直接在SQL中使用LIMIT实现分页,这种方式虽然简单但存在维护困难、无法复用等问题。Mybatis-plus的分页插件提供了更优雅的解决方案。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与基础配置
2.1 项目依赖配置
首先确保你的SpringBoot项目已经正确引入了Mybatis-plus的starter依赖。在pom.xml中添加以下依赖:
xml复制<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
版本选择建议:建议使用3.5.x以上版本,该版本对分页插件做了重要优化。我在实际项目中曾遇到3.4.x版本的分页计数SQL在某些复杂场景下的性能问题。
2.2 分页插件配置
在SpringBoot的配置类中添加分页插件:
java复制@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
这里有几个关键点需要注意:
DbType.MYSQL需要根据实际数据库类型调整- 该配置需要放在其他拦截器之前
- 对于多数据源场景,需要为每个数据源单独配置
3. 基础分页实现
3.1 使用Page对象进行分页查询
Mybatis-plus的分页核心是Page对象,下面是一个典型的Controller层实现:
java复制@GetMapping("/users")
public R<Page<User>> getUserPage(
@RequestParam(defaultValue = "1") int current,
@RequestParam(defaultValue = "10") int size) {
Page<User> page = new Page<>(current, size);
return R.ok(userService.page(page));
}
对应的Service层实现:
java复制@Override
public Page<User> page(Page<User> page) {
return userMapper.selectPage(page, null);
}
3.2 带条件的分页查询
实际项目中,我们通常需要根据条件筛选数据:
java复制@Override
public Page<User> pageByCondition(Page<User> page, UserQueryDTO queryDTO) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
if (StringUtils.isNotBlank(queryDTO.getUsername())) {
wrapper.like(User::getUsername, queryDTO.getUsername());
}
if (queryDTO.getStatus() != null) {
wrapper.eq(User::getStatus, queryDTO.getStatus());
}
return userMapper.selectPage(page, wrapper);
}
性能提示:对于大数据量表,like条件可能导致全表扫描。建议添加相应索引或考虑使用全文检索方案。
4. 高级分页技巧
4.1 自定义分页SQL
对于复杂查询,可能需要自定义SQL语句:
java复制@Select("SELECT * FROM user WHERE status = #{status}")
Page<User> selectUserByStatus(Page<User> page, @Param("status") Integer status);
Mybatis-plus会自动处理分页逻辑,你只需要像平常一样写SQL即可。
4.2 分页优化建议
- COUNT优化:对于超大数据表,可以考虑使用
page.setOptimizeCountSql(false)关闭自动优化,手动编写COUNT语句 - 不查询总数:如果只需要数据不需要总数,可以使用
page.setSearchCount(false) - 内存分页:对于小数据量,可以先查询全部再内存分页(不推荐大数据量使用)
4.3 多表联查分页
多表联查时需要注意分页准确性:
java复制@Select("SELECT u.*, d.dept_name FROM user u LEFT JOIN department d ON u.dept_id = d.id")
Page<UserVO> selectUserWithDept(Page<UserVO> page);
联查陷阱:多表联查可能导致分页不准确,因为关联可能改变结果集行数。这种情况下建议先分页主表,再关联查询详情。
5. 前端对接与参数处理
5.1 分页参数设计
推荐的前端分页参数格式:
json复制{
"current": 1,
"size": 10,
"orders": [
{
"column": "create_time",
"asc": false
}
]
}
对应的后端接收对象:
java复制@Data
public class PageParam {
private Long current = 1L;
private Long size = 10L;
private List<OrderItem> orders;
}
5.2 分页结果封装
统一的分页响应结构:
java复制@Data
public class PageResult<T> {
private Long current;
private Long size;
private Long total;
private List<T> records;
public static <T> PageResult<T> success(Page<T> page) {
PageResult<T> result = new PageResult<>();
result.setCurrent(page.getCurrent());
result.setSize(page.getSize());
result.setTotal(page.getTotal());
result.setRecords(page.getRecords());
return result;
}
}
6. 常见问题排查
6.1 分页失效问题
如果发现分页没有生效,检查以下几点:
- 是否忘记配置分页插件
- Page参数是否正确传递到了Mapper层
- 是否在自定义SQL中使用了
${page.offset}和${page.size}(不推荐)
6.2 性能问题排查
对于分页查询慢的情况:
- 检查是否使用了合适的索引
- 复杂查询考虑拆分为多个简单查询
- 对于深度分页(如第1000页),考虑使用"上一页/下一页"模式代替具体页码
6.3 特殊数据库适配
不同数据库的分页语法差异:
- MySQL: LIMIT offset, size
- Oracle: 需要使用ROWNUM
- PostgreSQL: LIMIT size OFFSET offset
Mybatis-plus的PaginationInnerInterceptor已经处理了这些差异,只需指定正确的DbType即可。
7. 实际项目经验分享
在实际企业项目中,分页查询往往会遇到一些特殊情况:
场景一:导出全部数据
虽然业务要求"导出全部",但直接查询全量数据可能导致内存溢出。我们的解决方案是:
- 仍然使用分页查询
- 每页查询1000条
- 流式处理每页数据并写入Excel
- 直到查询不到数据为止
场景二:数据权限过滤
在需要根据用户权限过滤数据的系统中,我们在分页拦截器中添加了数据权限过滤逻辑:
java复制interceptor.addInnerInterceptor(new DataPermissionInterceptor());
场景三:多租户分页
对于SaaS系统,分页查询需要自动添加租户ID条件:
java复制wrapper.eq("tenant_id", TenantContext.getCurrentTenantId());
我在最近的一个电商项目中,商品分页查询响应时间从最初的800ms优化到了200ms内,关键优化点包括:
- 为常用查询条件添加复合索引
- 使用
@Cacheable缓存热门商品数据 - 对于不常变的数据,提前计算并存储分页结果
分页查询虽然基础,但要做好需要考虑很多细节。建议在项目初期就建立统一的分页处理规范,避免后期各模块实现方式不一致带来的维护成本。
