MyBatis Plus分页插件【PaginationInnerInterceptor】实战:从配置到高级特性详解

家有萌小主

1. MyBatis Plus分页插件入门指南

刚接触MyBatis Plus时,最让我头疼的就是分页查询的实现。传统的MyBatis需要手动编写limit语句,不同数据库语法还不一样,调试起来特别麻烦。直到发现了PaginationInnerInterceptor这个神器,我才真正体会到什么叫"开箱即用"。

PaginationInnerInterceptor是MyBatis Plus提供的分页拦截器,它能自动拦截SQL语句并添加分页逻辑。比如你执行一个普通的查询方法,它会在底层自动帮你加上LIMIT 10 OFFSET 20这样的语句。更棒的是,它支持多种数据库类型,MySQL、Oracle、PostgreSQL等都能自动适配,完全不用操心方言问题。

在实际项目中,我通常会这样初始化配置:

java复制@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 添加分页拦截器,指定数据库类型为MySQL
        PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        interceptor.addInnerInterceptor(paginationInterceptor);
        return interceptor;
    }
}

这个基础配置已经能满足80%的分页需求。但PaginationInnerInterceptor的强大之处在于,它还提供了许多实用的高级特性,比如防止恶意请求的超大分页、控制单页数据量上限等,这些我们后面会详细展开。

2. 分页插件核心配置详解

2.1 数据库类型适配

第一次使用PaginationInnerInterceptor时,我踩过一个坑:没有正确配置DbType。结果分页查询在测试环境(MySQL)跑得好好的,上了生产环境(Oracle)就直接报错。这是因为不同数据库的分页语法差异很大:

  • MySQL使用LIMIT offset, size
  • Oracle需要使用ROWNUM
  • PostgreSQL使用LIMIT size OFFSET offset

解决方法很简单,在创建拦截器时指定正确的数据库类型:

java复制// 根据实际数据库配置
new PaginationInnerInterceptor(DbType.MYSQL); 
// 或者
new PaginationInnerInterceptor(DbType.ORACLE);

如果项目需要支持多数据源,可以通过动态数据源切换时同步更新分页拦截器的数据库类型。我在一个多租户项目中就是这样处理的:

java复制// 在数据源切换后
PaginationInnerInterceptor innerInterceptor = (PaginationInnerInterceptor)interceptor.getInterceptors()
    .stream()
    .filter(i -> i instanceof PaginationInnerInterceptor)
    .findFirst()
    .orElse(null);
if(innerInterceptor != null){
    innerInterceptor.setDbType(newDbType);
}

2.2 溢出页处理策略

去年双十一大促时,我们的商品查询接口突然出现性能问题。排查发现是有用户直接修改URL参数,请求了pageNumber=99999这样不存在的页码。虽然数据不存在,但数据库还是要扫描大量记录,导致CPU飙升。

这就是setOverflow(true)该出场的时候了:

java复制paginationInterceptor.setOverflow(true);

当设置为true时,如果请求的页码超过最大页数(比如总共10页但请求第11页),会自动返回第一页数据。这就像电梯的楼层按钮,按了不存在的楼层会自动回到1楼一样。

我建议在Web项目中都开启这个配置,可以有效防止恶意爬虫或者参数篡改导致的性能问题。它的实现原理也很巧妙:

java复制// 伪代码展示overflow逻辑
if (currentPage > totalPage && overflow) {
    currentPage = 1; // 回到第一页
    rebuildLimit();  // 重新构建分页SQL
}

3. 高级特性实战技巧

3.1 单页数据量限制

记得有一次安全扫描,报告说我们的接口存在DoS风险,因为攻击者可以传pageSize=10000这样的大数值。这正是setMaxLimit的用武之地:

java复制// 限制单页最多100条记录
paginationInterceptor.setMaxLimit(100L);

设置后,即使客户端传size=1000,实际查询也只会返回100条。这就像自助餐厅的"每人每次限取三份"规定,防止有人一次性搬空整个餐台。

实际开发中,我通常会根据业务场景设置不同的上限:

  • 管理后台可以设置大一些(如500)
  • 移动端列表建议50-100
  • 图表数据可以更小(如20)
java复制// 根据不同业务场景动态设置
if (request.getRequestURI().contains("/mobile/")) {
    paginationInterceptor.setMaxLimit(50L);
} else if (request.getRequestURI().contains("/admin/")) {
    paginationInterceptor.setMaxLimit(500L);
}

3.2 自定义分页参数名

