解决MyBatis的N+1问题

解决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 JOININNER 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官方文档

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/679436.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

线性电源运放驱动调整管的方案仿真

群里有人的电路板做出来电压不稳&#xff0c;加负载就掉电压。我对这个运放的工作状态不是很理解&#xff0c;所以仿真了一下。结果却是稳定的。他用12v给运放供电&#xff0c;要求输出10.5. 从仿真看。12运放供电只能输出9v。而且还是到了运放的极限。所以通过仿真后确定怀疑路…

10-Django项目--Ajax请求

目录 Ajax请求 简单示范 html 数据添加 py文件 html文件 demo_list.html Ajax_data.py 图例 Ajax请求 简单示范 html <input type"button" id"button-one" class"btn btn-success" value"点我"> ​ ​ <script>/…

实现秒传与限速!深度解析万亿GB网盘系统架构

1. 系统需求与挑战 1.1 DBox核心功能 在设计一个面向万亿GB的网盘系统时&#xff0c;我们需要首先明确系统的核心功能需求。DBox 作为一个高并发、高可靠的网盘系统&#xff0c;核心功能需求主要包括以下几点&#xff1a; 海量存储&#xff1a;支持存储海量数据&#xff0c;…

面粉厂/木材厂选择防爆客流统计系统的原因

在面粉厂和木材厂这样的特殊行业中&#xff0c;存在着一系列的痛点问题。 对于面粉厂而言&#xff0c;面粉粉尘的存在使其面临着爆炸的潜在危险&#xff0c;而人员的随意流动和不确切统计可能会进一步加剧安全风险。同时&#xff0c;难以精确掌握不同区域的人员分布情况&#x…

SploitScan:一款多功能实用型安全漏洞管理平台

关于SploitScan SploitScan是一款功能完善的实用型网络安全漏洞管理工具&#xff0c;该工具提供了用户友好的界面&#xff0c;旨在简化广大研究人员识别已知安全漏洞的相关信息和复现过程。 SploitScan可以帮助网络安全专业人员快速识别和测试已知安全漏洞&#xff0c;如果你需…

解线性方程组——最速下降法及图形化表示 | 北太天元 or matlab

文章所对应的视频讲解 最速下降法 解线性方程组 一、思路转变 A为对称正定矩阵&#xff0c; A x b Ax b Axb 求解向量 x x x这个问题可以转化为一个求 f ( x ) f(x) f(x)极小值点的问题&#xff0c;为什么可以这样&#xff1a; f ( x ) 1 2 x T A x − x T b c f(x) \f…

大数据数据治理

大数据数据治理介绍 大数据数据治理是一个复杂的过程&#xff0c;涉及到数据的标准化、融通、关联、解析、聚合等一系列活动。其核心目标是在确保数据安全的基础上&#xff0c;提高大数据资源和资产的可用性、易用性和可靠性&#xff0c;从而显著提升大数据资源和资产的价值7。…

从0开始学人工智能测试节选:Spark -- 结构化数据领域中测试人员的万金油技术(四)

上一章节我们了解了 shuffle 相关的概念和原理后其实可以发现一个问题&#xff0c;那就是 shuffle 比较容易造成数据倾斜的情况。 例如上一节我们看到的图&#xff0c;在这批数据中&#xff0c;hello 这个单词的行占据了绝大部分&#xff0c;当我们执行 groupByKey 的时候触发了…

FL Studio21.2.8中文版水果音乐制作的革新之旅!

在数字化浪潮的推动下&#xff0c;音乐制作领域经历了翻天覆地的变化。从最初的模拟技术到如今的全数字化处理&#xff0c;音乐制作的门槛被大幅降低&#xff0c;越来越多的音乐爱好者和专业人士开始尝试自行创作和编辑音乐。在这个过程中&#xff0c;各种专业音乐制作软件成为…

孵化器补贴政策提问模板

对于一些需要创业的人来说&#xff0c;找场地是非常非常难的&#xff0c;一个好的场地能够提高创业的成功率&#xff0c;下面这些内容对于孵化器产业园的政策有一个好的提问&#xff0c;可以帮助你们了解这个孵化器合不合适。需要创业的人可以收藏 某孵化器政策示例 提问模板 …

