c 网站做微信支付功能,手机网站建设策划,wordpress 教育类主题,多用户商城系统的效果解决MyBatis的N1问题
N1问题通常出现在一对多关联查询中。当我们查询主表数据#xff08;如订单#xff09;并希望获取关联的从表数据#xff08;如订单的商品#xff09;时#xff0c;如果每获取一条主表记录都要执行一次从表查询#xff0c;就会产生N1次查询的问题。假…解决MyBatis的N1问题
N1问题通常出现在一对多关联查询中。当我们查询主表数据如订单并希望获取关联的从表数据如订单的商品时如果每获取一条主表记录都要执行一次从表查询就会产生N1次查询的问题。假设有10个订单主查询执行1次从查询执行10次总共执行了11次查询。这种情况显然会导致性能低下。
这个问题比较傻可能只有刚接触编程的人才会犯这么初级的错误最近在面试的过程中被问到了这个问题给我搞的一愣一愣的。所以记录一下。
示例
假设我们有两个表orders订单表和items商品表一个订单可以有多个商品。传统的MyBatis配置可能会这样写
select idgetOrders resultMaporderResultMapSELECT * FROM orders
/selectselect idgetItemsByOrderId resultMapitemResultMap parameterTypeintSELECT * FROM items WHERE order_id #{orderId}
/select在Java代码中调用
ListOrder orders orderMapper.getOrders();
for (Order order : orders) {ListItem items orderMapper.getItemsByOrderId(order.getId());order.setItems(items);
}这种方式会导致N1问题。
解决方法
1. 使用嵌套查询Subqueries
嵌套查询通过在一个查询中嵌套其他查询可以减少查询次数。这个方法通常在SQL语句中使用IN子句。例如
select idgetOrdersWithItems resultMaporderWithItemsResultMapSELECT * FROM orders WHERE id IN(SELECT DISTINCT order_id FROM items WHERE order_id IS NOT NULL)
/selectresultMap idorderWithItemsResultMap typeOrderid propertyid columnid/result propertyorderName columnorder_name/collection propertyitems ofTypeItemid propertyid columnitem_id/result propertyitemName columnitem_name/result propertyorderId columnorder_id//collection
/resultMap2. 使用JOIN查询
JOIN查询通过一次性获取所有需要的数据避免了多次查询的问题。这个方法通常在SQL语句中使用LEFT JOIN或INNER JOIN等连接操作。例如
select idgetOrdersWithItems resultMaporderWithItemsResultMapSELECT o.*, i.* FROM orders oLEFT JOIN items i ON o.id i.order_id
/selectresultMap idorderWithItemsResultMap typeOrderid propertyid columnid/result propertyorderName columnorder_name/collection propertyitems ofTypeItemid propertyid columnitem_id/result propertyitemName columnitem_name/result propertyorderId columnorder_id//collection
/resultMap3. 使用批量查询Batch Query
批量查询可以将多个查询合并为一个查询减少查询次数。例如
select idgetOrders resultMaporderResultMapSELECT * FROM orders
/selectselect idgetItemsByOrderIds resultMapitemResultMap parameterTypelistSELECT * FROM items WHERE order_id INforeach itemorderId collectionlist open( separator, close)#{orderId}/foreach
/select在Java代码中批量查询
ListOrder orders orderMapper.getOrders();
ListInteger orderIds orders.stream().map(Order::getId).collect(Collectors.toList());
ListItem items orderMapper.getItemsByOrderIds(orderIds);// 处理查询结果将items分配给对应的order
MapInteger, ListItem itemsMap items.stream().collect(Collectors.groupingBy(Item::getOrderId));
for (Order order : orders) {order.setItems(itemsMap.get(order.getId()));
}4. 使用缓存Caching
缓存可以减少数据库的查询次数特别是在数据变化不频繁的情况下。MyBatis提供了一级缓存和二级缓存机制。例如
settingssetting namecacheEnabled valuetrue/
/settingscache/在Mapper文件中使用缓存
cache/
select idgetOrders resultMaporderResultMap useCachetrueSELECT * FROM orders
/selectselect idgetItemsByOrderId resultMapitemResultMap parameterTypeint useCachetrueSELECT * FROM items WHERE order_id #{orderId}
/select5. 使用懒加载Lazy Loading
MyBatis支持懒加载当访问到关联对象时才执行查询。可以通过以下方式开启懒加载
settingssetting namelazyLoadingEnabled valuetrue/setting nameaggressiveLazyLoading valuefalse/
/settings在Mapper文件中设置
resultMap idorderResultMap typeOrderid propertyid columnid/result propertyorderName columnorder_name/association propertyitems javaTypeList selectgetItemsByOrderId fetchTypelazy/
/resultMap参考链接
MyBatis官方文档