优化:遍历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));
                        }
                    }
                }
            }
        }
}
相关推荐
鸣弦artha5 分钟前
蓝桥杯——杨辉三角
java·算法·蓝桥杯·eclipse
大波V513 分钟前
设计模式-参考的雷丰阳老师直播课
java·开发语言·设计模式
计算机-秋大田19 分钟前
基于微信小程序的平安驾校预约平台的设计与实现(源码+LW++远程调试+代码讲解等)
java·spring boot·微信小程序·小程序·vue·课程设计
《源码好优多》31 分钟前
基于Java Springboot旅游信息推荐系统
java·spring boot·旅游
岁月无声code32 分钟前
Spring Boot 牛刀小试 org.springframework.boot:spring-boot-maven-plugin:找不到类错误
java·spring boot·github
不爱学习的YY酱1 小时前
【计网不挂科】计算机网络第二章< 物理层 >习题库(含答案)
java·数据库·计算机网络
南城花随雪。1 小时前
Spring框架之装饰者模式 (Decorator Pattern)
java·开发语言·装饰器模式
编程、小哥哥1 小时前
设计模式之装饰器模式(SSO单点登录功能扩展,增加拦截用户访问方法范围场景)
java·设计模式·装饰器模式
hummhumm2 小时前
第 12 章 - Go语言 方法
java·开发语言·javascript·后端·python·sql·golang
hummhumm2 小时前
第 8 章 - Go语言 数组与切片
java·开发语言·javascript·python·sql·golang·database