不少公司(特別是作電商的)其實都是不容許多表關聯查詢的,或者嚴格控制關聯的表數量,好比最多關聯二、3張表。此時,若是某個需求又確實須要進行關聯查詢怎麼辦呢?前端
好比前端有個頁面:java
id | product_name | price | user_name | user_age |
---|---|---|---|---|
10086 | iphone 12 pro | 6666 | zhangsan | 18 |
很明顯,這個頁面字段來自兩張表:sql
正常來講,直接這樣寫SQL便可:markdown
SELECT p.id, p.product_name, p.price, u.user_name, u.user_age
FROM t_product p
LEFT JOIN t_user u ON p.user_id=u.id;
複製代碼
但上面說了,不能關聯查詢。app
做爲替代方案,能夠先從t_product表查出10條數據到內存(t_product做爲主表),而後取出10條數據的uid,再調用UserService#listUser(uids),獲得對應的userList。此時內存中有10條product,也有10條user,匹配組合便可。iphone
public List<ProductExtendsTO> getList(Integer page, Integer pageSize) {
// 1.查詢Product
List<Product> productList = listProduct(page, pageSize);
// 2.取出裏面的全部uid
List<Long> uids = productList.stream()
.map(Product::getUid)
.collect(Collectors.toList());
// 3.查出userList
List<User> userList = listUser(uids);
// 4.把List轉成Map
Map<Long, User> userMap = new HashMap<Long, User>();
for(userList : user){
userMap.put(user.getId(), user);
}
// 組合並返回數據
List<ProductExtendsTO> result = new ArrayList<>();
productList.foreach(product->{
ProductExtendsTO productExtends = new ProductExtendsTO();
BeanUtils.copyProperties(product, productExtends);
// 根據product的uid從userMap獲取user(此處省略user null判斷)
User user = userMap.get(product.getUid());
productExtends.setUserAge(user.getUserAge());
productExtends.setUserName(user.getUserName());
result.add(productExtends);
});
return result;
}
複製代碼
上面的代碼能夠優化爲(主要第4點):分佈式
public List<ProductExtendsTO> getList(Integer page, Integer pageSize) {
// 1.查詢Product
List<Product> productList = listProduct(page, pageSize);
// 2.取出裏面的全部uid
List<Long> uids = productList.stream()
.map(Product::getUid)
.collect(Collectors.toList());
// 3.查出userList
List<User> userList = listUser(uids);
// 4.把List轉成Map(優化了這裏)
Map<Long, User> userMap = userList.stream()
.collect(Collectors.toMap(User::getId(), user->user));
// 組合並返回數據
List<ProductExtendsTO> result = new ArrayList<>();
productList.foreach(product->{
ProductExtendsTO productExtends = new ProductExtendsTO();
BeanUtils.copyProperties(product, productExtends);
// 根據product的uid從userMap獲取user(此處省略user null判斷)
User user = userMap.get(product.getUid());
productExtends.setUserAge(user.getUserAge());
productExtends.setUserName(user.getUserName());
result.add(productExtends);
})
return result;
}
複製代碼
List轉Map是很是廣泛的需求,Stream API其實仍是有點囉嗦(代碼太長了),因此咱們能夠試着封裝一下:函數
public final class ConvertUtil {
private ConvertUtil() {
}
/** * 將List轉爲Map * * @param list 原數據 * @param keyExtractor Key的抽取規則 * @param <K> Key * @param <V> Value * @return */
public static <K, V> Map<K, V> listToMap(List<V> list, Function<V, K> keyExtractor) {
if (list == null || list.isEmpty()) {
return new HashMap<>();
}
Map<K, V> map = new HashMap<>(list.size());
for (V element : list) {
// 利用keyExtractor從對象中抽取Key
K key = keyExtractor.apply(element);
// 這裏默認key不能爲null
if (key == null) {
continue;
}
map.put(key, element);
}
return map;
}
}
複製代碼
除了List轉Map,從List中抽取特定字段的需求也是很是廣泛的,好比上面代碼:post
// 2.取出裏面的全部uid(省略null判斷)
List<Long> uids = productList.stream()
.map(Product::getUid)
.collect(Collectors.toList());
複製代碼
意思是從productList中抽取uids。爲了複用,咱們也封裝一下:大數據
public class ConvertUtil {
private ConvertUtil() {
}
/** * 將List映射爲List,好比List<Person> personList轉爲List<String> nameList * * @param originList 原數據 * @param mapper 映射規則 * @param <T> 原數據的元素類型 * @param <R> 新數據的元素類型 * @return */
public static <T, R> List<R> resultToList(List<T> originList, Function<T, R> mapper) {
if (list == null || list.isEmpty()) {
return new ArrayList<>();
}
List<R> newList = new ArrayList<>(originList.size());
for (T originElement : originList) {
R newElement = mapper.apply(originElement);
if (newElement == null) {
continue;
}
newList.add(newElement);
}
return newList;
}
/** * 將List轉爲Map * * @param list 原數據 * @param keyExtractor Key的抽取規則 * @param <K> Key * @param <V> Value * @return */
public static <K, V> Map<K, V> listToMap(List<V> list, Function<V, K> keyExtractor) {
if (list == null || list.isEmpty()) {
return new HashMap<>();
}
Map<K, V> map = new HashMap<>(list.size());
for (V element : list) {
K key = keyExtractor.apply(element);
if (key == null) {
continue;
}
map.put(key, element);
}
return map;
}
}
複製代碼
上面權當拋磚引玉,你們能夠基於實際需求自行擴展ConvertUtil,讓它更好用。
總結:
有時遇到複雜的統計報表等數據,很難經過上面「內存關聯」的方式完成需求,此時可讓公司的大數據部門提供接口,直接從大數據那邊獲取數據。但這個並不須要咱們操心:小公司適當關聯查詢無傷大雅,大公司通常都有大數據部門。
五一假期最後一天,收拾收拾心情準備回杭搬磚。
我是bravo1988,下次見。
よろしく・つづく
往期文章: