【项目扩展实战|第8篇】Spring Boot + MyBatis 分页条件查询接口优化
前言前面我们已经写过商品详情缓存、文件上传等功能。这一篇继续回到后台管理系统里非常高频的一个接口分页条件查询。只要是后台列表页面基本都离不开分页查询。比如商品列表分页查询用户列表分页查询订单列表分页查询操作日志分页查询文件记录分页查询如果只是查全部数据接口很简单但真实后台一般都需要支持条件筛选例如商品名称、状态、价格区间、创建时间范围等。这一篇我们就以商品列表为例实现一个完整的分页条件查询接口并顺带优化参数对象、分页返回结构和 MyBatis 动态 SQL。一、分页条件查询的整体需求我们要实现的接口是GET /admin/product/page支持这些查询条件当前页码page每页条数pageSize商品名称关键字keyword商品状态status最低价格minPrice最高价格maxPrice创建开始时间beginTime创建结束时间endTime返回结果大概是{code:200,message:操作成功,data:{total:100,rows:[]}}这里使用total rows的结构前端分页组件比较容易使用。二、准备分页返回对象先定义一个通用分页返回类。packagecom.example.common;importlombok.AllArgsConstructor;importlombok.Data;importlombok.NoArgsConstructor;importjava.util.List;DataNoArgsConstructorAllArgsConstructorpublicclassPageResultT{privateLongtotal;privateListTrows;}文字说明PageResult是一个通用类不只商品列表能用用户列表、订单列表、日志列表也都能用。其中total表示总记录数rows表示当前页数据前端拿到total后就可以计算总页数拿到rows后就可以渲染表格。三、添加 PageHelper 依赖分页可以自己写limit也可以使用 PageHelper。这里使用 PageHelper因为它和 MyBatis 配合比较方便。dependencygroupIdcom.github.pagehelper/groupIdartifactIdpagehelper-spring-boot-starter/artifactIdversion1.4.7/version/dependency文字说明PageHelper 的核心用法是PageHelper.startPage(page,pageSize);ListProductVOlistproductMapper.selectPage(queryDTO);PageProductVOpageResult(PageProductVO)list;它会自动帮我们在 SQL 后面加分页语句并统计总记录数。四、准备查询参数对象不要把所有查询参数都散落在 Controller 方法参数里更推荐封装成一个 DTO。packagecom.example.dto;importlombok.Data;importorg.springframework.format.annotation.DateTimeFormat;importjava.math.BigDecimal;importjava.time.LocalDateTime;DatapublicclassProductPageQueryDTO{privateIntegerpage1;privateIntegerpageSize10;privateStringkeyword;privateIntegerstatus;privateBigDecimalminPrice;privateBigDecimalmaxPrice;DateTimeFormat(patternyyyy-MM-dd HH:mm:ss)privateLocalDateTimebeginTime;DateTimeFormat(patternyyyy-MM-dd HH:mm:ss)privateLocalDateTimeendTime;}文字说明这个 DTO 封装了所有分页查询条件。这里给page和pageSize设置了默认值privateIntegerpage1;privateIntegerpageSize10;这样前端如果没传分页参数也不会直接报错。时间参数加了DateTimeFormat(patternyyyy-MM-dd HH:mm:ss)这样 Spring MVC 才能把字符串时间转换成LocalDateTime。例如前端传beginTime2026-07-01 00:00:00后端就能正常接收。五、准备商品列表 VO分页列表通常不需要返回商品详情里的全部字段所以单独定义一个列表 VO。packagecom.example.vo;importlombok.Data;importjava.math.BigDecimal;importjava.time.LocalDateTime;DatapublicclassProductPageVO{privateLongid;privateStringname;privateBigDecimalprice;privateIntegerstock;privateStringcoverUrl;privateIntegerstatus;privateLocalDateTimecreateTime;}文字说明列表页一般展示核心字段即可比如商品 ID商品名称价格库存封面状态创建时间如果列表页字段太多会导致接口响应变大也会让前端表格变得很复杂。六、Controller 层实现packagecom.example.controller;importcom.example.common.PageResult;importcom.example.common.Result;importcom.example.dto.ProductPageQueryDTO;importcom.example.service.ProductService;importcom.example.vo.ProductPageVO;importlombok.RequiredArgsConstructor;importorg.springframework.web.bind.annotation.*;RestControllerRequestMapping(/admin/product)RequiredArgsConstructorpublicclassAdminProductController{privatefinalProductServiceproductService;GetMapping(/page)publicResultPageResultProductPageVOpage(ProductPageQueryDTOqueryDTO){PageResultProductPageVOpageResultproductService.page(queryDTO);returnResult.success(pageResult);}}文字说明这里没有使用RequestBody。因为分页条件查询一般是 GET 请求参数通常放在 URL 后面例如GET /admin/product/page?page1pageSize10keyword手机status1Spring MVC 会自动把这些请求参数封装到ProductPageQueryDTO中。Controller 层依旧保持简单只负责接收参数、调用 Service、返回结果。七、Service 层实现分页逻辑1. Service 接口packagecom.example.service;importcom.example.common.PageResult;importcom.example.dto.ProductPageQueryDTO;importcom.example.vo.ProductPageVO;publicinterfaceProductService{PageResultProductPageVOpage(ProductPageQueryDTOqueryDTO);}2. Service 实现类packagecom.example.service.impl;importcom.example.common.PageResult;importcom.example.common.ResultCode;importcom.example.dto.ProductPageQueryDTO;importcom.example.exception.BusinessException;importcom.example.mapper.ProductMapper;importcom.example.service.ProductService;importcom.example.vo.ProductPageVO;importcom.github.pagehelper.Page;importcom.github.pagehelper.PageHelper;importlombok.RequiredArgsConstructor;importorg.springframework.stereotype.Service;importjava.util.List;ServiceRequiredArgsConstructorpublicclassProductServiceImplimplementsProductService{privatestaticfinalintMAX_PAGE_SIZE100;privatefinalProductMapperproductMapper;OverridepublicPageResultProductPageVOpage(ProductPageQueryDTOqueryDTO){checkPageParams(queryDTO);PageHelper.startPage(queryDTO.getPage(),queryDTO.getPageSize());ListProductPageVOlistproductMapper.selectPage(queryDTO);PageProductPageVOpage(PageProductPageVO)list;returnnewPageResult(page.getTotal(),page.getResult());}privatevoidcheckPageParams(ProductPageQueryDTOqueryDTO){if(queryDTOnull){thrownewBusinessException(ResultCode.PARAM_ERROR);}if(queryDTO.getPage()null||queryDTO.getPage()0){queryDTO.setPage(1);}if(queryDTO.getPageSize()null||queryDTO.getPageSize()0){queryDTO.setPageSize(10);}if(queryDTO.getPageSize()MAX_PAGE_SIZE){queryDTO.setPageSize(MAX_PAGE_SIZE);}if(queryDTO.getMinPrice()!nullqueryDTO.getMaxPrice()!nullqueryDTO.getMinPrice().compareTo(queryDTO.getMaxPrice())0){thrownewBusinessException(ResultCode.PARAM_ERROR.getCode(),最低价格不能大于最高价格);}if(queryDTO.getBeginTime()!nullqueryDTO.getEndTime()!nullqueryDTO.getBeginTime().isAfter(queryDTO.getEndTime())){thrownewBusinessException(ResultCode.PARAM_ERROR.getCode(),开始时间不能晚于结束时间);}}}文字说明Service 层主要做三件事。第一校验分页参数checkPageParams(queryDTO);防止前端传入异常参数比如page-1、pageSize99999。第二开启分页PageHelper.startPage(queryDTO.getPage(),queryDTO.getPageSize());注意PageHelper.startPage必须紧挨着下一次 MyBatis 查询执行。第三封装分页结果returnnewPageResult(page.getTotal(),page.getResult());这样前端拿到的就是统一分页格式。八、Mapper 层实现1. Mapper 接口packagecom.example.mapper;importcom.example.dto.ProductPageQueryDTO;importcom.example.vo.ProductPageVO;importorg.apache.ibatis.annotations.Mapper;importjava.util.List;MapperpublicinterfaceProductMapper{ListProductPageVOselectPage(ProductPageQueryDTOqueryDTO);}2. Mapper XML 动态 SQL?xml version1.0 encodingUTF-8 ?!DOCTYPEmapperPUBLIC-//mybatis.org//DTD Mapper 3.0//ENhttps://mybatis.org/dtd/mybatis-3-mapper.dtdmappernamespacecom.example.mapper.ProductMapperselectidselectPageresultTypecom.example.vo.ProductPageVOselect id, name, price, stock, cover_url, status, create_time from productwhereiftestkeyword ! null and keyword ! and name like concat(%, #{keyword}, %)/ififteststatus ! nulland status #{status}/ififtestminPrice ! nulland pricegt; #{minPrice}/ififtestmaxPrice ! nulland pricelt; #{maxPrice}/ififtestbeginTime ! nulland create_timegt; #{beginTime}/ififtestendTime ! nulland create_timelt; #{endTime}/if/whereorder by create_time desc, id desc/select/mapper文字说明这里使用了 MyBatis 动态 SQL。where标签会自动处理where和多余的and。比如只传了status时最终 SQL 类似select...fromproductwherestatus?orderbycreate_timedesc,iddesc如果什么条件都没传就不会拼接where最终查询全部商品分页。关键字查询使用name like concat(%, #{keyword}, %)价格和时间范围分别使用price #{minPrice} price #{maxPrice} create_time #{beginTime} create_time #{endTime}这就是后台列表条件查询里非常常见的写法。九、分页查询接口测试示例1. 查询第一页GET /admin/product/page?page1pageSize10表示查询第一页每页 10 条。2. 根据名称模糊查询GET /admin/product/page?page1pageSize10keyword手机表示查询名称中包含“手机”的商品。3. 根据状态查询GET /admin/product/page?page1pageSize10status1表示查询上架商品。4. 根据价格区间查询GET /admin/product/page?page1pageSize10minPrice100maxPrice500表示查询价格在 100 到 500 之间的商品。5. 根据时间范围查询GET /admin/product/page?page1pageSize10beginTime2026-07-01 00:00:00endTime2026-07-05 23:59:59表示查询指定时间范围内创建的商品。十、为什么要限制 pageSize如果不限制pageSize前端或者恶意请求可能传pageSize100000这样一次查询就可能查出大量数据导致数据库压力变大接口响应变慢内存占用升高前端渲染卡顿所以 Service 里做了限制if(queryDTO.getPageSize()MAX_PAGE_SIZE){queryDTO.setPageSize(MAX_PAGE_SIZE);}后台管理系统中一般每页 10、20、50 条已经够用最大限制到 100 比较合理。十一、涉及知识点1. PageHelperPageHelper 是 MyBatis 常用分页插件。它可以自动帮我们拼接分页 SQL并计算总数。使用时要注意PageHelper.startPage(page,pageSize);必须放在 Mapper 查询之前。2. MyBatis 动态 SQL动态 SQL 适合处理可选查询条件。常用标签有whereifsetforeach本篇主要使用where和if。3. DTO 和 VO 分层ProductPageQueryDTO用来接收查询参数。ProductPageVO用来返回列表数据。这样比直接用实体类更清晰也更适合前后端接口设计。4. 模糊查询模糊查询常见写法是namelikeconcat(%,#{keyword}, %)注意不要直接拼接字符串避免 SQL 注入风险。5. 时间范围查询时间范围查询是后台列表中非常常见的需求。前端传字符串时间后端通过DateTimeFormat转成LocalDateTimeMapper 再用于 SQL 查询。十二、常见问题1. PageHelper 为什么没有生效常见原因有没有引入 PageHelper starterPageHelper.startPage没有紧挨着 Mapper 查询Mapper 查询之前执行了其他查询返回类型转换方式不正确正确顺序是PageHelper.startPage(page,pageSize);ListProductPageVOlistproductMapper.selectPage(queryDTO);PageProductPageVOpage(PageProductPageVO)list;2. GET 请求能不能接收对象可以。只要参数名和 DTO 字段名一致Spring MVC 会自动封装。例如?page1pageSize10keyword手机可以封装到ProductPageQueryDTO3. 查询条件为空时会不会 SQL 报错不会。因为where会自动处理条件。如果所有if都不满足就不会生成where。4. 排序字段能不能让前端传可以但要谨慎。不能直接把前端传入的排序字段拼进 SQL否则有 SQL 注入风险。更安全的做法是后端定义白名单比如只允许按create_time、price、stock排序。十三、实际开发建议1. 分页参数一定要兜底不要完全相信前端传参。后端要处理页码为空页码小于 1每页条数为空每页条数过大2. 列表接口不要返回过多字段列表页只返回展示需要的字段。详情页再返回完整信息。这样接口更轻前端也更好处理。3. 模糊查询字段要考虑索引like %关键字%在数据量大时可能导致索引效果不好。如果商品量很大可以考虑只支持前缀匹配增加搜索字段使用全文索引接入 Elasticsearch4. 后台查询要注意组合条件后台列表的核心不只是分页而是多个条件组合查询。所以动态 SQL 是这一类接口必须掌握的内容。十四、总结这一篇我们完成了 Spring Boot MyBatis 分页条件查询接口。整个流程包括定义分页返回对象PageResult定义查询参数 DTO定义列表返回 VOController 接收 GET 查询参数Service 校验分页参数并调用 PageHelperMapper 使用动态 SQL 组合查询条件返回统一分页结构给前端相比基础 CRUD这一篇更接近后台管理系统真实列表页的写法。分页条件查询是非常高频的能力不管是商品、用户、订单、日志基本都能套用这一套思路。学会这篇之后后面写后台列表接口会顺很多。

