前提:
? ?我們在寫查詢的時候,有時候會遇到多表聯查,一遇到多表聯查大家就會直接寫sql語句,不會使用較為方便的LambdaQueryWrapper去查詢了。作為一個2024新進入碼農世界的小白,我喜歡使用LambdaQueryWrapper,雖然他會有很多缺點,但是能跑就行嘛。
背景:
? 我在公司寫了一套查詢,遍歷一個list,在遍歷的時候每次都會查詢一次數據庫,該list極有可能是十萬級的,我的好師兄這時候給我說為什么我的接口調的那么慢,我說我也不知道啊,然后他給我看了一下我的代碼,咬牙切齒到,你一個查詢要跟數據庫交互10萬次啊,查一次就算是10ms,你這也得超過10m了。隨后我就一邊被他吐槽一邊聽他說解決方案。
解決方案:
? 不要循環查找數據庫,和數據庫交互是很慢的,我們選擇的應該是先直接把這一大把數據全部查出來,然后交給內存處理這些數據就好了,內存處理數據是非常快的。
舉個栗子:
? 現在我們有一個類目(類目名稱:金屬),該類目有一些屬性(金屬顏色,金屬材料),屬性會有屬性值(顏色:黃綠藍;材料:金銀鐵)。類目,屬性,都是單獨一張表來記錄,屬性值單表中記錄屬性的id,類目和屬性之間存在一張關聯表。我們根據此關聯關系需要做一個屬性的分頁,分頁需要展示的數據為屬性的基本數據+屬性的類目+屬性值。
思路:
? OK!!!!!我們來理一下思路。基本數據就不在多說了,主要關注一下我們的分類info和屬性值info。首先是屬性值:我們查到屬性分頁(只含有基本數據)數據后,需要根據屬性ids到屬性值表中查詢到屬性值然后塞進返回值中返回。一下我想到的就是遍歷ids,然后每一次遍歷的時候拿著屬性id在屬性值表中查到這個屬性值List然后添加到結果集合中,最后返回出去。很好想對吧,但是這就觸及到了我們的這篇文章的問題了,假如有10萬的屬性,那我們就會10萬次交互數據庫,最后造成接口查詢十分緩慢。
? ? ? ? ? ? 這個方法我就是用了這個循環遍歷查詢數據庫的方式,導致接口反應速度極慢
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();//屬性idLambdaQueryWrapper<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塞入即可。下面的代碼為正確做法。
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:
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));}}}}}
}