优化:遍历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));
                        }
                    }
                }
            }
        }
}
相关推荐
BillKu40 分钟前
Java + Spring Boot + Mybatis 插入数据后,获取自增 id 的方法
java·tomcat·mybatis
全栈凯哥41 分钟前
Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解
java·算法·leetcode·链表
chxii41 分钟前
12.7Swing控件6 JList
java
全栈凯哥43 分钟前
Java详解LeetCode 热题 100(27):LeetCode 21. 合并两个有序链表(Merge Two Sorted Lists)详解
java·算法·leetcode·链表
YuTaoShao44 分钟前
Java八股文——集合「List篇」
java·开发语言·list
PypYCCcccCc1 小时前
支付系统架构图
java·网络·金融·系统架构
华科云商xiao徐1 小时前
Java HttpClient实现简单网络爬虫
java·爬虫
扎瓦1 小时前
ThreadLocal 线程变量
java·后端
BillKu2 小时前
Java后端检查空条件查询
java·开发语言
jackson凌2 小时前
【Java学习笔记】String类(重点)
java·笔记·学习