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

一文读懂 PageQueryUtil:分页查询的优雅打开方式

适用人群:Java 开发者,想了解函数式编程在实际项目中的应用前置知识:了解 Java 8 Lambda 表达式基础


一、先来看一个实际场景

场景:同步 10000 条债券数据到接口平台

传统做法:

Java// 一次性查询所有数据List<BondQuotaQueryBO> allData = bondQuotaQueryService.queryAll(queryBO);// 逐条处理for (BondQuotaQueryBO bo : allData) { syncSingleQuota(bo, tenantId);}

问题:

  • ❌ 10000 条数据一次性加载到内存,可能 OOM(内存溢出)

  • ❌ 如果数据有 10 万条呢?

  • ❌ 查询超时怎么办?

优雅做法:

Java// 分页查询,逐页处理PageQueryUtil.queryAndProcess( pageInfo -> bondQuotaQueryService. queryBondQuotaQueryByPage(queryBO, pageInfo), rows -> { for (BondQuotaQueryBO bo : rows) { syncSingleQuota(bo, tenantId); } }, 100 // 每页 100 条);

好处:

  • ✅ 每次只加载 100 条到内存

  • ✅ 自动处理所有分页

  • ✅ 代码简洁,逻辑清晰


二、PageQueryUtil 的核心设计

1. 方法签名(记住这个公式)

