优化:遍历List循环查找数据库导致接口过慢问题

前提:

我们在写查询的时候,有时候会遇到多表联查,一遇到多表联查大家就会直接写sql语句,不会使用较为方便的LambdaQueryWrapper去查询了。作为一个2024新进入码农世界的小白,我喜欢使用LambdaQueryWrapper,虽然他会有很多缺点,但是能跑就行嘛。

背景:

我在公司写了一套查询,遍历一个list,在遍历的时候每次都会查询一次数据库,该list极有可能是十万级的,我的好师兄这时候给我说为什么我的接口调的那么慢,我说我也不知道啊,然后他给我看了一下我的代码,咬牙切齿到,你一个查询要跟数据库交互10万次啊,查一次就算是10ms,你这也得超过10m了。随后我就一边被他吐槽一边听他说解决方案。

解决方案:

不要循环查找数据库,和数据库交互是很慢的,我们选择的应该是先直接把这一大把数据全部查出来,然后交给内存处理这些数据就好了,内存处理数据是非常快的。

举个栗子:

现在我们有一个类目(类目名称:金属),该类目有一些属性(金属颜色,金属材料),属性会有属性值(颜色:黄绿蓝;材料:金银铁)。类目,属性,都是单独一张表来记录,属性值单表中记录属性的id,类目和属性之间存在一张关联表。我们根据此关联关系需要做一个属性的分页,分页需要展示的数据为属性的基本数据+属性的类目+属性值。

思路:

OK!!!!!我们来理一下思路。基本数据就不在多说了,主要关注一下我们的分类info和属性值info。首先是属性值:我们查到属性分页(只含有基本数据)数据后,需要根据属性ids到属性值表中查询到属性值然后塞进返回值中返回。一下我想到的就是遍历ids,然后每一次遍历的时候拿着属性id在属性值表中查到这个属性值List然后添加到结果集合中,最后返回出去。很好想对吧,但是这就触及到了我们的这篇文章的问题了,假如有10万的属性,那我们就会10万次交互数据库,最后造成接口查询十分缓慢。

这个方法我就是用了这个循环遍历查询数据库的方式,导致接口反应速度极慢

cs 复制代码
public List<ItemAttributePoInfo> processCategoryAndValues(List<ItemAttributePo> itemAttributePos) {
        List<ItemAttributePoInfo> results = AbstractModelConverter.convertListByBeanCopier(itemAttributePos, ItemAttributePoInfo.class);//最终需要被返回的结果集
        List<Long> attributeIds = results.stream().map(e -> e.getId()).collect(Collectors.toList());
        
        
        for (ItemAttributePoInfo result : results) {
            Long attributeId = result.getId();//属性id
            LambdaQueryWrapper<ItemAttributeValue> attributeValueWrapper = new LambdaQueryWrapper<>();
            attributeValueWrapper.eq(ItemAttributeValue::getAttributeId, attributeId);
            List<ItemAttributeValue> itemAttributeValueList = itemAttributeValueReadService.list(attributeValueWrapper);
            List<ItemAttributeValueInfo> itemAttributeValueInfos = AbstractModelConverter.convertList(itemAttributeValueList, ItemAttributeValueInfo.class);
            result.setItemAttributeValueInfos(itemAttributeValueInfos);
        }
        return results;
    }
}

正确做法:

那么正确的做法是什么呢,就是我上文所说的我们应该根据属性ids一次性把所有的数据都查出来,然后我们根据属性id分组。分组成为一个Map<Long, List<属性值>>。这样我们最后直接循环结果集,将对应属性id作为map的key,从map中查到对应属性值的list塞入即可。下面的代码为正确做法。

cs 复制代码
public List<ItemAttributePoInfo> processCategoryAndValues(List<ItemAttributePo> itemAttributePos) {
        List<ItemAttributePoInfo> results = AbstractModelConverter.convertListByBeanCopier(itemAttributePos, ItemAttributePoInfo.class);
        List<Long> attributeIds = results.stream().map(e -> e.getId()).collect(Collectors.toList());
        //封装属性值信息
        LambdaQueryWrapper<ItemAttributeValue> attributeValueWrapper = new LambdaQueryWrapper<>();
        attributeValueWrapper.in(ItemAttributeValue::getAttributeId, attributeIds);
        List<ItemAttributeValue> itemAttributeValueList = itemAttributeValueReadService.list(attributeValueWrapper);
        Map<Long, List<ItemAttributeValue>> attributeValueMaps = itemAttributeValueList.stream().collect(Collectors.groupingBy(ItemAttributeValue::getAttributeId));
        if (CollectionUtils.isNotEmpty(itemAttributeValueList)) {
            for (ItemAttributePoInfo result : results) {
                List<ItemAttributeValue> attributeValueResult = attributeValueMaps.get(result.getId());
                if (CollectionUtils.isEmpty(attributeValueResult)) {
                    result.setItemAttributeValueInfos(new ArrayList<>());
                } else {
                    result.setItemAttributeValueInfos(AbstractModelConverter.convertList(attributeValueResult, ItemAttributeValueInfo.class));
                }
            }
        }
        return results;
    }

