MyBatis 分离查询优化策略
传统的JOIN查询容易导致致的数据膨胀和分页问题对于该问题的解决方案就是将主表查询与关联表查询分离通过 Stream API 高效组装数据。方案对比方案 A传统 JOIN 查询┌─────────────────────────────────────────────────────────────┐ │ SQL: SELECT * FROM orders o │ │ LEFT JOIN order_detail od ON od.order_id o.id │ │ LIMIT 0, 10 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 数据库返回: │ │ ┌──────┬──────┬─────────┬──────────┬──────────┬──────────┐ │ │ │ 订单1 │ 详情1 │ (重复) │ (重复) │ (重复) │ (重复) │ │ │ │ 订单1 │ 详情2 │ (重复) │ (重复) │ (重复) │ (重复) │ │ │ │ 订单1 │ 详情3 │ (重复) │ (重复) │ (重复) │ (重复) │ │ │ │ 订单2 │ 详情1 │ (重复) │ (重复) │ (重复) │ (重复) │ │ │ │ ... │ ... │ │ │ │ │ │ │ └──────┴──────┴─────────┴──────────┴──────────┴──────────┘ │ │ │ │ 问题: │ │ ❌ 返回 30 行3个订单×每个10个详情 │ │ ❌ 但实际只有 3 个独立订单 │ │ ❌ PageHelper 错误地认为有 30 条记录 │ │ ❌ 分页结果不准确 │ │ │ └─────────────────────────────────────────────────────────────┘方案 B当前分离查询方案┌─────────────────────────────────────────────────────────────┐ │ Step 1: 查询主表分页 │ │ │ │ SQL: SELECT * FROM orders LIMIT 0, 10 │ │ │ │ 返回: [订单1, 订单2, 订单3] ← 精确的 3 条✅ │ │ │ ├─────────────────────────────────────────────────────────────┤ │ Step 2: 批量提取ID │ │ │ │ orderIds [1, 2, 3] │ │ │ ├─────────────────────────────────────────────────────────────┤ │ Step 3: 批量查询详情 │ │ │ │ SQL: SELECT * FROM order_detail │ │ WHERE order_id IN (1, 2, 3) │ │ │ │ 返回: [详情1-1, 详情1-2, ..., 详情3-10] ← 所有详情 │ │ │ ├─────────────────────────────────────────────────────────────┤ │ Step 4 5: 内存中组装 │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 订单1 │ │ │ │ ├─ orderDetailList: [详情1-1, 详情1-2, ...] │ │ │ │ └─ orderDishes: 宫保鸡丁、麻婆豆腐、... │ │ │ ├─────────────────────────────────────────────────────┤ │ │ │ 订单2 │ │ │ │ ├─ orderDetailList: [详情2-1, 详情2-2, ...] │ │ │ │ └─ orderDishes: 红烧肉、糖醋里脊、... │ │ │ ├─────────────────────────────────────────────────────┤ │ │ │ 订单3 │ │ │ │ ├─ orderDetailList: [详情3-1, ...] │ │ │ │ └─ orderDishes: 鱼香肉丝、... │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ ✅ 分页准确 │ │ ✅ 数据完整 │ │ ✅ 性能优秀 │ │ │ └─────────────────────────────────────────────────────────────┘以上是两种方案的思路对比对于分离查询优化策略有四大优势体现出来。四大优势优势1解决分页数据膨胀问题最关键问题场景-- 传统 JOIN 查询 SELECT o.*, od.* FROM orders o LEFT JOIN order_detail od ON od.order_id o.id WHERE ... LIMIT 0, 10; -- 希望获取前 10 个订单实际情况订单数平均详情数实际返回行数预期行数10550 行10 行1010100 行10 行1020200 行10 行后果// PageHelper 会错误地认为 page.getTotal() 200 // ❌ 错误应该是 10 page.getResult().size() 200 // ❌ 应该是 10 个订单对象 // 前端分页组件会显示错误的总页数 // 用户点击第 2 页时可能看到重复数据或遗漏数据解决方案效果// 当前方案精确的分页 try (PageOrderVO page PageHelper.startPage(1, 10)) { ListOrderVO orders orderMapper.queryOrders(dto); // 只查主表 // page.getTotal() 真实的订单总数如 156 // orders.size() 精确的 10 个订单对象 ✅ }优势2减少网络传输和内存占用数据量对比假设查询 20 个订单每个订单平均 8 个详情项传统 JOIN 方案传输数据量: 20 个订单 × (8 个详情 20 个订单字段) 160 行完整数据 每行包含: - 订单字段: ~500 字节 - 详情字段: ~200 字节 总计: 160 × 700 字节 112,000 字节 ≈ 109 KB分离查询方案Step 1 - 主表查询: 20 行订单数据 20 × 500 字节 10,000 字节 ≈ 9.8 KB Step 2 - 详情查询: 160 行详情数据 160 × 200 字节 32,000 字节 ≈ 31 KB 总计传输: 9.8 31 40.8 KB ≈ 40 KB节省比例节省空间 (109 - 40) / 109 63.3% ↓ 网络传输减少 63% 内存占用减少 63%优势3避免数据冗余和重复反序列化传统 JOIN 的数据冗余示例[ { id: 1, number: 20240701001, userId: 5, status: 3, amount: 158.00, orderTime: 2024-07-01 12:00:00, // ... 其他 15 个字段全部重复 ... detailId: 101, dishName: 宫保鸡丁, detailAmount: 38.00 }, { id: 1, // ⚠️ 重复 number: 20240701001, // ⚠️ 重复 userId: 5, // ⚠️ 重复 // ... 所有订单字段都重复 ... detailId: 102, dishName: 麻婆豆腐, detailAmount: 28.00 }, // 同一个订单的 20 个字段 × 8 个详情 160 个字段的冗余 ]分离查询的数据结构{ id: 1, number: 20240701001, userId: 5, status: 3, amount: 158.00, // ... 订单字段只出现一次 ✅ orderDetailList: [ {id: 101, dishName: 宫保鸡丁, amount: 38.00}, {id: 102, dishName: 麻婆豆腐, amount: 28.00}, // ... 详情数据独立存储 ], orderDishes: 宫保鸡丁、麻婆豆腐、... }优势4灵活的数据组装能力Stream API 的强大之处需求 1获取完整的详情对象列表MapLong, ListOrderDetail detailMap details.stream() .collect(Collectors.groupingBy(OrderDetail::getOrderId)); // 用途订单详情弹窗展示所有信息 order.setOrderDetailList(detailMap.get(orderId));输出示例orderDetailList: [ { id: 101, name: 宫保鸡丁, dishId: 12, number: 1, amount: 38.00, image: /images/dish/12.jpg }, { id: 102, name: 麻婆豆腐, dishId: 15, number: 2, amount: 56.00, image: /images/dish/15.jpg } ]需求 2提取菜品名称用于列表展示MapLong, ListString orderDishesMap details.stream() .collect(Collectors.groupingBy( OrderDetail::getOrderId, Collectors.mapping(OrderDetail::getName, Collectors.toList()) )); // 用途订单列表快速预览 String dishes String.join(、, orderDishesMap.get(orderId)); order.setOrderDishes(dishes);输出示例orderDishes: 宫保鸡丁、麻婆豆腐×2、米饭×3需求 3还可以轻松扩展其他聚合操作// 统计每个订单的菜品数量 MapLong, Long dishCountMap details.stream() .collect(Collectors.groupingBy( OrderDetail::getOrderId, Collectors.counting() )); // 计算每个订单的总金额从详情角度验证 MapLong, BigDecimal detailAmountMap details.stream() .collect(Collectors.groupingBy( OrderDetail::getOrderId, Collectors.reducing(BigDecimal.ZERO, OrderDetail::getAmount, BigDecimal::add) )); // 提取所有菜品ID用于缓存预热 SetLong dishIds details.stream() .map(OrderDetail::getDishId) .collect(Collectors.toSet());这种灵活性在 JOIN 方案中很难实现因此正如开头所说将主表查询与关联表查询分离后利用Stream API 即可高效组装数据。

