MyBatis N+1问题的5种解决方案
创作时间:
作者:
@小白创作中心
MyBatis N+1问题的5种解决方案
引用
CSDN
1.
https://blog.csdn.net/kaka_buka/article/details/139439727
什么是MyBatis的N+1问题
N+1问题通常出现在一对多关联查询中。当我们查询主表数据(如订单)并希望获取关联的从表数据(如订单的商品)时,如果每获取一条主表记录都要执行一次从表查询,就会产生N+1次查询的问题。假设有10个订单,主查询执行1次,从查询执行10次,总共执行了11次查询。这种情况显然会导致性能低下。
传统做法示例
假设我们有两个表:orders(订单表)和items(商品表),一个订单可以有多个商品。传统的MyBatis配置可能会这样写:
<select id="getOrders" resultMap="orderResultMap">
SELECT * FROM orders
</select>
<select id="getItemsByOrderId" resultMap="itemResultMap" parameterType="int">
SELECT * FROM items WHERE order_id = #{orderId}
</select>
在Java代码中调用:
List<Order> orders = orderMapper.getOrders();
for (Order order : orders) {
List<Item> items = orderMapper.getItemsByOrderId(order.getId());
order.setItems(items);
}
这种方式会导致N+1问题。
解决方案
1. 使用嵌套查询(Subqueries)
嵌套查询通过在一个查询中嵌套其他查询,可以减少查询次数。这个方法通常在SQL语句中使用IN子句。例如:
<select id="getOrdersWithItems" resultMap="orderWithItemsResultMap">
SELECT * FROM orders WHERE id IN
(SELECT DISTINCT order_id FROM items WHERE order_id IS NOT NULL)
</select>
<resultMap id="orderWithItemsResultMap" type="Order">
<id property="id" column="id"/>
<result property="orderName" column="order_name"/>
<collection property="items" ofType="Item">
<id property="id" column="item_id"/>
<result property="itemName" column="item_name"/>
<result property="orderId" column="order_id"/>
</collection>
</resultMap>
2. 使用JOIN查询
JOIN查询通过一次性获取所有需要的数据,避免了多次查询的问题。这个方法通常在SQL语句中使用LEFT JOIN或INNER JOIN等连接操作。例如:
<select id="getOrdersWithItems" resultMap="orderWithItemsResultMap">
SELECT o.*, i.* FROM orders o
LEFT JOIN items i ON o.id = i.order_id
</select>
<resultMap id="orderWithItemsResultMap" type="Order">
<id property="id" column="id"/>
<result property="orderName" column="order_name"/>
<collection property="items" ofType="Item">
<id property="id" column="item_id"/>
<result property="itemName" column="item_name"/>
<result property="orderId" column="order_id"/>
</collection>
</resultMap>
3. 使用批量查询(Batch Query)
批量查询可以将多个查询合并为一个查询,减少查询次数。例如:
<select id="getOrders" resultMap="orderResultMap">
SELECT * FROM orders
</select>
<select id="getItemsByOrderIds" resultMap="itemResultMap" parameterType="list">
SELECT * FROM items WHERE order_id IN
<foreach item="orderId" collection="list" open="(" separator="," close=")">
#{orderId}
</foreach>
</select>
在Java代码中批量查询:
List<Order> orders = orderMapper.getOrders();
List<Integer> orderIds = orders.stream().map(Order::getId).collect(Collectors.toList());
List<Item> items = orderMapper.getItemsByOrderIds(orderIds);
// 处理查询结果,将items分配给对应的order
Map<Integer, List<Item>> itemsMap = items.stream().collect(Collectors.groupingBy(Item::getOrderId));
for (Order order : orders) {
order.setItems(itemsMap.get(order.getId()));
}
4. 使用缓存(Caching)
缓存可以减少数据库的查询次数,特别是在数据变化不频繁的情况下。MyBatis提供了一级缓存和二级缓存机制。例如:
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
<cache/>
在Mapper文件中使用缓存:
<cache/>
<select id="getOrders" resultMap="orderResultMap" useCache="true">
SELECT * FROM orders
</select>
<select id="getItemsByOrderId" resultMap="itemResultMap" parameterType="int" useCache="true">
SELECT * FROM items WHERE order_id = #{orderId}
</select>
5. 使用懒加载(Lazy Loading)
MyBatis支持懒加载,当访问到关联对象时才执行查询。可以通过以下方式开启懒加载:
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
在Mapper文件中设置:
<resultMap id="orderResultMap" type="Order">
<id property="id" column="id"/>
<result property="orderName" column="order_name"/>
<association property="items" javaType="List" select="getItemsByOrderId" fetchType="lazy"/>
</resultMap>
参考链接
- MyBatis官方文档
热门推荐
上证指数最新走势解析:涨跌停板谁主沉浮?
四姑娘山长穿毕:4668米高原徒步,邂逅雪山与星空
长穿毕徒步路线:川西最美徒步天堂
桑葚泡水:秋季养生的滋补佳品,4种经典搭配方案
桑葚:从古代养生圣品到现代科学验证的多重功效
桑葚:千年养生食材的现代科学解读
成都老房子的酸菜炒肉:从选材到烹饪的完整攻略
川进青出大环线:15天川藏自驾游攻略
冬季川藏线自驾游全攻略:安全驾驶与实用建议
豆浆四季养生,改善饮食so easy!
黑芝麻豆浆 vs 香蕉核桃豆浆:谁才是真正的“营养王者”?
用AI工具写开题可以吗?探索科研与智能的交汇
微信关怀生病好友的最佳姿势
遵义红果树景区:卡斯特地貌博物馆,60元畅游山水溶洞
古代宫女的“魔鬼训练营”
“合卺之欢”中“卺”字怎么读,是什么意思,有什么典故?
揭秘慈禧太后的日常:从凌晨起床到朝政之间的梳妆时光
紫禁城里的宫女生活:奢华背后的辛酸
板楼 vs 塔楼:谁才是你的理想家园?
板楼 vs 塔楼:谁才是健康生活的首选?
春节亲子新玩法:“拍灰舞”
量子纠缠证实,不涉及人生意义与灵魂不朽
量子位叠加纠缠:量子计算三大基石
盘点心想事成的近义成语:8个表达愿望实现的美好词语
麦玲玲详解2024年生肖龙运势:事业财运双丰收
国六排放标准下的GPF技术:挑战与解决方案
中国舞比赛评分标准揭秘:你知道多少?
《红扇》教你中国舞情感表达
国花之争:牡丹、梅花、菊花、莲花谁才是真正的花魁?
古诗词中的花意象:牡丹与梅花的千年情缘