前后端联调时,经常遇到前端传的是pageIndex/pageSize,而后端默认接收的是current/size。与其让前端改,不如后端适配:

java复制// 在配置类中添加这个Bean
@Bean
public PaginationInnerInterceptor paginationInterceptor() {
    PaginationInnerInterceptor interceptor = new PaginationInnerInterceptor();
    // 设置请求参数名
    interceptor.setDbType(DbType.MYSQL);
    interceptor.setOptimizeJoin(true);
    // 关键配置:自定义参数名
    interceptor.setMaxLimit(100L);
    interceptor.setOverflow(true);
    return interceptor;
}

更灵活的做法是实现自定义的Page对象:

java复制public class CustomPage<T> extends Page<T> {
    @JsonProperty("page_index")
    private long current;
    
    @JsonProperty("page_size")
    private long size;
    
    // 构造方法...
}

4. 分页查询实战演示

4.1 基础分页查询

在DAO层,使用MyBatis Plus的分页查询简单得不可思议:

java复制// 创建分页参数(第2页,每页10条)
Page<User> page = new Page<>(2, 10);
// 执行查询
userMapper.selectPage(page, null);

执行后,page对象会包含所有分页信息:

java复制page.getRecords();    // 当前页数据列表
page.getCurrent();    // 当前页码
page.getSize();       // 每页大小
page.getTotal();      // 总记录数
page.getPages();      // 总页数
page.hasNext();       // 是否有下一页
page.hasPrevious();   // 是否有上一页

我在项目中最喜欢的是它的链式调用方式:

java复制userMapper.selectPage(new Page<>(1, 10), 
    Wrappers.<User>lambdaQuery()
        .gt(User::getAge, 18)
        .orderByAsc(User::getName))
    .getRecords()
    .forEach(System.out::println);

4.2 自定义分页SQL

遇到复杂查询时,可能需要手写SQL。MyBatis Plus同样支持:

xml复制<!-- mapper.xml -->
<select id="selectUserPage" resultType="User">
    SELECT * FROM user WHERE age > #{age} 
    ORDER BY create_time DESC
</select>

对应的Mapper接口:

java复制// 方法参数必须用@Param("page")标注
IPage<User> selectUserPage(@Param("page") Page<User> page, @Param("age") int age);

调用方式:

java复制Page<User> page = new Page<>(1, 10);
userMapper.selectUserPage(page, 18);
// page对象同样包含所有分页信息

4.3 多表联查分页

联表查询的分页要特别注意:如果直接使用JOIN,分页结果可能会不准确。我推荐两种解决方案:

  1. 使用子查询先分页再关联:
sql复制SELECT a.*, b.extra_info 
FROM (
    SELECT * FROM main_table 
    WHERE conditions 
    LIMIT 0, 10
) a LEFT JOIN extra_table b ON a.id = b.main_id
  1. 使用MyBatis Plus的selectPage优化:
java复制// 先查询主表分页
Page<Main> mainPage = mainMapper.selectPage(page, queryWrapper);
// 再批量查询关联数据
List<Extra> extras = extraMapper.selectBatchIds(
    mainPage.getRecords().stream()
        .map(Main::getId)
        .collect(Collectors.toList())
);

5. 性能优化与常见问题

5.1 避免COUNT查询

在数据量大的表中,COUNT操作可能非常耗时。如果不需要知道总页数,可以关闭这个功能:

java复制Page<User> page = new Page<>(1, 10);
// 关闭优化COUNT SQL
page.setSearchCount(false);
userMapper.selectPage(page, null);

这样生成的SQL就不会包含COUNT语句,性能能提升50%以上。我在一个500万数据的订单表中测试过:

  • 开启searchCount:1200ms
  • 关闭searchCount:400ms

5.2 分页缓存策略

对于变化不频繁的数据,可以考虑缓存分页结果。我的常用方案:

java复制public Page<User> getUsersWithCache(int pageNum, int pageSize) {
    String cacheKey = "users:page:" + pageNum + ":" + pageSize;
    Page<User> page = (Page<User>) redisTemplate.opsForValue().get(cacheKey);
    if (page == null) {
        page = userMapper.selectPage(new Page<>(pageNum, pageSize), null);
        redisTemplate.opsForValue().set(cacheKey, page, 5, TimeUnit.MINUTES);
    }
    return page;
}

注意要设置合理的过期时间,并在数据变更时清除相关缓存。