相关新闻

这个 App 明天就要上线了

这个 App 明天就要上线了

「审核已通过。」林川坐在工位上,看着 App Store Connect 里那行绿色的小字。 周围是下午四点钟寻常的办公室噪音——键盘声、空调声、隔壁运营在打电话。 他截图,发到群里。 群里炸了。「川哥牛逼!」 「终于!!&#x…

2026/7/23 22:31:25 阅读更多 →
智慧水利机器视觉应用:AI视觉水位监测完整技术剖析

智慧水利机器视觉应用:AI视觉水位监测完整技术剖析

摘要在智慧水利数字化转型全面推进的行业背景下,传统接触式水位监测设备存在易淤堵、运维成本高、极端天气观测安全风险大、无实景佐证等短板,已难以满足河道、水库、灌区、城市内涝等多场景精细化水文管控需求。本文围绕AI视觉水位监测这套非接触式全自…

2026/7/23 22:30:25 阅读更多 →
PVC鞋材稳定剂应用与发展

PVC鞋材稳定剂应用与发展

PVC材质凭借柔韧耐磨、性价比突出的优势,广泛应用于各类鞋材生产加工领域,但PVC树脂在高温塑化、注塑发泡等加工工序中,容易出现分解变色、材质脆化、物料析出等问题,直接影响鞋材成品质量与使用寿命,而PVC鞋材稳定剂(…

2026/7/23 22:30:25 阅读更多 →

最新新闻

移动车载工业路由器跨国大规模配置更新架构:基于事务回滚与双向校验的深度实践代码解析

移动车载工业路由器跨国大规模配置更新架构:基于事务回滚与双向校验的深度实践代码解析

摘要:随着国内特种车辆与旅居车的大规模出口,网络设备在海外的连通性维护成为电气架构师必须面对的技术挑战。当车辆远赴重洋,海外本地通信运营商的基站频段变更、接入点名称规范调整,随时可能导致车载数字座舱大面积断网。如果依…

2026/7/23 22:39:28 阅读更多 →
新能源储能与充电设施出海通信架构实战:全球射频自适应与高可用拨号守护机制

新能源储能与充电设施出海通信架构实战:全球射频自适应与高可用拨号守护机制

摘要:随着新能源储能系统与快充基础设施的大规模出海,中国制造的设备正全面接入海外当地的复杂通信网络。然而,跨国部署中频段碎片化、海外运营商通信制式高度非标准化以及野外弱网环境下的链路僵死,构成了出海设备交付后的重大运…

2026/7/23 22:39:28 阅读更多 →
打破“无坐标、慢响应”魔咒:AI Agent驱动的毫秒级态势推演与决策闭环

打破“无坐标、慢响应”魔咒:AI Agent驱动的毫秒级态势推演与决策闭环

一、行业共性技术桎梏传统安防系统缺失像素原生三维坐标体系。二维监控画面无统一空间基准。跨摄像机跟踪目标坐标漂移轨迹断裂。静态数字孪生模型更新延迟分钟级。态势分析依赖人工回看事后处置响应滞后。有源定位硬件部署周期长存在电磁泄密风险。多路视频融合时序不同步数据…

2026/7/23 22:39:28 阅读更多 →
Sheared LLaMA: Accelerating Language Model Pre-training via Structured Pruning 解读

Sheared LLaMA: Accelerating Language Model Pre-training via Structured Pruning 解读

一、论文基本信息 论文题目:Sheared LLaMA: Accelerating Language Model Pre-training via Structured Pruning 方法名称:LLM-Shearing 作者:Mengzhou Xia、Tianyu Gao、Zhiyuan Zeng、Danqi Chen 发表:ICLR 2024&#xff0c…

2026/7/23 22:39:28 阅读更多 →
像素即坐标:打通跨摄像机追踪断层,重构全域空间连续建模底座

像素即坐标:打通跨摄像机追踪断层,重构全域空间连续建模底座

像素即坐标:打通跨摄像机追踪断层,重构全域空间连续建模底座一、行业技术固有断层痛点传统监控体系像素仅承载画面可视化信息,无空间坐标映射逻辑。单摄像机视野边界割裂,跨摄像机追踪目标ID漂移轨迹断裂。静态数字孪生依赖前置建…

2026/7/23 22:39:28 阅读更多 →
海康摄像头RTSP转换前端浏览器实时播放

海康摄像头RTSP转换前端浏览器实时播放

背景: 公司运维平台需求:把机房监控接入,可在运维平台web实时查看。 解决方案:前端无法播放RTSP数据,问了deepseek和查看别人的建议,最后决定采用以下方案: 基于 ffmpeg 的 Node 后端推流方案 …

2026/7/23 22:38:27 阅读更多 →

日新闻

从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

更多请点击: https://intelliparadigm.com 第一章:从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表) 当AI副业主理人不再仅满足于单次服务交付,而是主动构建可复用、可裂变、可…

2026/7/23 0:00:25 阅读更多 →
AI写作开头钩子设计:为什么你的AI文案完读率不足18%?——基于2,346篇A/B测试报告的归因分析

AI写作开头钩子设计:为什么你的AI文案完读率不足18%?——基于2,346篇A/B测试报告的归因分析

更多请点击: https://codechina.net 第一章:AI写作开头钩子设计:为什么你的AI文案完读率不足18%?——基于2,346篇A/B测试报告的归因分析 在对2,346篇跨行业AI生成文案的A/B测试数据进行聚类分析后,我们发现&#xff1…

2026/7/23 0:01:26 阅读更多 →
Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具 【免费下载链接】chitchatter Secure peer-to-peer chat that is serverless, decentralized, and ephemeral 项目地址: https://gitcode.com/gh_mirrors/ch/chitchatter Chitchatter是一款革命性的安…

2026/7/23 0:01:26 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/22 8:58:19 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/22 19:43:43 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/23 17:49:47 阅读更多 →

月新闻