MyBatis-Plus 分页功能全面指南

一、环境准备

  1. 添加依赖
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>
  1. 配置分页插件
@Configuration
public class MybatisPlusConfig {
    
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        
        // 添加分页插件
        PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();
        paginationInterceptor.setDbType(DbType.MYSQL); // 设置数据库类型
        paginationInterceptor.setOverflow(true);      // 超出总页数后返回第一页
        
        interceptor.addInnerInterceptor(paginationInterceptor);
        return interceptor;
    }
}

二、基础分页使用

  1. 简单分页查询
// 创建分页对象(查询第2页,每页10条)
Page<User> page = new Page<>(2, 10);

// 执行分页查询
Page<User> result = userMapper.selectPage(page, null);

// 获取结果
List<User> users = result.getRecords(); // 当前页数据
long total = result.getTotal();        // 总记录数
long pages = result.getPages();        // 总页数
  1. 带条件分页查询
/ 创建查询条件
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name", "张")
            .gt("age", 18);

// 创建分页对象
Page<User> page = new Page<>(1, 5);

// 执行查询
Page<User> result = userMapper.selectPage(page, queryWrapper);

三、自定义SQL分页

  1. Mapper接口定义
public interface UserMapper extends BaseMapper<User> {
    
    // 方法1:使用@Select注解
    @Select("SELECT * FROM user WHERE status = #{status}")
    Page<User> selectByStatus(Page<User> page, @Param("status") Integer status);
    
    // 方法2:XML配置
    Page<User> selectComplexPage(Page<User> page, @Param("param") Map<String, Object> params);
}
2. XML配置示例
xml
<!-- resources/mapper/UserMapper.xml -->
<select id="selectComplexPage" resultType="User">
    SELECT * FROM user 
    WHERE age > #{param.minAge} 
      AND create_time > #{param.startDate}
    ORDER BY create_time DESC
</select>
3. Service层调用
java
public Page<User> getActiveUsers(int page, int size) {
    Page<User> pageParam = new Page<>(page, size);
    return userMapper.selectByStatus(pageParam, 1);
}

四、分页结果处理

  1. 获取分页数据
Page<User> result = userService.page(new Page<>(1, 10));

// 获取数据列表
List<User> records = result.getRecords();

// 获取分页信息
long current = result.getCurrent();   // 当前页
long size = result.getSize();        // 每页大小
long total = result.getTotal();      // 总记录数
long pages = result.getPages();      // 总页数
2. 转换VO对象
java
public Page<UserVO> getUserVOPage(int page, int size) {
    Page<User> userPage = userService.page(new Page<>(page, size));
    
    // 转换分页结果
    return userPage.convert(user -> {
        UserVO vo = new UserVO();
        vo.setId(user.getId());
        vo.setName(user.getName());
        vo.setAge(user.getAge());
        return vo;
    });
}

五、高级功能

  1. 不分页但获取总数
Page<User> page = new Page<>(1, -1); // size设为-1表示不分页
Page<User> result = userMapper.selectPage(page, null);

List<User> allUsers = result.getRecords(); // 所有数据
long total = result.getTotal();           // 总数
2. 自定义COUNT语句
java
@Select("SELECT * FROM user WHERE dept_id = #{deptId}")
@Options(countSql = "SELECT COUNT(1) FROM user WHERE dept_id = #{deptId}")
Page<User> selectByDept(Page<User> page, @Param("deptId") Long deptId);
  1. 多表关联分页优化
public Page<UserDTO> getUserWithDept(int page, int size) {
    Page<User> userPage = new Page<>(page, size);
    userMapper.selectPage(userPage, null);
    
    // 提取用户ID
    List<Long> userIds = userPage.getRecords().stream()
                                .map(User::getId)
                                .collect(Collectors.toList());
    
    // 批量查询部门信息
    Map<Long, Dept> deptMap = deptService.getDeptMapByUserIds(userIds);
    
    // 转换DTO
    return userPage.convert(user -> {
        UserDTO dto = new UserDTO();
        BeanUtils.copyProperties(user, dto);
        dto.setDeptName(deptMap.get(user.getDeptId()).getName());
        return dto;
    });
}

