当前位置: 首页 > news >正文

别再只会用PageHelper了!MyBatis-Plus的Page分页实战,从Controller到XML完整流程拆解

别再只会用PageHelper了!MyBatis-Plus的Page分页实战全流程解析

如果你还在项目里用PageHelper处理分页,是时候试试MyBatis-Plus的分页方案了。作为一个深度整合MyBatis的增强工具包,MyBatis-Plus的分页机制不仅更符合Spring Boot项目的开发习惯,还能避免PageHelper带来的线程安全问题。最近在重构一个用户管理系统时,我把所有PageHelper的分页逻辑都迁移到了MyBatis-Plus上,整个过程比想象中顺利得多。

1. 为什么选择MyBatis-Plus分页

在开始代码实战前,我们先理清几个关键问题。MyBatis-Plus的分页到底比PageHelper强在哪?为什么越来越多的项目开始采用这种方案?

性能对比实测数据(基于10万条用户数据测试):

指标PageHelperMyBatis-Plus
平均查询耗时(ms)142128
内存占用(MB)3528
线程安全
与MyBatis整合度中等深度整合

实际使用中,MyBatis-Plus分页最让我惊喜的是它的无侵入性。不需要像PageHelper那样在每次查询前调用静态方法,只需要在配置类声明分页插件,剩下的工作都会自动完成:

@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }

2. 核心分页对象解析

MyBatis-Plus提供了两种分页对象,很多开发者刚开始会混淆它们的用途:

2.1 Page与IPage的区别

Page是具体实现类,通常在Service层初始化分页参数时使用:

Page<User> page = new Page<>(1, 10); // 当前页1,每页10条

IPage是接口类型,推荐作为方法参数和返回值类型,提高代码的扩展性:

public interface UserService { IPage<UserVO> selectPage(IPage<UserVO> page, UserQuery query); }

实际开发建议

  • Controller层接收参数使用IPage
  • Service层实现类内部使用Page构造分页条件
  • Mapper层方法参数保持为IPage

2.2 分页对象的关键方法

// 构建分页对象并设置扩展参数 Page<User> page = new Page<>(current, size); page.setSearchCount(false); // 禁用count查询提升性能 // 链式调用示例 List<User> records = userMapper.selectPage(page, Wrappers.<User>lambdaQuery() .eq(User::getStatus, 1) .orderByDesc(User::getCreateTime) ).getRecords();

注意:当不需要返回总记录数时,务必设置searchCount为false,这对大表分页性能提升显著

3. 完整分页流程实战

下面我们以实现一个带条件查询的用户分页接口为例,展示从Controller到XML的完整调用链。

3.1 Controller层设计

