java中批量查询+stream返回map提高查询效率

一 批量查询的常规使用与调优

1.1 反例

1.2 优化场景

2.优雅的解法:批量查询+stream组装map

第一步:查列表,拿到所有订单

第二步:提取ID集合,批量查询关联数据,用 Stream 转成 Map

第三步:循环里只从 Map 取值,零次数据库查询

对应代码案例

复制代码
@Service
@Slf4j
public class OrderExportService {

    public List<OrderExportVO> exportOrders(OrderQuery query) {

        // ==================== 循环外面:批量查询,组装Map ====================

        // ① 查订单列表(1次查询)
        List<Order> orders = orderMapper.selectList(query);
        if (CollectionUtils.isEmpty(orders)) {
            return Collections.emptyList();
        }

        // ② 提取所有关联ID
        Set<Long> userIds   = orders.stream().map(Order::getUserId)
                .filter(Objects::nonNull).collect(Collectors.toSet());
        Set<Long> productIds = orders.stream().map(Order::getProductId)
                .filter(Objects::nonNull).collect(Collectors.toSet());
        Set<Long> shopIds    = orders.stream().map(Order::getShopId)
                .filter(Objects::nonNull).collect(Collectors.toSet());
        Set<Long> couponIds  = orders.stream().map(Order::getCouponId)
                .filter(Objects::nonNull).collect(Collectors.toSet());

        // ③ 批量查询 + Stream组装Map(4次查询)
        Map<Long, User>    userMap    = batchQueryMap(userIds,  userMapper::selectBatchIds,    User::getId);
        Map<Long, Product> productMap = batchQueryMap(productIds, productMapper::selectBatchIds, Product::getId);
        Map<Long, Shop>    shopMap    = batchQueryMap(shopIds, shopMapper::selectBatchIds,    Shop::getId);
        Map<Long, Coupon>  couponMap  = couponIds.isEmpty()
                ? Collections.emptyMap()
                : batchQueryMap(couponIds, couponMapper::selectBatchIds,  Coupon::getId);

        // ==================== 循环里面:纯内存组装,零DB查询 

        // ④ 遍历订单,从Map取值组装VO
        return orders.stream()
                .map(order -> buildExportVO(order, userMap, productMap, shopMap, couponMap))
                .collect(Collectors.toList());
    }

    /**
     * 通用的批量查询 + Map组装方法
     * 传入ID集合、查询方法、Key提取器,返回 Map<id, 实体>
     */
    private <T> Map<Long, T> batchQueryMap(
            Collection<Long> ids,
            Function<Collection<Long>, List<T>> queryFunc,
            Function<T, Long> keyExtractor
    ) {
        if (CollectionUtils.isEmpty(ids)) {
            return Collections.emptyMap();
        }
        return queryFunc.apply(ids).stream()
                .collect(Collectors.toMap(keyExtractor, Function.identity(), (a, b) -> a));
    }}

二 实操mock案例

2.1 场景1

1.实体类

复制代码
public class Goods {
    private Long userId;
    private String Name;

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getName() {
        return Name;
    }

    public void setName(String name) {
        Name = name;
    }
}

2.goodservice类

复制代码
public class GoodService {
    public List<Goods> getGoodsList(Collection<Long> userIds) {
        List<Goods> goodsList = new ArrayList<>();
        Goods goods = new Goods();
        goods.setUserId(200L);
        goods.setName("商品1");
        goodsList.add(goods);
        Goods goods2 = new Goods();
        goods2.setUserId(201L);
        goods2.setName("商品2");
        goodsList.add(goods2);
        Goods goods3 = new Goods();
        goods3.setUserId(202L);
        goods3.setName("商品3");
        goodsList.add(goods3);
        return goodsList;
    }
}

3.测试类

复制代码
public class TestStream {
    public static void main(String[] args) {
        Set<Long> userIds = new HashSet<>();
        userIds.add(200L);
        userIds.add(201L);
        GoodService goodService = new GoodService();
        Map<Long, Goods> m = batchQueryMap(userIds, goodService::getGoodsList, Goods::getUserId);
        System.out.println("m:" + m);
    }

    /**
     * 通用的批量查询 + Map组装方法
     * 传入ID集合、查询方法、Key提取器,返回 Map<id, 实体>
     * 方法的参数:Collection<Long> ids,作为第二个方法的参数
     * queryFunc: 调用的方法,Function<Collection<Long>, List<T>>   参数Collection<Long>为请求方法queryFunc的参数类型,和第一个参数的类型一致,
     * 第二个参数为方法queryFunc的返回值类型
     * keyExtractor 为返回map类型,以哪个字段为key,value为对应的实体bean
     */
    private static <T> Map<Long, T> batchQueryMap(
            Collection<Long> ids,
            Function<Collection<Long>, List<T>> queryFunc,
            Function<T, Long> keyExtractor
    ) {
        if (CollectionUtils.isEmpty(ids)) {
            return Collections.emptyMap();
        }
        return queryFunc.apply(ids).stream().collect(Collectors.toMap(keyExtractor, Function.identity(), (a, b) -> a));
    }
}

4.结果

2.2 场景2

相关推荐
wear工程师17 分钟前
Spring 循环依赖为什么要三级缓存:两级会把 AOP 代理弄丢
java·spring
无线通信科研笔记18 分钟前
IEEE TWC 2026 论文精读与完整复现|H-PASS:机械—电子双重可重构波束成形
论文阅读·人工智能·笔记·python·论文笔记
paopaokaka_luck19 分钟前
基于springboot3+vue3+uniapp的河南非遗数字图谱小程序(协同过滤算法、数字图谱展示、ECharts 图形化分析)
java·前端·spring boot·学习·小程序·uni-app·echarts
OPEN-F23 分钟前
C++进阶教程:类与对象深入
java·开发语言·c++
莫陌尛.23 分钟前
StarRocks Iceberg MinIO S3 301报错完整修复文档(Docker环境)
java·docker·容器
AI人工智能+电脑小能手25 分钟前
大白话说Java设计模式-42-访问者模式(业务实战篇)
java·spring·设计模式·访问者模式·asm·营销规则·数据结构与操作分离
青 春 记 忆26 分钟前
零基础入门python35:Django用户登录与权限边界
python·django·后端开发
淼澄研学31 分钟前
LangChain构建Prompt模板解决Python长尾报错实操
python·langchain·prompt
步行cgn32 分钟前
传统 SSM 与 Spring Boot 开发对比:从配置地狱到约定优于配置
java·spring boot·后端