六、最佳实践

  1. Controller层统一分页响应
@GetMapping("/users")
public R<PageData<UserVO>> listUsers(
    @RequestParam(defaultValue = "1") int page,
    @RequestParam(defaultValue = "10") int size) {
    
    Page<UserVO> result = userService.getUserVOPage(page, size);
    
    // 封装统一响应格式
    PageData<UserVO> data = new PageData<>();
    data.setList(result.getRecords());
    data.setTotal(result.getTotal());
    data.setPage(result.getCurrent());
    data.setPageSize(result.getSize());
    data.setPages(result.getPages());
    
    return R.ok(data);
}
2. 分页参数封装
java
@Data
public class PageParam {
    @Min(1)
    private int page = 1;
    
    @Range(min = 1, max = 100)
    private int size = 10;
    
    private String sortField;
    private SortOrder sortOrder;
    
    public <T> Page<T> buildPage() {
        Page<T> page = new Page<>(this.page, this.size);
        
        // 添加排序
        if (sortField != null && sortOrder != null) {
            if (sortOrder == SortOrder.ASC) {
                page.addOrder(OrderItem.asc(sortField));
            } else {
                page.addOrder(OrderItem.desc(sortField));
            }
        }
        
        return page;
    }
    
    public enum SortOrder {
        ASC, DESC
    }
}

性能优化建议 避免大表COUNT:

java // 对于超大数据集,关闭自动COUNT Page page = new Page<>(1, 10, false);
索引优化:

sql
– 确保分页字段有索引 CREATE INDEX idx_create_time ON user(create_time);

深度分页优化:


// 使用游标分页替代传统分页
Cursor<User> cursor = userMapper.selectCursor(
    new QueryWrapper<User>().gt("id", lastId).orderByAsc("id"),
    new Cursor<>(lastId, 10)
);

七、常见问题解决

  1. 分页失效问题 原因:未配置分页插件或配置不正确 解决:

检查 @Configuration 类中的插件配置

确保 PaginationInnerInterceptor 已添加到拦截器

  1. 查询第二页返回第一页数据 原因:错误使用 setPages() 而不是 setCurrent()

正确用法:

java
Page<User> page = new Page<>();
page.setCurrent(2); // 设置当前页码
page.setSize(10);
  1. 自定义SQL分页无效
    原因:XML中的SQL未正确处理分页参数
    正确配置:
<select id="selectUsers" resultType="User">
    SELECT * FROM user</select>
    <!-- 不需要手动写LIMIT,插件会自动处理 -->
4. 排序失效 正确用法:
Page<User> page = new Page<>(1, 10);
page.addOrder(OrderItem.desc("create_time")); // 添加排序条件

八、完整示例项目结构

src/main/java
├── com.example
│   ├── config
│   │   └── MybatisPlusConfig.java   # 配置类
│   ├── controller
│   │   └── UserController.java      # 控制器
│   ├── entity
│   │   └── User.java                # 实体类
│   ├── mapper
│   │   └── UserMapper.java          # Mapper接口
│   ├── service
│   │   ├── UserService.java         # 服务接口
│   │   └── impl
│   │       └── UserServiceImpl.java # 服务实现
│   └── dto
│       ├── PageParam.java           # 分页参数
│       └── PageData.java            # 分页响应
resources
└── mapper
    └── UserMapper.xml               # SQL映射文件

提示:在实际项目中,建议将分页参数处理和响应封装提取到公共模块,保持代码统一性。

通过本指南,您可以全面掌握MyBatis-Plus分页功能,从基础使用到高级优化,解决常见问题,构建高效的分页查询系统。

