MyBatis-Plus 分页使用详解

1. 配置分页插件

首先需要配置分页插件:

java 复制代码
@Configuration
@MapperScan("com.example.mapper")
public class MybatisPlusConfig {
    
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 添加分页插件
        PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();
        // 设置请求的页面大于最大页后的操作,true调回到首页,false继续请求,默认false
        paginationInterceptor.setOverflow(false);
        // 设置单页限制数量,默认无限制
        paginationInterceptor.setMaxLimit(500L);
        // 设置数据库类型
        paginationInterceptor.setDbType(DbType.MYSQL);
        
        interceptor.addInnerInterceptor(paginationInterceptor);
        return interceptor;
    }
}

2. 基本使用方式

2.1 简单分页查询
java 复制代码
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    
    // 分页查询所有用户
    public void pageQuery() {
        // 参数1:当前页,参数2:每页条数
        Page<User> page = new Page<>(1, 10);
        Page<User> userPage = userMapper.selectPage(page, null);
        
        System.out.println("总记录数:" + userPage.getTotal());
        System.out.println("总页数:" + userPage.getPages());
        System.out.println("当前页:" + userPage.getCurrent());
        System.out.println("每页条数:" + userPage.getSize());
        System.out.println("数据列表:" + userPage.getRecords());
    }
}
2.2 条件分页查询
java 复制代码
public void pageQueryWithCondition() {
    Page<User> page = new Page<>(1, 10);
    
    // 构建查询条件
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(User::getStatus, 1)
           .ge(User::getAge, 18)
           .orderByDesc(User::getCreateTime);
    
    Page<User> userPage = userMapper.selectPage(page, wrapper);
}
2.3 自定义分页方法

Mapper接口:

java 复制代码
public interface UserMapper extends BaseMapper<User> {
    // 自定义分页查询
    Page<User> selectUserPage(Page<User> page, @Param(Constants.WRAPPER) Wrapper<User> wrapper);
    
    // 多表关联分页
    Page<UserVO> selectUserWithRole(Page<UserVO> page, @Param("age") Integer age);
}

XML映射文件:

XML 复制代码
<!-- 方式1:使用Wrapper -->
<select id="selectUserPage" resultType="com.example.entity.User">
    SELECT * FROM user
    ${ew.customSqlSegment}
</select>

<!-- 方式2:多表关联 -->
<select id="selectUserWithRole" resultType="com.example.vo.UserVO">
    SELECT u.*, r.role_name 
    FROM user u
    LEFT JOIN user_role ur ON u.id = ur.user_id
    LEFT JOIN role r ON ur.role_id = r.id
    <where>
        <if test="age != null">
            AND u.age = #{age}
        </if>
    </where>
</select>

3. 高级特性