5.3 常见问题排查

  1. 分页失效怎么办?
  • 检查是否正确配置了拦截器
  • 确认Page对象作为第一个参数
  • 查看生成的SQL日志
  1. 排序不生效?
  • 检查是否有多个ORDER BY冲突
  • 确认Wrapper中的orderBy方法调用正确
  1. 性能突然下降?
  • 检查是否查询了所有字段
  • 确认是否有合适的索引
  • 考虑使用searchCount(false)

我在实际项目中总结了一个检查清单:

  • [ ] 拦截器配置正确
  • [ ] 数据库类型匹配
  • [ ] Page对象创建正确
  • [ ] 没有N+1查询问题
  • [ ] 关键字段有索引

内容推荐

Ureport2分组统计实战:小计与合计的父格配置精解
本文深入解析Ureport2分组统计功能中父格配置的核心原理与实战技巧,重点讲解如何正确设置小计与合计功能。通过实际案例演示父格配置方法,包括左父格和上父格的使用场景,帮助开发者避免常见错误,提升报表开发效率。
ICLR 2025 | TIMEMIXER++:从一维时序到二维图像,揭秘通用预测的SOTA突破
ICLR 2025论文TIMEMIXER++提出了一种革命性的时序预测方法,通过将一维时间序列转换为二维图像,结合双轴注意力机制和多尺度处理,实现了SOTA性能。该方法在金融预测、医疗诊断和工业维护等领域展现出卓越效果,计算效率比传统Transformer提升75%,为通用时序AI树立了新标杆。
pyqtgraph绘图实战指南:从PlotWidget到GraphicsLayout的灵活应用
本文详细介绍了pyqtgraph绘图实战指南,从PlotWidget的快速绘图到GraphicsLayout的复杂布局应用。通过实例演示如何灵活使用PlotWidget、PlotItem和GraphicsLayout,提升数据可视化效率,适用于传感器监控、ECG心电图等场景。
GNU Radio消息传递:从异步通信到外部交互的实战解析
本文深入解析GNU Radio消息传递机制,从异步通信原理到外部系统交互实践,详细介绍了消息端口注册、订阅机制及处理函数编写技巧。通过实战案例展示如何与ZeroMQ、REST API等外部系统集成,并分享性能优化与常见问题排查方法,帮助开发者高效利用消息传递机制提升软件无线电系统灵活性。
图像隐写分析实战——从数据集构建到含密图像生成
本文详细介绍了图像隐写分析的全过程,从数据集构建到含密图像生成,涵盖了S-UNIWARD、HUGO和WOW等算法的实战应用。通过具体代码示例和效果评估,帮助读者掌握生成含密图像的技术要点,提升在商业安全和知识产权保护领域的应用能力。
A2FSeg解析:自适应多模态融合网络在医学图像分割中的创新实践
本文深入解析A2FSeg网络在医学图像分割中的创新应用,重点介绍其自适应多模态融合网络设计。通过双阶段融合策略(平均融合与注意力机制驱动的自适应融合),有效解决临床中模态缺失问题,在BraTS2020数据集上展现优越性能。该框架不仅提升脑肿瘤分割精度,还具备向肝脏肿瘤等多病种扩展的潜力,为计算机辅助诊断提供新思路。
从电磁到热流:基于HFSS与Icepak的微带电路热设计实战解析
本文详细解析了基于HFSS与Icepak的微带电路热设计实战方法,涵盖电磁-热流协同仿真的必要性、模型准备、参数设置及散热优化。通过实际案例展示如何解决工程中常见的过热问题,提升系统可靠性,为射频/微波系统设计提供全面的热仿真指导。
SAP资产折旧调整实战:ABAA与ABMA的深度辨析与应用指南
本文深入解析SAP资产管理中ABAA与ABMA的核心区别与应用场景,帮助用户准确执行资产折旧调整。通过实战案例和配置指南,详细说明非计划折旧(ABAA)与折旧冲销(ABMA)的操作流程及账务影响,避免常见错误,提升资产管理效率。
Ubuntu18下IPQ6000 OpenWrt编译全流程:从环境配置到成功烧录
本文详细介绍了在Ubuntu18系统下为IPQ6000芯片编译OpenWrt固件的完整流程,从环境配置、源代码获取到解决常见编译错误和最终固件烧录。特别针对IPQ6000平台的特性,提供了实用的优化建议和硬件适配指南,帮助开发者高效完成嵌入式路由器固件开发。
告别玄学调参:用实际波形图带你一步步调试LPDDR5的Read Gate Training(附RDQS信号分析)
本文深入探讨了LPDDR5信号调试中的Read Gate Training技术,通过实际波形图分析RDQS信号,帮助工程师优化参数设置。文章详细介绍了调试装备配置、Toggle Mode和Enhanced Mode的实战流程,以及高级调试技巧,为DDR信号完整性提供了实用解决方案。
树莓派玩家看过来:用安信可M62-CBS模组(BL616芯片)给你的Pi加装双频Wi-Fi和蓝牙,保姆级教程
本文详细介绍了如何为树莓派安装安信可M62-CBS模组(基于BL616芯片),以提升双频Wi-Fi和蓝牙5.0性能。教程涵盖硬件连接、驱动编译、固件部署及实战配置,特别适合需要稳定无线连接和低功耗蓝牙功能的树莓派玩家。通过SDIO或USB接口,轻松实现高性能无线升级。
AUTOSAR内存管理进阶:拆解vLinkGen如何实现多阶段数据初始化(Zero/One/Early阶段实战)
本文深入解析AUTOSAR架构下vLinkGen模块的多阶段数据初始化策略,包括ZERO、ONE、EARLY等阶段的实战配置。通过详细代码示例和配置说明,帮助开发者实现嵌入式系统启动过程的精准控制,提升内存安全性和系统可靠性。特别适用于汽车电子和功能安全关键系统的开发。
Vben Admin中Vxe Table自定义筛选组件的设计与实践
本文详细介绍了在Vben Admin项目中如何设计与实现Vxe Table自定义筛选组件。通过三层模型架构设计、关键实现细节剖析以及与Vxe Table的深度集成,帮助开发者掌握自定义筛选组件的开发技巧,提升表格功能的灵活性和扩展性。特别适合需要处理复杂业务场景的前端开发者参考。
从实验室到产线:TWS耳机ANC调试实战与一致性管控
本文详细解析了TWS耳机ANC调试从实验室到量产的全流程,包括消音室环境搭建、参数调优技巧和生产一致性控制。重点介绍了调试环境的关键要素、滤波器配置的实用技巧以及量产中的常见问题解决方案,帮助工程师提升ANC调试效率与产品一致性。
STM32 Flash写保护锁死?巧用ST-LINK Utility解锁与防护全解析
本文详细解析了STM32 Flash写保护锁死的现象及解决方案,重点介绍了使用ST-LINK Utility进行解锁的实战指南。通过分步操作流程和常见问题排查技巧,帮助开发者有效应对Flash Timeout等错误,同时深入探讨了STM32的多级保护机制和防护策略,为嵌入式开发提供实用参考。
手把手教你用迅雷+WinSCP搞定Linux服务器上的Ollama离线更新(附Qwen3模型适配指南)
本文详细介绍了如何利用迅雷和WinSCP在Linux服务器上实现Ollama的离线更新,并提供了Qwen3模型的适配指南。通过分阶段下载策略和图形化传输工具,开发者可以高效完成AI服务的更新与部署,显著提升工作效率。
Windows下保姆级部署腾讯混元3D模型:从Anaconda到成功渲染一棵红柳树
本文提供Windows系统下腾讯混元3D模型的完整部署教程,涵盖从Anaconda环境配置到成功渲染3D红柳树的全流程。详细讲解PyTorch版本选择、模型文件获取、依赖管理及常见问题解决方案,帮助开发者在消费级硬件上实现专业级3D内容生成。特别针对NVIDIA显卡优化,提供性能调优建议和创意应用思路。
硬件设计——反激电源MOS管波形解析(1)
本文深入解析反激电源中MOS管的工作波形,探讨其在导通和关断阶段的电压电流特性。通过实际测试案例,揭示波形异常的原因及解决方案,帮助硬件工程师优化电源设计,提升效率和可靠性。重点关注MOS管波形分析在反激电源调试中的关键作用。
Flowable7.x实战指南(五)Vue3+SpringBoot3混合存储架构下的流程定义管理界面实现
本文详细介绍了在Vue3+SpringBoot3混合存储架构下实现Flowable流程定义管理界面的实战指南。通过MySQL+MongoDB的混合存储方案,优化流程定义管理的性能与灵活性,涵盖后端API设计、前端界面开发及数据一致性保障方案,助力开发者高效构建企业级流程管理系统。
泰凌微 TLSR8208 开发避坑指南:透传、串口与调试实战解析
本文详细解析了泰凌微TLSR8208蓝牙芯片开发中的常见问题,包括透传数据错位、串口与Debug引脚冲突等,提供了实用的解决方案和调试技巧,帮助开发者高效避坑。
已经到底了哦
精选内容
热门内容
最新内容
告别‘脑补’失败:PCDreamer如何用多视角图像解决复杂物体点云补全难题?
PCDreamer通过多视角扩散先验技术,革命性地解决了复杂物体点云补全难题。该方法将3D点云问题降维至2D图像处理,利用扩散模型的物体常识生成合理结构,再升维回3D空间,显著提升了细长结构、对称元素和拓扑复杂部件的补全精度。实验显示其平均Chamfer Distance降低38.7%,为自动驾驶、工业检测等场景提供了可靠解决方案。
别再死磕代码了!Origin弦图配色与图例美化全攻略(让审稿人眼前一亮)
本文详细介绍了Origin弦图的视觉升级技巧,从色彩美学到图例美化,帮助研究者打造专业级数据可视化效果。通过色彩理论应用、弦图结构优化和图例定制,提升弦图的视觉冲击力和学术呈现质量,让审稿人眼前一亮。
Zabbix API实战指南:从认证到自动化监控配置
本文详细介绍了Zabbix API的实战应用,从认证机制到自动化监控配置,帮助用户高效管理监控系统。内容包括主机管理、监控项配置、触发器设置等核心功能,并提供了Python代码示例和最佳实践,适合需要提升Zabbix自动化水平的运维人员。
ENVI植被指数计算实战:从NDVI到NDWI的完整指南
本文详细介绍了使用ENVI软件计算植被指数(如NDVI和NDWI)的完整流程与实战技巧。从波段选择、公式输入到异常值处理,结合BAND MATH工具的具体操作步骤,帮助读者掌握遥感影像分析的核心技术。文章还对比了ENVI与GEE的优缺点,并分享了项目实战中的宝贵经验与常见问题解决方案。
深入解析K8s Node节点连接拒绝问题:从dial tcp 127.0.0.1:8080错误到解决方案
本文深入解析Kubernetes Node节点连接拒绝问题,特别是'dial tcp 127.0.0.1:8080: connect: connection refused'错误的五大常见原因及解决方案。从环境变量配置、API服务器状态到网络连接性问题,提供系统化排查流程和实战解决方案,帮助开发者快速定位和修复K8s节点连接问题。
交叉验证的5种实战用法:从Scikit-learn的`cross_val_score`到防止模型“过拟合”你的验证集
本文深入探讨了交叉验证的5种高阶实战策略,从基础的K折到对抗验证集过拟合的嵌套交叉验证。通过Scikit-learn的`cross_val_score`等工具,帮助数据科学家在模型评估中避免常见陷阱,确保验证结果真实可靠。特别针对训练集、验证集和测试集的分割问题,提供了分层K折、时间序列CV等专业解决方案。
MySQL事务隔离级别深度解析:从理论到实战,彻底搞懂脏读、幻读与不可重复读
本文深度解析MySQL事务隔离级别,从理论到实战全面讲解脏读、幻读与不可重复读问题。通过实际案例演示不同隔离级别(读未提交、读已提交、可重复读)的应用场景与潜在风险,并提供金融、电商等行业的隔离级别选型指南,帮助开发者合理平衡数据一致性与系统性能。
深入解析STM32 ADC的多通道转换与中断处理机制
本文深入解析STM32 ADC的多通道转换与中断处理机制,详细介绍了电压输入范围、通道选择、转换顺序配置等核心原理,并分享了中断处理、DMA优化及常见问题排查的实战技巧。通过具体代码示例和优化方案,帮助开发者高效实现多通道ADC采集,提升嵌入式系统性能。
【折腾系列—All In One主机】4、 PVE虚拟机网卡直通实战与效能解析
本文详细介绍了在PVE虚拟机中实现网卡直通的实战步骤与效能优化技巧。通过对比桥接模式与直通模式的性能差异,展示了直通技术在提升网络吞吐量和降低CPU占用率方面的显著优势。文章涵盖硬件兼容性检查、BIOS设置、PVE系统配置以及iKuai软路由的直通优化,为All In One主机用户提供全面的解决方案。
Win10隐私保护:3分钟搞定文件夹和照片的‘最近浏览’记录(附注册表清理)
本文详细介绍了Windows 10中如何彻底清除文件和照片的'最近浏览'记录,保护用户隐私。从简单的图形界面设置到高级的注册表编辑,再到一键清理脚本的创建,提供了多种实用方法。特别适合共享电脑用户或注重隐私保护的技术人员,帮助消除文件资源管理器中的数字足迹。