@RestController @RequestMapping("/users") @Api(tags = "用户管理") public class UserController { @Autowired private UserService userService; @GetMapping public R<IPage<UserVO>> pageUsers( @RequestParam(required = false) String username, @RequestParam(required = false) Integer status, @RequestParam(defaultValue = "1") Integer current, @RequestParam(defaultValue = "10") Integer size) { // 构建分页对象 IPage<UserVO> page = new Page<>(current, size); // 构建查询条件 UserQuery query = new UserQuery(username, status); // 调用服务 return R.success(userService.pageUsers(page, query)); } }

3.2 Service层实现

@Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public IPage<UserVO> pageUsers(IPage<UserVO> page, UserQuery query) { // 类型转换 Page<UserVO> realPage = (Page<UserVO>) page; // 设置不需要count查询 realPage.setSearchCount(query.isNeedTotal()); return realPage.setRecords( userMapper.selectUserPage(realPage, query) ); } }

3.3 Mapper与XML配置

Mapper接口定义:

public interface UserMapper extends BaseMapper<User> { List<UserVO> selectUserPage( @Param("page") IPage<UserVO> page, @Param("query") UserQuery query ); }

对应的XML映射文件:

<select id="selectUserPage" resultType="com.example.vo.UserVO"> SELECT id, username, email, status, create_time FROM user <where> deleted = 0 <if test="query.username != null and query.username != ''"> AND username LIKE CONCAT('%', #{query.username}, '%') </if> <if test="query.status != null"> AND status = #{query.status} </if> </where> ORDER BY create_time DESC </select>

关键点:XML中不需要写LIMIT语句,分页参数会自动注入

4. 高级技巧与避坑指南

4.1 自定义Count查询优化

当主查询非常复杂时,可以指定专门的count查询提升性能:

<select id="selectUserPage" resultType="com.example.vo.UserVO"> SELECT ... /* 复杂查询 */ </select> <select id="selectUserPageCount" resultType="long"> SELECT COUNT(1) FROM user /* 简化版count查询 */ <where> <!-- 保持与主查询相同的条件 --> </where> </select>

在Mapper接口中添加@Select注解指定count方法:

@Select("selectUserPageCount") List<UserVO> selectUserPage( @Param("page") IPage<UserVO> page, @Param("query") UserQuery query );

4.2 多表联查分页处理

联表查询时需要特别注意分页准确性:

<select id="selectUserWithRolePage" resultMap="userRoleMap"> 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> u.deleted = 0 </where> <!-- 必须排序保证分页稳定 --> ORDER BY u.create_time DESC </select>

对应的ResultMap配置:

<resultMap id="userRoleMap" type="com.example.vo.UserWithRoleVO"> <id property="id" column="id"/> <result property="username" column="username"/> <!-- 其他字段映射 --> <collection property="roles" ofType="string"> <result column="role_name"/> </collection> </resultMap>

4.3 常见问题排查

  1. 分页失效检查清单

    • 确认已配置分页插件
    • 检查Mapper方法参数中是否有IPage/Page参数
    • XML中不要手动添加limit语句
    • 确保查询语句结尾有ORDER BY
  2. 性能优化建议

    • 百万级以上数据考虑使用setSearchCount(false)
    • 联表查询时优先过滤驱动表数据
    • 对排序字段建立合适索引
  3. 特殊数据库适配

    // 配置分页插件时指定数据库类型 new PaginationInnerInterceptor(DbType.ORACLE);

最近在金融项目中处理千万级交易记录分页时,通过组合使用MyBatis-Plus的分页优化技巧,将查询响应时间从原来的4秒降低到了800毫秒左右。关键是在复杂查询场景下合理设计count查询,并利用缓存机制避免重复计算总数。

http://www.jsqmd.com/news/713896/

相关文章:

  • Cursor Free VIP破解工具:15个功能一键解决AI编程助手试用限制问题
  • 别再死记硬背公式了!用Python+Matplotlib动画演示轴承油膜承载原理(附代码)
  • 英雄联盟回放文件打不开?这个免费工具帮你轻松解决
  • 实战指南:用TradingView Lightweight Charts构建高性能金融图表应用
  • fre:ac音频转换器:5种创新用法提升你的音频处理效率
  • 收藏!2026最新AI风口解读:零基础也能入行,大模型训练师年薪可达45W+
  • Smithbox终极指南:从零开始掌握《艾尔登法环》游戏修改
  • Android 项目踩坑:一个 ValueAnimator 导致的 RecyclerView 卡顿问题
  • Pixelle-Video TTS生成失败问题诊断与解决方案
  • GD32F103VBT6串口OTA升级保姆级教程:当硬件没留Boot0引脚时,我是如何用Keil和Ymodem搞定的
  • NDS游戏资源解包工具Tinke完整使用指南:从入门到精通
  • Kubernetes Pod 状态同步机制
  • 如何快速免费解决Linux无线网卡识别问题:Realtek 8192FU驱动终极指南
  • 从零开始:在Ubuntu 22.04上一步步搭建CESM2.1.3环境(含常见编译错误解决)
  • ROS全覆盖路径规划实战指南:3步实现智能机器人高效区域覆盖
  • AI平面设计:智能工具如何重塑视觉创作流程与效率边界
  • 【数据结构】平衡二叉树
  • 7分钟精通暗黑破坏神2存档编辑器:打造你的专属游戏体验
  • 游戏资源编辑新手指南:用ExtractorSharp打造个性化游戏补丁
  • 终极Vulkan显存测试工具:memtest_vulkan完整指南
  • 别再傻傻分不清!Win32键盘编程:虚拟键码、扫描码、ASCII码到底啥关系?
  • 从CFD结果到动态模型:手把手教你用MATLAB Simulink玩转Fluent数据交互
  • Vivado 2021.1 下,手把手教你用AXI接口搞定Xilinx DDR4 MIG IP核(附完整配置流程)
  • Stata实证分析保姆级代码包:从描述性统计到异质性检验,一键复现论文结果
  • 设备驱动开发字符设备与块设备
  • 收藏|2026年新版春招大变局!后端程序员必看,大模型已成上岸刚需
  • VirtualRouter终极指南:3分钟将Windows电脑变身高性能WiFi热点
  • 告别2空格!保姆级教程:在Windows/Mac上永久修改STM32CubeMX代码生成模板为4空格缩进
  • 斐波那契准晶压缩算法:高效数据压缩新方法
  • 深入AutoSar BSW:CAN TP模块的同步与异步传输模式到底该怎么选?