3.1 多表联查分页
java 复制代码
@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;
    
    public Page<OrderVO> getOrderPage(Integer current, Integer size, String userName) {
        Page<OrderVO> page = new Page<>(current, size);
        return orderMapper.selectOrderPage(page, userName);
    }
}
XML 复制代码
<!-- OrderMapper.xml -->
<select id="selectOrderPage" resultType="com.example.vo.OrderVO">
    SELECT o.id, o.order_no, o.amount, u.user_name, u.phone
    FROM orders o
    LEFT JOIN user u ON o.user_id = u.id
    <where>
        <if test="userName != null and userName != ''">
            AND u.user_name LIKE CONCAT('%', #{userName}, '%')
        </if>
    </where>
    ORDER BY o.create_time DESC
</select>
3.2 分页结果封装
java 复制代码
@Data
public class PageResult<T> {
    private Long total;
    private Long pages;
    private Long current;
    private Long size;
    private List<T> records;
    
    public static <T> PageResult<T> of(Page<T> page) {
        PageResult<T> result = new PageResult<>();
        result.setTotal(page.getTotal());
        result.setPages(page.getPages());
        result.setCurrent(page.getCurrent());
        result.setSize(page.getSize());
        result.setRecords(page.getRecords());
        return result;
    }
}

// 使用
PageResult<User> result = PageResult.of(userPage);
3.3 分页排序
java 复制代码
// 方式1:通过Page对象排序
Page<User> page = new Page<>(1, 10);
page.addOrder(new OrderItem("age", true));  // 升序
page.addOrder(new OrderItem("create_time", false)); // 降序

// 方式2:通过Wrapper排序
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.orderByAsc(User::getAge)
       .orderByDesc(User::getCreateTime);

Page<User> userPage = userMapper.selectPage(page, wrapper);

4. 性能优化建议

4.1 使用count优化
java 复制代码
// 当数据量大时,可以控制是否查询总记录数
Page<User> page = new Page<>(1, 10);
page.setSearchCount(false);  // 不查询总记录数,提升性能

// 或使用乐观锁优化
PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();
paginationInterceptor.setOptimizeJoin(true);  // 优化join查询
4.2 避免N+1问题
java 复制代码
// 不推荐:循环查询
List<User> users = userMapper.selectList(null);
for (User user : users) {
    user.getOrders();  // 每次都会查询数据库
}

// 推荐:使用联表查询或批量查询
Page<UserOrderVO> page = new Page<>(1, 10);
Page<UserOrderVO> result = userMapper.selectUserWithOrders(page);

5. 完整示例

java 复制代码
@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @GetMapping("/page")
    public Result<PageResult<User>> getUsers(
            @RequestParam(defaultValue = "1") Integer current,
            @RequestParam(defaultValue = "10") Integer size,
            @RequestParam(required = false) String name,
            @RequestParam(required = false) Integer minAge) {
        
        Page<User> page = new Page<>(current, size);
        
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(StringUtils.hasText(name), User::getName, name)
               .ge(minAge != null, User::getAge, minAge)
               .orderByDesc(User::getCreateTime);
        
        Page<User> userPage = userService.page(page, wrapper);
        return Result.success(PageResult.of(userPage));
    }
}

6. 常见问题

问题 解决方案
分页不生效 检查是否配置了分页插件
总记录数为0 检查SQL是否正确,参数是否传递
分页位置错误 分页参数必须是第一个参数
内存溢出 检查是否设置了合理的分页大小

注意事项

  1. 分页参数必须是第一个参数(在自定义方法中)

  2. 必须配置分页插件,否则分页不生效

  3. Page对象会自动处理总记录数,不需要手动count

  4. 多租户、逻辑删除等插件会与分页插件产生顺序影响

相关推荐
不谈恋爱的猫10 小时前
redis哨兵模式
数据库·redis·mybatis
步行cgn13 小时前
JNDI 详解:它是什么,为什么用,怎么用?
mybatis
paopaokaka_luck13 小时前
基于springboot3+vue3的云南本土影视文旅推荐平台(协同过滤算法、Echarts图形化分析)
前端·spring boot·学习·算法·echarts·mybatis
黄毛火烧雪下14 小时前
2026 Java后端面试题总结(持续更新):Spring、MyBatis、Redis、MySQL、多线程
java·spring·mybatis
paopaokaka_luck1 天前
基于springboot3+vue3的企业考勤管理系统(部门树递归、Echarts图形化分析)
开发语言·spring boot·学习·echarts·mybatis·需求分析·代码规范
SQL-First布道者2 天前
⚡ Spring JDBC 完整体系 · 第 8 讲 · 查询四象限完备
java·spring boot·后端·spring·mybatis·spring jdbc
乐观的Terry2 天前
11、发布系统-用户认证与权限体系
java·spring boot·spring·spring cloud·mybatis
步行cgn2 天前
carList.forEach(System.out::println)
java·开发语言·mybatis
豆角焖肉4 天前
MyBatis多表关联查询(一对多/多对一/多对多)
mybatis·一对多·多表设计·多对多·一对一·多对一
学Linux的语莫4 天前
Redis 的两种持久化方式:RDB 和 AOF,一次讲懂
数据库·redis·mybatis