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

相关推荐
智慧物业老杨3 小时前
物业数字化落地思考:真正的转型,是底层数据秩序的重构
java·大数据·人工智能·微服务·系统架构
星空6 小时前
金蝶苍穹build.gradle配置
java·build.gradle
麻雀飞吧6 小时前
先判断工具用来学习、开发还是执行
人工智能·python
人邮异步社区7 小时前
学习Python的最佳学习路径是什么?
python·程序员
步行cgn8 小时前
Spring 基于 XML 的自动装配:byName 详解
java·后端·spring
大帅点兵9 小时前
Apache Ant 1.9.9 完整讲解
java
troy1289 小时前
Python 基础语法(九):Django/Flask/FastAPI 三大 Web 框架详细对比解析
python·jupyter·django·flask·github·fastapi
jzshmyt9 小时前
我用 Python 从零“生成“了一个宇宙,然后让它观察自己(v14)
人工智能·pytorch·python·numpy·matplotlib·空间计算·scipy
LuTshoes9 小时前
spring ai 实战 手搓 PlaneExecuteAgent
java·人工智能·spring·ai
这料鬼有毒9 小时前
二刷hot100-73.矩阵置零
java