logo

MyBatis-Plus分页查询实战:从基础到进阶的全流程指南

作者:梅琳marlin2025.09.18 16:02浏览量:0

简介:本文详细解析MyBatis-Plus分页查询的实现原理,涵盖配置、基础用法、高级技巧及性能优化,提供可落地的代码示例与最佳实践。

一、MyBatis-Plus分页核心机制解析

MyBatis-Plus的分页功能通过PaginationInterceptor拦截器实现,其核心流程分为三步:

  1. SQL拦截与改写:拦截原始SQL,在末尾追加LIMIT offset, size子句
  2. 总数统计优化:默认执行COUNT(1)查询总记录数,可通过@SqlParser(filter = true)注解优化
  3. 结果封装:将查询结果与分页参数封装为Page<T>对象

配置示例(Spring Boot环境):

  1. @Configuration
  2. public class MybatisPlusConfig {
  3. @Bean
  4. public MybatisPlusInterceptor mybatisPlusInterceptor() {
  5. MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
  6. // 添加分页插件(3.5.1+版本推荐方式)
  7. interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
  8. return interceptor;
  9. }
  10. }

二、基础分页查询实现

1. Service层标准实现

  1. @Service
  2. public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
  3. @Override
  4. public Page<User> queryPage(Long current, Long size) {
  5. // 创建分页对象(自动计算offset)
  6. Page<User> page = new Page<>(current, size);
  7. // 执行分页查询(自动封装总数)
  8. return baseMapper.selectPage(page, null);
  9. }
  10. }

2. Controller层标准响应

  1. @RestController
  2. @RequestMapping("/user")
  3. public class UserController {
  4. @Autowired
  5. private UserService userService;
  6. @GetMapping("/list")
  7. public Result<Page<User>> list(
  8. @RequestParam(defaultValue = "1") Long current,
  9. @RequestParam(defaultValue = "10") Long size) {
  10. Page<User> page = userService.queryPage(current, size);
  11. return Result.success(page);
  12. }
  13. }

响应数据结构:

  1. {
  2. "records": [...], // 当前页数据
  3. "total": 100, // 总记录数
  4. "size": 10, // 每页大小
  5. "current": 1, // 当前页码
  6. "pages": 10 // 总页数
  7. }

三、进阶功能实现

1. 条件分页查询

  1. public Page<User> queryByCondition(Long current, Long size, String name) {
  2. Page<User> page = new Page<>(current, size);
  3. QueryWrapper<User> wrapper = new QueryWrapper<>();
  4. wrapper.like("name", name); // 添加模糊查询条件
  5. return baseMapper.selectPage(page, wrapper);
  6. }

2. 自定义SQL分页

对于复杂SQL,推荐使用XML方式:

  1. <!-- UserMapper.xml -->
  2. <select id="selectCustomPage" resultType="User">
  3. SELECT * FROM user
  4. WHERE age > #{age}
  5. ORDER BY create_time DESC
  6. </select>

Service层调用:

  1. public Page<User> customQueryPage(Long current, Long size, Integer age) {
  2. Page<User> page = new Page<>(current, size);
  3. // 手动设置SQL参数
  4. Map<String, Object> params = new HashMap<>();
  5. params.put("age", age);
  6. // 执行分页查询(需配合@Select注解使用)
  7. IPage<User> result = userMapper.selectCustomPage(page, params);
  8. return (Page<User>) result;
  9. }

3. 多表关联分页

对于关联查询,建议采用两种方案:

  1. DTO封装方案
    ```java
    public class UserWithRoleDTO {
    private User user;
    private Role role;
    // getters/setters
    }

public Page queryWithRole(Long current, Long size) {
Page page = new Page<>(current, size);
List users = baseMapper.selectPage(page, null).getRecords();

  1. return users.stream().map(user -> {
  2. UserWithRoleDTO dto = new UserWithRoleDTO();
  3. dto.setUser(user);
  4. // 查询关联角色(实际开发中建议批量查询优化)
  5. Role role = roleService.getByUserId(user.getId());
  6. dto.setRole(role);
  7. return dto;
  8. }).collect(Collectors.toCollection(Page::new)); // 需自定义Page转换

}

  1. 2. **MyBatis关联查询方案**(需XML配置):
  2. ```xml
  3. <select id="selectUserWithRole" resultMap="userWithRoleMap">
  4. SELECT u.*, r.name as role_name
  5. FROM user u LEFT JOIN user_role ur ON u.id = ur.user_id
  6. LEFT JOIN role r ON ur.role_id = r.id
  7. </select>

四、性能优化实践

1. 计数查询优化

对于大数据量表,建议:

  1. @Bean
  2. public MybatisPlusInterceptor mybatisPlusInterceptor() {
  3. MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
  4. PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
  5. // 关闭自动COUNT(需手动处理总数)
  6. paginationInnerInterceptor.setMaxLimit(-1L);
  7. interceptor.addInnerInterceptor(paginationInnerInterceptor);
  8. return interceptor;
  9. }

替代方案:使用缓存总数

  1. @Cacheable(value = "userCount", key = "#root.methodName")
  2. public Long getUserCount() {
  3. return baseMapper.selectCount(null);
  4. }

2. 数据库索引优化

建议为分页查询字段建立复合索引:

  1. ALTER TABLE user ADD INDEX idx_name_age (name, age);

3. 深度分页优化

对于深度分页(如第1000页),建议使用子查询优化:

  1. public Page<User> deepPageQuery(Long current, Long size) {
  2. Page<User> page = new Page<>(current, size);
  3. // 计算offset
  4. long offset = (current - 1) * size;
  5. QueryWrapper<User> wrapper = new QueryWrapper<>();
  6. wrapper.last("LIMIT " + offset + "," + size); // 3.5.1+版本慎用
  7. // 更推荐使用子查询方式
  8. return baseMapper.selectPage(page, wrapper);
  9. }

五、常见问题解决方案

1. 分页失效问题排查

  • 检查是否正确配置PaginationInterceptor
  • 确认Mapper接口是否继承BaseMapper<T>
  • 检查SQL是否包含ORDER BY(可能导致总数统计异常)

2. 总数统计错误处理

  1. @Override
  2. public Page<User> safeQueryPage(Long current, Long size) {
  3. Page<User> page = new Page<>(current, size);
  4. try {
  5. return baseMapper.selectPage(page, null);
  6. } catch (Exception e) {
  7. // 总数统计失败时降级处理
  8. page.setTotal(0L);
  9. List<User> records = baseMapper.selectList(null);
  10. // 手动分页(仅适用于小数据量)
  11. int fromIndex = (int) ((current - 1) * size);
  12. int toIndex = Math.min(fromIndex + size.intValue(), records.size());
  13. page.setRecords(records.subList(fromIndex, toIndex));
  14. return page;
  15. }
  16. }

3. 多数据源分页配置

对于多数据源场景,需分别配置:

  1. @Bean
  2. @ConfigurationProperties("spring.datasource.master")
  3. public DataSource masterDataSource() {
  4. return DataSourceBuilder.create().build();
  5. }
  6. @Bean
  7. public SqlSessionFactory masterSqlSessionFactory() throws Exception {
  8. MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
  9. factory.setDataSource(masterDataSource());
  10. factory.setPlugins(new Interceptor[]{
  11. new PaginationInnerInterceptor(DbType.MYSQL)
  12. });
  13. return factory.getObject();
  14. }

六、最佳实践建议

  1. 分页大小限制:建议前端限制最大分页大小为1000条
  2. 默认分页参数:Controller层应设置合理的默认值
  3. 结果缓存:对不常变动的分页数据添加缓存
  4. 异步加载:对于大数据量分页,考虑前端懒加载
  5. 监控告警:对深度分页查询添加监控日志

七、版本兼容说明

MyBatis-Plus版本 分页插件配置方式 注意事项
3.4.x及以下 @Bean public PaginationInterceptor paginationInterceptor() 已废弃
3.5.0+ MybatisPlusInterceptor + PaginationInnerInterceptor 推荐方式
最新版 支持多数据源分页 需显式指定DbType

本文通过12个核心代码示例、5个优化方案和3个问题解决方案,系统阐述了MyBatis-Plus分页查询的实现要点。实际开发中,建议结合项目特点选择适合的分页策略,并持续监控分页查询性能。对于超大数据量场景,可考虑采用Elasticsearch等搜索引擎实现分页功能。

相关文章推荐

发表评论