stream不直接查数据库,而是高效处理已查出的结果集,需先取数再转流;支持嵌套结构扁平化、条件分组、聚合映射等操作,但需注意分页、避免io阻塞及并行流适用场景。

Stream流本身不直接查数据库,但它能高效处理从MySQL、PostgreSQL等数据库查出的结果集——关键在于“先取数、再转流、最后映射”,而不是让Stream去执行SQL。真正复杂的转换,往往发生在数据加载进内存后的对象组装、嵌套聚合、条件分组等环节。
把ResultSet或List转成Stream流
数据库查询结果通常以List
- 推荐方式:用JDBC + try-with-resources查出List,再调用
list.stream()——安全、可控、易调试 - 慎用方式:基于ResultSet手写Spliterator实现自定义Stream(适合超大数据量+流式消费场景,但开发成本高、易出错)
- ORM场景(如MyBatis):直接返回List
,天然适配Stream,无需额外转换
多层嵌套结构的扁平化与重组
真实业务中常见“订单→订单项→商品→类目”四级嵌套。传统for循环易漏判空、逻辑分散;用Stream可集中表达意图:
- 用
flatMap展开子集合:比如orders.stream().flatMap(o -> o.getItems().stream()) - 用
map做字段投影或对象转换:如把OrderItem转为简化的SaleRecord - 组合filter提前剪枝:在flatMap前加
filter(o -> o.getStatus().equals("PAID")),避免无效展开
按业务规则聚合并映射到目标变量
不是所有结果都要塞进一个List。常需按维度聚合成Map、统计值或DTO:
- 按用户分组求最近3单总金额:
.collect(groupingBy(Order::getUserId, summingDouble(Order::getAmount))) - 生成带汇总信息的报表VO:
.collect(Collectors.collectingAndThen(..., ReportVO::new)) - 提取唯一标识列表供后续查库:
.map(Order::getProductId).distinct().toList()(Java 16+)
注意边界与性能陷阱
Stream简化的是代码,不是IO或内存压力:
- 百万级数据别一次性查全再转Stream——应配合分页(limit/offset)或游标式查询,每次只处理一批
- 避免在map/filter里调远程服务或查DB,会把流式变成串行阻塞操作
- 并行流(parallelStream)对CPU密集型计算有益,但对简单对象转换收益小,还可能引发线程安全问题