Javapublic static <T> long queryAndProcess( Function<PageInfo, PageResult<T>> queryFunction, // ← 怎么查 Consumer<List<T>> pageHandler, // ← 怎么处理 int pageSize // ← 每页多少条)

通俗理解:

  • 参数 1:你告诉它"怎么查数据"(给个查询函数)

  • 参数 2:你告诉它"查到数据后怎么处理"(给个处理函数)

  • 参数 3:每页查多少条

返回值:一共处理了多少条数据


三、实战演练:完整代码拆解

步骤 1:准备查询条件

JavaBondQuotaQueryBO queryBO = new BondQuotaQueryBO();queryBO.setLockStatus(2); // 已审批queryBO.setNotEndType(1); // 未终止

步骤 2:调用 queryAndProcess

Javalong total = PageQueryUtil.queryAndProcess( // 参数 1:查询函数(怎么查) pageInfo -> bondQuotaQueryService. queryBondQuotaQueryByPage(queryBO, pageInfo), // 参数 2:处理函数(怎么处理) rows -> { for (BondQuotaQueryBO bo : rows) { syncSingleQuota(bo, tenantId); } }, // 参数 3:每页条数 PAGE_SIZE // 100);

步骤 3:理解 Lambda 表达式

参数 1 详解:
JavapageInfo -> bondQuotaQueryService.queryBondQuotaQueryByPage(queryBO, pageInfo)

翻译成人话:

"给我一个分页信息(pageInfo),我返回一页数据给你"

等价于:

Javanew Function<PageInfo, PageResult<BondQuotaQueryBO>>() { @Override public PageResult<BondQuotaQueryBO> apply(PageInfo pageInfo) { return bondQuotaQueryService. queryBondQuotaQueryByPage(queryBO, pageInfo); }}

关键点:

  • pageInfo 是 Lambda 的参数(占位符)

  • queryBO 是从外部捕获的变量(闭包)

  • 这个 Lambda 现在不执行,只是定义了一个"规则"

参数 2 详解:
Javarows -> { for (BondQuotaQueryBO bo : rows) { syncSingleQuota(bo, tenantId); }}

翻译成人话:

"给我一页数据(rows),我逐条处理它们"

等价于:

Javanew Consumer<List<BondQuotaQueryBO>>() { @Override public void accept(List<BondQuotaQueryBO> rows) { for (BondQuotaQueryBO bo : rows) { syncSingleQuota(bo, tenantId); } }}

四、PageQueryUtil 内部是如何工作的?

执行流程图:

PlainTextPageQueryUtil.queryAndProcess()│├─ 【第 1 步】创建第 1 页的分页参数│ PageInfo pageInfo = new PageInfo(1, 100);│├─ 【第 2 步】调用你的查询函数│ PageResult<T> result = queryFunction.apply(pageInfo);│ ↑│ └─ 实际执行:bondQuotaQueryService.queryBondQuotaQueryByPage(queryBO, pageInfo)│├─ 【第 3 步】调用你的处理函数│ pageHandler.accept(result.getRows());│ ↑│ └─ 实际执行:for (BondQuotaQueryBO bo : rows) { syncSingleQuota(bo, tenantId); }│├─ 【第 4 步】获取总条数,计算总页数│ long total = result.getTotal(); // 比如 1000│ int totalPages = 1000 / 100 = 10;│├─ 【第 5 步】循环查询第 2~10 页│ for (int page = 2; page <= 10; page++) {│ PageInfo pageInfo = new PageInfo(page, 100);│ PageResult<T> result = queryFunction.apply(pageInfo); // 查询│ pageHandler.accept(result.getRows()); // 处理│ }│└─ 【第 6 步】返回总条数 return processedCount;

时序图:

PlainText调用方 PageQueryUtil 你的查询函数 你的处理函数 │ │ │ │ │──queryAndProcess──> │ │ │ │ │ │ │ │ │──apply(pageInfo)──> │ │ │ │ │ │ │ │<─PageResult 返回── │ │ │ │ │ │ │ │────────accept(rows) ──────────────────────────> │ │ │ │ │ │ │ (循环第 2~N 页...) │ │ │ │ │ │<─返回总条数───────── │ │ │ │ │ │ │

五、为什么需要 pageInfo ->?

常见疑惑:

"pageInfo 不是在 queryAndProcess 里面定义了吗?为什么 Lambda 还要写一次?"

答案:

Lambda 是在定义"函数",不是在"调用函数"!

类比理解:

场景:你点外卖

传统方式:

Java// 你直接去餐厅吃List<Food> allFood = restaurant.getAllFood();eat(allFood);

外卖方式(PageQueryUtil):

Java// 你告诉外卖平台:FoodDelivery.orderAndEat( // 参数 1:怎么取餐 box -> restaurant.getFoodByBox(box), // 参数 2:怎么吃 foods -> { for (Food food : foods) { eat(food); } }, // 参数 3:每箱装多少 10);

Lambda 的 box -> 就像:

"给我一个餐盒编号(box),我去取对应的菜"

外卖平台会:

  1. 准备第 1 个餐盒 → 调用你的取餐函数 → 送到你家 → 你开吃

  2. 准备第 2 个餐盒 → 调用你的取餐函数 → 送到你家 → 你开吃

  3. ...

关键:餐盒编号是外卖平台准备的,你只需要定义"收到编号后怎么做"!


六、闭包:Lambda 的超能力

问题:

queryBO 没有作为参数传给 queryAndProcess,为什么查询函数里能用?

答案:

Lambda 可以"记住"定义时的上下文!

JavaBondQuotaQueryBO queryBO = new BondQuotaQueryBO(); // ← 外部变量queryBO.setLockStatus(2);PageQueryUtil.queryAndProcess( // Lambda"捕获"了外部的 queryBO pageInfo -> bondQuotaQueryService. queryBondQuotaQueryByPage(queryBO, pageInfo), // ↑ // └─ 外部变量,被 Lambda 捕获 ...);

通俗理解:

Lambda 就像一个" closures(闭包)",把定义时能看到的变量都装进包里,以后随时能用!


七、举一反三:更多使用场景

场景 1:批量发送邮件

JavaEmailQueryBO queryBO = new EmailQueryBO();queryBO.setStatus("PENDING");PageQueryUtil.queryAndProcess( pageInfo -> emailService.queryPendingEmails(queryBO, pageInfo), emails -> { for (Email email : emails) { sendEmail(email); } }, 50);

场景 2:导出 Excel

JavaPageQueryUtil.queryAndProcess( pageInfo -> orderService.queryOrders(queryBO, pageInfo), orders -> { // 逐页写入 Excel excelWriter.write(orders); }, 1000);

场景 3:数据清洗

JavaPageQueryUtil.queryAndProcess( pageInfo -> dataService.queryDirtyData(pageInfo), dataList -> { for (Data data : dataList) { cleanData(data); } }, 200);

八、常见错误避坑

❌ 错误 1:直接调用方法(没有 Lambda)

Java// 错误!pageInfo 未定义PageQueryUtil.queryAndProcess( bondQuotaQueryService.queryBondQuotaQueryByPage (queryBO, pageInfo), ...);

正确写法:

JavaPageQueryUtil.queryAndProcess( pageInfo -> bondQuotaQueryService. queryBondQuotaQueryByPage(queryBO, pageInfo), ...);

❌ 错误 2:忘记写 ->

Java// 错误!这不是 LambdaPageQueryUtil.queryAndProcess( (PageInfo pageInfo) bondQuotaQueryService. queryBondQuotaQueryByPage(queryBO, pageInfo), ...);

正确写法:

JavaPageQueryUtil.queryAndProcess( (PageInfo pageInfo) -> bondQuotaQueryService. queryBondQuotaQueryByPage(queryBO, pageInfo), ...);

❌ 错误 3:类型不匹配

Java// 错误!queryFunction 返回值类型不对PageQueryUtil.queryAndProcess( pageInfo -> bondQuotaQueryService.queryCount (queryBO), // ← 返回 Integer,不是 PageResult ...);

正确写法:

JavaPageQueryUtil.queryAndProcess( pageInfo -> bondQuotaQueryService. queryBondQuotaQueryByPage(queryBO, pageInfo), ...);

九、核心要点总结

1. 方法签名(背下来)

JavaqueryAndProcess( Function<PageInfo, PageResult<T>> queryFunction, // 怎么查 Consumer<List<T>> pageHandler, // 怎么处理 int pageSize // 每页多少条)

2. Lambda 表达式

Java// 查询函数:接收 pageInfo,返回 PageResultpageInfo -> service.query(queryBO, pageInfo)// 处理函数:接收 rows,没有返回值rows -> { for (T item : rows) { process(item); } }

3. 闭包特性

JavaSomeBO queryBO = new SomeBO(); // 外部变量pageInfo -> service.query(queryBO, pageInfo); // Lambda 捕获外部变量

4. 执行流程

PlainText1. 创建第 1 页 PageInfo2. 调用 queryFunction.apply(pageInfo) → 查询3. 调用 pageHandler.accept(rows) → 处理4. 循环第 2~N 页5. 返回总条数

十、课后练习

练习题 1:补全代码

Java// 需求:分页查询用户,每页 50 条,打印每个用户的名字UserQueryBO queryBO = new UserQueryBO();queryBO.setStatus("ACTIVE");PageQueryUtil.queryAndProcess( // 补全参数 1:查询函数 ________________________________, // 补全参数 2:处理函数 ________________________________, // 补全参数 3:每页条数 ____);

参考答案:

JavaPageQueryUtil.queryAndProcess( pageInfo -> userService.queryUsers(queryBO, pageInfo), users -> { for (User user : users) { System.out.println(user.getName()); } }, 50);

练习题 2:判断正误

Java// 这段代码有什么问题?PageQueryUtil.queryAndProcess( pageInfo -> { userService.queryUsers(queryBO, pageInfo); }, users -> System.out.println(users.size()), 100);

答案:查询函数缺少 return 语句!应该改为:

JavapageInfo -> { return userService.queryUsers(queryBO, pageInfo);}

结语

PageQueryUtil.queryAndProcess 是模板方法模式+函数式编程的完美结合:

  • 模板方法模式:PageQueryUtil 定义流程,你填充具体逻辑

  • 函数式编程:用 Lambda 表达式传递行为,代码简洁优雅

掌握这个工具,让你的分页查询代码:

  • ✅ 内存友好

  • ✅ 逻辑清晰

  • ✅ 易于复用

  • ✅ 优雅简洁

记住这个公式:

PlainTextqueryAndProcess( 怎么查(Lambda), 怎么处理(Lambda), 每页多少条)

下次遇到分页查询,试试这个优雅的打开方式吧!🎉

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

相关文章:

  • AI 辅助开发实战:高效完成自动化专业毕业设计的工程化路径
  • 2026年系统门窗选购终极指南:五大厂商深度解析与避坑要点 - 2026年企业推荐榜
  • 2026企业微信哪家服务商更靠谱?综合实力对比参考 - 品牌排行榜
  • EasyAnimateV5图生视频模型:VMware环境搭建与中文提示词实战
  • 新手福音:用快马AI生成三极管工作状态模拟器,轻松掌握三种状态
  • Display Driver Uninstaller(DDU)彻底解决驱动残留问题专业指南
  • 从 Apache SeaTunnel 走向 ASF Member:一位开发者的长期主义样本
  • 前端迷惑行为大赏:JSFuck编码的原理、恶作剧与正经用途
  • 中文大语言模型生态系统的深度解析与技术演进路径
  • OpenClaw+GLM-4.7-Flash:个人健康数据追踪
  • ArcGIS Pro批量合并OSM数据的实用技巧
  • Boss-Key:多场景窗口隐私保护工具的全方位解决方案
  • 如何给虚拟机扩容
  • 2026浙江靠谱的企业微信服务商有哪些?这份推荐值得参考 - 品牌排行榜
  • 如何突破AI音频处理瓶颈?开源工具让音质提升30%的秘密
  • BepInEx终极指南:掌握Unity游戏插件开发的完整教程
  • AI赋能镜像构建:让快马平台智能生成优化后的Dockerfile
  • PyTorch视觉模型库实战指南:如何从400+预训练模型中精准选择最佳方案
  • 珍珠棉生产厂家有哪些?2026年企业合作案例与综合能力参考 - 速递信息
  • 头歌Educoder离散数学实训避坑指南:手把手调试Python集合与自然数系统代码
  • Qwen3-1.7B部署案例分享:中小企业无需专业AI团队,30分钟上线语音转录SaaS服务
  • OpenClaw 的对话记忆压缩频率是多少?是否根据对话重要性动态调整?
  • 如何选择适合自己的工业智能体解决方案?关键指标有哪些?
  • 告别繁琐编程,低代码开发开启企业应用新时代!
  • 从Noise2Noise到Noise2Void:无监督图像去噪技术的演进与实践
  • 3步解决数字内容永久保存难题:文件导出与跨平台备份指南
  • 2026 年国内动态代理 IP 深度实测:五款主流服务商全维度对比
  • 四川音乐喷泉厂合作前看什么?2026年专业度与可持续性成焦点 - 速递信息
  • OpenClaw私有化部署:Qwen3-VL:30B+飞书低成本方案
  • 2026社媒获客公司口碑评价好的推荐参考 - 品牌排行榜