相关新闻

docker-selenium 4.48.0 发布实录:从 Firefox 142 看浏览器镜像的多标签生成与发布机制

docker-selenium 4.48.0 发布实录:从 Firefox 142 看浏览器镜像的多标签生成与发布机制

测试后端云原生容器编排可观测性 【免费下载链接】docker-selenium Provides a simple way to run Selenium Grid with Chrome, Firefox, and Edge using Container Platform, making it easier to perform browser automation at scale 项目地址: https://gitcode.…

2026/10/4 1:45:31 阅读更多 →
《Go Web 编程》第 12.1 节实战:用 seelog 为 Go Web 应用打造多输出、热更新、带邮件告警的日志系统

《Go Web 编程》第 12.1 节实战:用 seelog 为 Go Web 应用打造多输出、热更新、带邮件告警的日志系统

文档教程 【免费下载链接】build-web-application-with-golang A golang ebook intro how to build a web with golang 项目地址: https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang 点击查看 免费下载 本文基于开源仓库 build-web-applicatio…

2026/10/4 1:45:31 阅读更多 →
Symfony Routing 组件实战:URL 匹配与生成的核心机制全解析

Symfony Routing 组件实战:URL 匹配与生成的核心机制全解析

后端Web框架 【免费下载链接】symfony The Symfony PHP framework 项目地址: https://gitcode.com/GitHub_Trending/sy/symfony 点击查看 免费下载 本指南围绕 Symfony 官方 Routing 组件的入门文档展开,从一条路由的定义出发,完整讲解 Rout…

