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 】
相关推荐:
如何在沈阳梯子盘古建站优化SEO排名与功能模块?
Laravel如何与Pusher实现实时通信?(WebSocket示例)
Laravel如何配置和使用队列处理异步任务_Laravel队列驱动与任务分发实例
CSS3怎么给轮播图加过渡动画_transition加transform实现【技巧】
高防服务器租用首荐平台,企业级优惠套餐快速部署
标准网站视频模板制作软件,现在有哪个网站的视频编辑素材最齐全的,背景音乐、音效等?
微博html5版本怎么弄发语音微博_语音录制入口及时长限制操作【教程】
C#如何调用原生C++ COM对象详解
如何获取免费开源的自助建站系统源码?
javascript日期怎么处理_如何格式化输出
html文件怎么打开证书错误_https协议的html打开提示不安全【指南】
JS实现鼠标移上去显示图片或微信二维码
简单实现Android文件上传
浅谈javascript alert和confirm的美化
Laravel怎么使用Collection集合方法_Laravel数组操作高级函数pluck与map【手册】
rsync同步时出现rsync: failed to set times on “xxxx”: Operation not permitted
微信小程序 配置文件详细介绍
手机怎么制作网站教程步骤,手机怎么做自己的网页链接?
Laravel如何配置和使用缓存?(Redis代码示例)
php做exe能调用系统命令吗_执行cmd指令实现方式【详解】
Laravel如何创建自定义中间件?(Middleware代码示例)
高端云建站费用究竟需要多少预算?
LinuxShell函数封装方法_脚本复用设计思路【教程】
Laravel如何使用Gate和Policy进行权限控制_Laravel权限判定与策略规则配置
专业型网站制作公司有哪些,我设计专业的,谁给推荐几个设计师兼职类的网站?
智能起名网站制作软件有哪些,制作logo的软件?
微信小程序 canvas开发实例及注意事项
如何用PHP快速搭建高效网站?分步指南
JS经典正则表达式笔试题汇总
JavaScript实现Fly Bird小游戏
Laravel怎么生成二维码图片_Laravel集成Simple-QrCode扩展包与参数设置【实战】
如何自己制作一个网站链接,如何制作一个企业网站,建设网站的基本步骤有哪些?
厦门模型网站设计制作公司,厦门航空飞机模型掉色怎么办?
EditPlus中的正则表达式实战(6)
Laravel怎么返回JSON格式数据_Laravel API资源Response响应格式化【技巧】
Laravel怎么连接多个数据库_Laravel多数据库连接配置
Laravel如何使用Laravel Vite编译前端_Laravel10以上版本前端静态资源管理【教程】
Android仿QQ列表左滑删除操作
猪八戒网站制作视频,开发一个猪八戒网站,大约需要多少?或者自己请程序员,需要什么程序员,多少程序员能完成?
详解vue.js组件化开发实践
惠州网站建设制作推广,惠州市华视达文化传媒有限公司怎么样?
矢量图网站制作软件,用千图网的一张矢量图做公司app首页,该网站并未说明版权等问题,这样做算不算侵权?应该如何解决?
如何在不使用负向后查找的情况下匹配特定条件前的换行符
北京网站制作公司哪家好一点,北京租房网站有哪些?
Laravel如何发送邮件和通知_Laravel邮件与通知系统发送步骤
轻松掌握MySQL函数中的last_insert_id()
Laravel如何使用Gate和Policy进行授权?(权限控制)
如何用y主机助手快速搭建网站?
常州企业网站制作公司,全国继续教育网怎么登录?
高端网站建设与定制开发一站式解决方案 中企动力


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