注:

该方法可能对刚刚使用这个方法的同学不太友好,理解起来相对比较费力,大家要多看两遍,代码也很重要,理解其中的意思,该例子中的类目由于设计到关联表,使用起来可能理解难度会更大一些,先把属性值理解了再来看类目更容易一些。

附:

类目info:

cs 复制代码
    public List<ItemAttributePoInfo> processCategoryAndValues(List<ItemAttributePo> itemAttributePos) {
        List<ItemAttributePoInfo> results = AbstractModelConverter.convertListByBeanCopier(itemAttributePos, ItemAttributePoInfo.class);//最终需要被返回的结果集
        List<Long> attributeIds = results.stream().map(e -> e.getId()).collect(Collectors.toList());
        //封装类目信息
        LambdaQueryWrapper<ItemCategoryAttributeRel> categoryAttributeRelWrapper = new LambdaQueryWrapper<>();
        categoryAttributeRelWrapper.in(ItemCategoryAttributeRel::getAttributeId, attributeIds);
        List<ItemCategoryAttributeRel> itemCategoryAttributeRelList = itemCategoryAttributeRelReadService.list(categoryAttributeRelWrapper);
        if (CollectionUtils.isNotEmpty(itemCategoryAttributeRelList)) {
            List<Long> categoryIds = itemCategoryAttributeRelList.stream().map(e -> e.getCategoryId()).collect(Collectors.toList());
            Map<Long, List<ItemCategoryAttributeRel>> categoryAttributeMaps = itemCategoryAttributeRelList.stream().collect(Collectors.groupingBy(ItemCategoryAttributeRel::getAttributeId));
            List<ItemCategoryPo> categorise = itemCategoryPoReadService.findByIds(categoryIds);
            if (CollectionUtils.isNotEmpty(categorise)) {
                Map<Long, ItemCategoryPo> categoryMap = categorise.stream().collect(Collectors.toMap(e -> e.getId(), e -> e));
                for (ItemAttributePoInfo result : results) {
                    List<ItemCategoryAttributeRel> itemCategoryAttributeRels = categoryAttributeMaps.get(result.getId());
                    if (CollectionUtils.isNotEmpty(itemCategoryAttributeRels)) {
                        List<ItemCategoryPo> categoryResult = itemCategoryAttributeRels.stream().map(ItemCategoryAttributeRel::getCategoryId).map(categoryMap::get).collect(Collectors.toList());
                        if (CollectionUtils.isEmpty(categoryResult)) {
                            result.setItemCategoryPoInfo(new ItemCategoryPoInfo());
                        } else {
                            result.setItemCategoryPoInfo(AbstractModelConverter.convertModelByBeanCopier(categoryResult.get(0), ItemCategoryPoInfo.class));
                        }
                    }
                }
            }
        }
}
相关推荐
李慕婉学姐6 小时前
【开题答辩过程】以《基于JAVA的校园即时配送系统的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·开发语言·数据库
奋进的芋圆8 小时前
Java 延时任务实现方案详解(适用于 Spring Boot 3)
java·spring boot·redis·rabbitmq
sxlishaobin8 小时前
设计模式之桥接模式
java·设计模式·桥接模式
model20058 小时前
alibaba linux3 系统盘网站迁移数据盘
java·服务器·前端
荒诞硬汉9 小时前
JavaBean相关补充
java·开发语言
提笔忘字的帝国9 小时前
【教程】macOS 如何完全卸载 Java 开发环境
java·开发语言·macos
2501_941882489 小时前
从灰度发布到流量切分的互联网工程语法控制与多语言实现实践思路随笔分享
java·开发语言
華勳全栈9 小时前
两天开发完成智能体平台
java·spring·go
alonewolf_999 小时前
Spring MVC重点功能底层源码深度解析
java·spring·mvc
沛沛老爹10 小时前
Java泛型擦除:原理、实践与应对策略
java·开发语言·人工智能·企业开发·发展趋势·技术原理