2026/10/4 1:45:31 阅读更多 →

最新新闻

Figma Console MCP无障碍审计实战:14项WCAG设计检查+组件记分卡+axe-core 104条代码规则

Figma Console MCP无障碍审计实战:14项WCAG设计检查+组件记分卡+axe-core 104条代码规则

Figma Console MCP无障碍审计实战:14项WCAG设计检查组件记分卡axe-core 104条代码规则 【免费下载链接】figma-console-mcp Your design system as an API. Connect AI to Figma for extraction, creation, and debugging. 项目地址: https://gitcode.com/gh_mirr…

2026/10/4 5:07:40 阅读更多 →
校园社团小程序云开发实战:从选型到上线,扔掉服务器采购单

校园社团小程序云开发实战:从选型到上线,扔掉服务器采购单

简介:这份资源是面向校园社团运营者与小程序开发学习者的完整项目源码,基于腾讯小程序云开发方案实现,无需自备服务器和域名即可部署运行。功能覆盖社团通知、社团简介、社团福利、社团章程、社团招新以及活动报名预约等核心场景,…

2026/10/4 5:07:40 阅读更多 →
改写后意思没变吗?yomiyasu AI 日语精修 diff 工具自动校验机制完整指南

改写后意思没变吗?yomiyasu AI 日语精修 diff 工具自动校验机制完整指南

改写后意思没变吗?yomiyasu AI 日语精修 diff 工具自动校验机制完整指南 【免费下载链接】yomiyasu AI生成の日本語を自然な日本語へ推敲するAgent Skill / Agent Skill for Refining AI-Generated Japanese into Natural Japanese 项目地址: https://gitcode.com…

2026/10/4 5:07:40 阅读更多 →
盐城公考培训哪家好?2026 盐城公考机构实测对比与选择指南

盐城公考培训哪家好?2026 盐城公考机构实测对比与选择指南

写在前面:为什么"盐城公考培训哪家好"没有标准答案每年省考、国考季,盐城考生问得最多的一句话就是:盐城公考培训哪家好?这个问题没有"唯一正确"的答案,因为不同基础的考生、不同备考阶段、不同预…

2026/10/4 5:07:40 阅读更多 →
把五万条失败记录做成错题本,原本放弃的任务通过率飙了三成

把五万条失败记录做成错题本,原本放弃的任务通过率飙了三成

把五万条失败记录做成错题本,原本放弃的任务通过率飙了三成 如果一个刚上岗的实习生把事情搞砸了,聪明的师傅绝不会只甩过去一句冷冰冰的「不合格,重来」,而是会停下来问两句话:你究竟在哪一步拐错了弯?如果…

2026/10/4 5:07:40 阅读更多 →
MATLAB findpeaks 峰值检测全解:从原理到实战参数配置

MATLAB findpeaks 峰值检测全解:从原理到实战参数配置

名字叫 findpeaks 的这个函数,我前后用了快六年,第一次真正把它研究明白,却是被一段满是毛刺的心电信号逼的。导师丢给我一组数据,说“把 R 波全找出来”,我盯着波形上那些高高低低的山头,下意识就想找“高…

2026/10/4 5:06:39 阅读更多 →

日新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 10:36:31 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:35 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:36 阅读更多 →