Spring WebFlux 中高效实现非规范化数据的流式分组与 DTO 转换
发布时间 - 2025-12-26 00:00:00 点击率:次在 spring webflux 响应式编程中,针对数据库返回的重复用户记录(因多部门导致的笛卡尔展开),可通过 `groupby` + `collectlist` 非阻塞地完成按用户 id 分组、聚合部门信息并构建嵌套 dto 的全流程。
在使用 Spring WebFlux 访问 PostgreSQL 等关系型数据库时,若实体表为非规范化设计(例如一个用户对应多个部门,以多行形式冗余存储),findAllUsersByIds() 会返回多个 User 实例(如 ID=1 的用户出现 3 次,分别关联不同 department)。此时直接 .map(mapper::mapUserDTO) 会导致每个记录独立转成一个 UserDTO,无法满足「一个用户仅返回一个 DTO,且其 departmentDTO 字段为该用户全部部门列表」的业务需求。
关键在于:必须在响应式流中完成去重分组 + 列表聚合 + 嵌套映射,全程不调用任何阻塞操作(如 block()、toFuture().get())。推荐方案如下:
FluxuserDTOS = userRepo.findAllUsersByIds() .groupBy(User::getId) // 按用户 ID 分组,返回 Flux > .flatMap(group -> group.collectList() // 将每个分组内的 User 收集为 List (非阻塞) .map(users -> { User first = users.get(0); UserDTO dto = new UserDTO(); dto.setId(first.getId()); dto.setName(first.getName()); dto.setDepartmentDTO( users.stream() // 此处 stream 是纯内存操作,安全 .map(user -> { DepartmentDTO deptDto = new DepartmentDTO(); deptDto.setName(user.getDepartment()); deptDto.setArea(user.getDepartmentArea()); return deptDto; }) .toList() ); return dto; }) );
✅ 优势说明:
- groupBy 是响应式原生操作,底层基于 ConcurrentHashMap 和背压感知的分组缓冲,无线程阻塞;
- collectList() 是 Mono
- > 转换,适用于已知有限分组规模的场景(如单个用户部门数通常
- stream().map(...).toList() 发生在 map 内部,属于 CPU-bound 纯内存计算,不影响响应式链路的异步性;
- 整体仍保持 Flux
输出,可无缝接入 WebFlux 的 @GetMapping 返回值或后续 filter/flatMap 操作。
⚠️ 注意事项:
- 若存在大量用户(如百万级)且部分用户部门数极高(如上千),collectList() 可能引发内存压力,此时建议配合 .limitRate(n) 或改用 reduce 进行增量构建;
- 确保 User::getId 返回值稳定(不可为 null),否则 groupBy 会抛出 NullPointerException;
- 如需保持原始查询顺序(如按 ID 升序),groupBy 本身不保证分组间顺序,但各分组内元素顺序与源 Flux 一致;若需全局有序,应在 groupBy 前使用 sort(Comparator.comparing(Us
er::getId))(注意:sort 会缓冲全部数据,慎用于大数据量)。
通过该模式,你既满足了 REST API 对扁平化输入、嵌套化输出的 DTO 设计规范,又完全遵循了 WebFlux 的非阻塞、背压友好原则,是响应式数据聚合的经典实践。
# 大数据
# app
# stream
# rest api
# 响应式编程
# red
# spring
# NULL
# sort
# Filter
# 线程
# map
# 异步
# postgresql
# 数据库
# 多个
# 笛卡尔
# 返回值
# 升序
# 适用于
# 应在
# 极高
# 可通过
# 如需
# 可为
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
HTML5段落标签p和br怎么选_文本排版常用标签对比【解答】
QQ浏览器网页版登录入口 个人中心在线进入
Laravel如何使用集合(Collections)进行数据处理_Laravel Collection常用方法与技巧
如何为不同团队 ID 动态生成多个非值班状态按钮
JS弹性运动实现方法分析
如何自定义建站之星模板颜色并下载新样式?
使用spring连接及操作mongodb3.0实例
jQuery中的100个技巧汇总
,交易猫的商品怎么发布到网站上去?
详解Nginx + Tomcat 反向代理 负载均衡 集群 部署指南
Laravel路由Route怎么设置_Laravel基础路由定义与参数传递规则【详解】
详解Oracle修改字段类型方法总结
Laravel怎么配置不同环境的数据库_Laravel本地测试与生产环境动态切换【方法】
Laravel如何与Inertia.js和Vue/React构建现代单页应用
活动邀请函制作网站有哪些,活动邀请函文案?
购物网站制作费用多少,开办网上购物网站,需要办理哪些手续?
JavaScript如何实现类型判断_typeof和instanceof有什么区别
如何登录建站主机?访问步骤全解析
JavaScript 输出显示内容(document.write、alert、innerHTML、console.log)
桂林网站制作公司有哪些,桂林马拉松怎么报名?
如何在Windows环境下新建FTP站点并设置权限?
佛山网站制作系统,佛山企业变更地址网上办理步骤?
Java解压缩zip - 解压缩多个文件或文件夹实例
如何快速查询网址的建站时间与历史轨迹?
百度输入法全感官ai怎么关 百度输入法全感官皮肤关闭
利用python获取某年中每个月的第一天和最后一天
企业在线网站设计制作流程,想建设一个属于自己的企业网站,该如何去做?
JS去除重复并统计数量的实现方法
简单实现Android文件上传
laravel怎么通过契约(Contracts)编程_laravel契约(Contracts)编程方法
Python结构化数据采集_字段抽取解析【教程】
如何注册花生壳免费域名并搭建个人网站?
Python文件操作最佳实践_稳定性说明【指导】
如何在VPS电脑上快速搭建网站?
如何快速上传自定义模板至建站之星?
如何挑选最适合建站的高性能VPS主机?
node.js报错:Cannot find module 'ejs'的解决办法
Laravel怎么使用Markdown渲染文档_Laravel将Markdown内容转HTML页面展示【实战】
Laravel怎么做缓存_Laravel Cache系统提升应用速度的策略与技巧
Laravel如何生成PDF或Excel文件_Laravel文档导出工具与使用教程
百度浏览器网页无法复制文字怎么办 百度浏览器复制修复
奇安信“盘古石”团队突破 iOS 26.1 提权
C++时间戳转换成日期时间的步骤和示例代码
SQL查询语句优化的实用方法总结
谷歌Google入口永久地址_Google搜索引擎官网首页永久入口
音乐网站服务器如何优化API响应速度?
Laravel怎么发送邮件_Laravel Mail类SMTP配置教程
Laravel如何实现多级无限分类_Laravel递归模型关联与树状数据输出【方法】
JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)
bootstrap日历插件datetimepicker使用方法


er::getId))(注意:sort 会缓冲全部数据,慎用于大数据量)。