Сцены
Многие компании (особенно компании электронной коммерции) на самом деле не разрешают запросы на сопоставление нескольких таблиц или строго контролируют количество связанных таблиц, например, максимум 2 или 3 таблицы. Что делать, если определенное требование действительно требует выполнения связанного запроса?
Например, в интерфейсе есть страница:
id | product_name | price | user_name | user_age |
---|---|---|---|---|
10086 | iphone 12 pro | 6666 | zhangsan | 18 |
Очевидно, что это поле страницы происходит из двух таблиц:
- t_product
- t_user
Обычно вы можете написать SQL напрямую следующим образом:
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;
Но, как упоминалось выше, запрос не может быть связан.
решение
Как вариант, можно сначала найти 10 кусков данных из таблицы t_product в память (t_product используется как основная таблица), потом вынуть uid 10 кусков данных, а потом вызвать UserService#listUser(uids) чтобы получить соответствующий список пользователей. В настоящее время в памяти имеется 10 продуктов и 10 пользователей, и комбинация может быть сопоставлена.
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;
}
Оптимизация кода: инкапсулировать ConvertUtil
List to 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 to Map, также очень распространена необходимость извлечения определенных полей из List, например, приведенный выше код:
// 2.取出里面的所有uid(省略null判断)
List<Long> uids = productList.stream()
.map(Product::getUid)
.collect(Collectors.toList());
Средство для извлечения uid из productList. Для повторного использования мы также инкапсулируем:
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 в зависимости от фактических потребностей, чтобы сделать его более удобным.
Суммировать:
- List to Map, основное внимание уделяется правилам извлечения ключа во входящей карте, то есть KeyExtractor, который использует функциональный интерфейс
- Список извлекает FieldList, основное внимание также уделяется определению правил извлечения полей, а также использует функциональный интерфейс
Другие стратегии разрешения
Иногда встречаются сложные статистические отчеты и другие данные, и трудно выполнить требования с помощью вышеуказанного метода «ассоциации памяти».В настоящее время отдел больших данных компании может предоставить интерфейс для получения данных непосредственно из больших данных. Но нам не нужно беспокоиться об этом: для небольших компаний безвредно правильно коррелировать запросы, а в крупных компаниях вообще есть отделы больших данных.
В последний день майских праздников я собрался и приготовился вернуться в Ханчжоу, чтобы перевезти кирпичи.
Я bravo1988, увидимся в следующий раз.
よろしく・つづく
Прошлые статьи:
Комикс: От блокировки JVM к распределенной блокировке Redis