生成mybatisplus 分页极速使用指南 用markdown格式输出
我们创建一个极速使用指南,重点突出最常用、最核心的步骤,去除冗余说明,让开发者能快速上手。
指南包括:环境准备、基本使用、自定义SQL分页、常见问题。
注意:使用Markdown格式
MyBatis-Plus 分页极速使用指南
只需3步完成高效分页开发

一、极速配置(30秒)

  1. 添加依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>
  1. 配置分页插件
@Configuration
public class MybatisConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return interceptor;
    }
}

二、核心使用(3种场景)
场景1:基础分页查询


// 创建分页对象(第2页,每页10条)
Page<User> page = new Page<>(2, 10);

// 执行查询(无条件)
Page<User> result = userMapper.selectPage(page, null);

// 获取结果
List<User> users = result.getRecords();  // 当前页数据
long total = result.getTotal();         // 总记录数

场景2:带条件分页


// 创建查询条件
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("name", "张").gt("age", 18);

// 执行分页查询
Page<User> result = userMapper.selectPage(new Page<>(1, 5), wrapper);
场景3:自定义SQL分页
java
// Mapper接口
@Select("SELECT * FROM user WHERE status = #{status}")
Page<User> selectByStatus(Page<User> page, @Param("status") Integer status);

// Service调用
Page<User> result = userMapper.selectByStatus(new Page<>(1, 10), 1);

三、进阶技巧(高效开发)

  1. 分页参数自动封装

@Data
public class PageParam {
    private int page = 1;
    private int size = 10;
    
    public <T> Page<T> buildPage() {
        return new Page<>(page, size);
    }
}

// Controller使用
@GetMapping("/users")
public PageData<User> listUsers(PageParam pageParam) {
    return userService.getPageData(pageParam.buildPage());
}
  1. 分页结果转换

public Page<UserVO> convertToVO(Page<User> userPage) {
    return userPage.convert(user -> {
        UserVO vo = new UserVO();
        BeanUtils.copyProperties(user, vo);
        return vo;
    });
}
  1. 深度分页优化(百万数据)

// 方法1:游标分页
Cursor<User> cursor = userMapper.selectCursor(
    new QueryWrapper<User>().gt("id", lastId),
    new Cursor<>(lastId, 100)
);

// 方法2:ID范围查询
List<User> users = userMapper.selectList(
    new QueryWrapper<User>()
        .gt("id", lastId)
        .orderByAsc("id")
        .last("LIMIT 100")
);

四、避坑指南

  1. 分页失效检查清单 ✅ 是否配置了PaginationInnerInterceptor

✅ 是否使用new Page<>(pageNum, pageSize)创建对象

✅ 自定义SQL是否返回Page类型

  1. 高频错误解决方案

// 错误:查询总是返回第一页
Page<User> page = new Page<>();
page.setCurrent(2);  // 必须设置当前页!
page.setSize(10);

// 错误:排序失效
Page<User> page = new Page<>(1, 10);
page.addOrder(OrderItem.desc("create_time"));  // 添加排序

五、最佳实践
Controller层标准写法

@GetMapping("/page")
public R<PageData<UserVO>> getUserPage(
    @RequestParam(defaultValue = "1") int page,
    @RequestParam(defaultValue = "10") int size) {
    
    Page<User> pageParam = new Page<>(page, size);
    Page<UserVO> result = userService.getUserPage(pageParam);
    
    return R.ok(PageData.from(result));
}

PageData响应封装

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageData<T> {
    private List<T> list;
    private long total;
    private long page;
    private long size;
    private long pages;
    
    public static <T> PageData<T> from(Page<T> page) {
        return new PageData<>(
            page.getRecords(),
            page.getTotal(),
            page.getCurrent(),
            page.getSize(),
            page.getPages()
        );
    }
}

效率提示:使用此指南可节省80%的分页开发时间!实际项目只需复制对应代码片段修改即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值