Java进制转换

进制介绍 二进制&#xff1a;0B开头&#xff0c;0-1 八进制&#xff1a;0开头&#xff0c;0-7 十进制&#xff1a;0-9 十六进制&#xff1a;0x开头&#xff0c;0-9和A-F public class Binary{public static void main(String[] args){//二进制 10int n10B1010//十进制 1010int…

短视频动画脚本:成都鼎茂宏升文化传媒公司

短视频动画脚本&#xff1a;创作与魅力的探索 在数字化时代的浪潮中&#xff0c;短视频动画以其独特的魅力迅速崛起&#xff0c;成为大众娱乐和信息传播的重要载体。成都鼎茂宏升文化传媒公司作为一名原创文章编辑&#xff0c;我深入探索了短视频动画脚本的创作过程&#xff0…

揭秘GPU技术新趋势:从虚拟化到池化

从GPU虚拟化到池化 大模型兴起加剧GPU算力需求&#xff0c;企业面临GPU资源有限且利用率不高的挑战。为打破这一瓶颈&#xff0c;实现GPU算力资源均衡与国产化替代&#xff0c;GPU算力池化成为关键。本文深入探讨GPU设备虚拟化途径、共享方案及云原生实现&#xff0c;旨在优化资…

大模型学习资料整理:如何从0到1学习大模型,搭建个人或企业RAG系统,如何评估与优化(更新中...)

通过本文您可以了解到&#xff1a; 学习&#xff1a;从小白如何入手&#xff0c;从0到1开始学习大模型。RAG系统&#xff1a;我想搭建属于自己或者企业的RAG系统&#xff0c;我该怎么去做&#xff1f;评估&#xff1a;微调后的模型或者RAG系统&#xff0c;如何评估自己的模型和…

软件质量保障——三、四

三、黑盒测试 1.黑盒测试概述 1.1 如何理解黑盒测试&#xff1f; 1.2 黑盒测试有什么特点&#xff1f; 1.3 如何实施黑盒测试&#xff1f; 2. 黑盒测试用例设计和生成方法&#xff08;这里还是要自己找题做&#xff09; 2.1 等价类划分法 步骤&#xff1a; 1.选择划分准…

设置电脑定时关机

1.使用快捷键winR 打开运行界面 2.输入cmd &#xff0c;点击确认&#xff0c;打开命令行窗口&#xff0c;输入 shutdown -s -t 100&#xff0c;回车执行命令&#xff0c;自动关机设置成功 shutdown: 这是主命令&#xff0c;用于执行关闭或重启操作。-s: 这个参数用于指定执行关…

flask音乐交流平台-计算机毕业设计源码57105

摘要 科技进步的飞速发展引起人们日常生活的巨大变化&#xff0c;电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流&#xff0c;人类发展的历史正进入一个新时代。在现实运用中&#xff0c;应用软件的工作规…

kafka-生产者监听器(SpringBoot整合Kafka)

文章目录 1、生产者监听器1.1、创建生产者监听器1.2、发送消息测试1.3、使用Java代码创建主题分区副本1.4、application.yml配置----v1版1.5、屏蔽 kafka debug 日志 logback.xml1.6、引入spring-kafka依赖1.7、控制台日志 1、生产者监听器 1.1、创建生产者监听器 package co…

鸿蒙开发接口安全:【@ohos.abilityAccessCtrl (访问控制管理)】

访问控制管理 说明&#xff1a; 本模块首批接口从API version 8开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import abilityAccessCtrl from ohos.abilityAccessCtrlabilityAccessCtrl.createAtManager createAtManager(): AtMan…

用户管理的小demo --登录:

目录 1、建库、建表 1.1 连接数据库后&#xff0c;在idea中 通过快捷方式 自动导入实体类 1.2 实体类代码 2、idea中的准备工作 2.1 在父工程下 新建子工程 2.2 在子工程下 添加webapp、pom.xml设置为 war的打包方式 2.3 在父工程下的pom.xml中 添加依赖 2.3.1 mysql的…