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. 多租户、逻辑删除等插件会与分页插件产生顺序影响

相关推荐
落木萧萧8252 小时前
MyBatisGX 批量操作:比 MyBatis-Plus 和 MyBatis-Flex 更好用、更快
mybatis·orm
Jabes.yang2 小时前
Java电商订单系统面试全流程解析:接口设计、数据库、微服务与分布式事务实战
java·微服务·mybatis·分布式事务·电商·订单系统·接口设计
cheems95274 小时前
[开发日记]Spring Boot + MyBatis-Plus 抽奖系统开发复盘:从奖品创建、活动校验到前端圈选人员失效的一次完整排障
前端·spring boot·mybatis
就叫_这个吧20 小时前
Java+MySQL+Mybatis+Junit4实现学生信息管理系统
java·mysql·mybatis
噢,我明白了1 天前
MyBatis-Plus 中IPage的分页查询
java·mybatis
basketball6161 天前
Redis基础:3. Redis 持久化(重要)
redis·bootstrap·mybatis
Knight_AL1 天前
MyBatis 报错:Parameter ‘xxx‘ not found 的原因与解决方案
java·tomcat·mybatis
我登哥MVP1 天前
Spring Boot 从“会用”到“精通”:Converter 原理
java·spring boot·servlet·maven·mybatis·converter
XiYang-DING2 天前
【MyBatis】${}与 #{}的区别
java·tomcat·mybatis