当前位置: 首页 > news >正文

解决n+1查询数据库问题

文章目录

  • 1. 问题描述
  • 2. 解决方法
  • 3. 总结

1. 问题描述

在写项目中,可能会碰到一个问题:通过查询表A得到一个list结果,再对list中的n个元素各查询一次关联的表B。形成对数据库执行n+1次查询。这种代码会无形增加数据库的处理负担,影响整体性能。

举例:给定categoryId,查询dish和dish_flavor相关信息(dish表和dish_flavor表存在冗余字段dishId)。表的实体类结构如下:

  • dish:

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @TableName("dish")
    public class Dish {@TableId(type = IdType.AUTO)private Long id;private String name;private Integer categoryId;
    }
    
  • dish_flavor:

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @TableName("dish_flavor")
    public class DishFlavor {@TableId(type = IdType.AUTO)private Long id;private Long dishId;private String name;private String value;
    }
    

2. 解决方法

  1. 批量查询
    先根据categoryId查询到所有的dish信息,然后提取所有dish的id,再根据dish_id批量查询得到dish_flavor信息,最后组合返回。

    • vo:
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      @Builder
      public class DishFlavorVO {private Long id;private String name;private Integer categoryId;private List<DishFlavor> flavors = new ArrayList<>();
      }
      
    • ServiceImpl:
      @Service
      @Slf4j
      public class DishServiceImpl implements DishService {@Autowiredprivate DishMapper dishMapper;/*** 方法 1:批量查询* @param categoryId* @return*/public List<DishFlavorVO> method1(Integer categoryId) {//1. 获取所有的dishesList<DishFlavorVO> dishFlavorVOS = dishMapper.getDishes(categoryId);//1.1 获取所有的dish_idList<Long> dishIds = dishFlavorVOS.stream().map(DishFlavorVO::getId).collect(Collectors.toList());//2.获得所有的dish_flavorList<DishFlavor> dishFlavors = dishMapper.selectFlavorsByDishIdList(dishIds);//3 聚合dish和dish_flavorMap<Long, List<DishFlavor>> flavorsMap = dishFlavors.stream().collect(Collectors.groupingBy(DishFlavor::getDishId));for (DishFlavorVO dishFlavorVO : dishFlavorVOS) {dishFlavorVO.setFlavors(flavorsMap.get(dishFlavorVO.getId()));}return dishFlavorVOS;}}
      
    • mapper:
      <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
      <mapper namespace="com.example.solution.mapper.DishMapper"><select id="getDishes" resultType="com.example.solution.pojo.vo.DishFlavorVO">select * from dish where category_id = #{categoryId}</select><!--批量查询--><select id="selectFlavorsByDishIdList" resultType="com.example.solution.pojo.entity.DishFlavor">select * from dish_flavorwhere dish_id in<foreach collection="dishIds" separator="," open="(" close=")" item="dishId">#{dishId}</foreach></select></mapper>
      
  2. join联表查询
    需要额外新建接收返回参数的类,再封装成返回给前端的vo。

    • item:
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class DishesAndFlavorsItem {private Long id;private String name;private Integer categoryId;private Long fDishId;private Long fId;private String fName;private String fValue;
      }
      
    • ServiceImpl:
      /*** 方法 2:联表查询* @param categoryId* @return*/
      public List<DishFlavorVO> method2(Integer categoryId){List<DishFlavorVO> dishFlavorVOS = new ArrayList<>();//1. 联表查询List<DishesAndFlavorsItem> dishes = dishMapper.getDishesAndFlavors(categoryId);//2. 组合dish和dish_flavorMap<Long, List<DishesAndFlavorsItem>> collect = dishes.stream().collect(Collectors.groupingBy(DishesAndFlavorsItem::getFDishId));for (Map.Entry<Long, List<DishesAndFlavorsItem>> entry : collect.entrySet()) {List<DishFlavor> dishFlavors = new ArrayList<>();Long dishId = entry.getKey();List<DishesAndFlavorsItem> dishesAndFlavorsItems = entry.getValue();for (DishesAndFlavorsItem dishesAndFlavorsItem : dishesAndFlavorsItems) {DishFlavor dishFlavor = new DishFlavor();dishFlavor.setId(dishesAndFlavorsItem.getFId());dishFlavor.setValue(dishesAndFlavorsItem.getFValue());dishFlavor.setName(dishesAndFlavorsItem.getFName());dishFlavor.setDishId(dishesAndFlavorsItem.getFDishId());dishFlavors.add(dishFlavor);}DishFlavorVO dishFlavorVO = new DishFlavorVO();dishFlavorVO.setFlavors(dishFlavors);dishFlavorVO.setId(dishId);}return dishFlavorVOS;
      }
      
    • mapper:
      <select id="getDishesAndFlavors" resultType="com.example.solution.pojo.item.DishesAndFlavorsItem">SELECTd.id as id,d.category_id as categoryId,df.dish_id as f_dishId,df.id as f_id,df.`name` as f_name,df.`value` as f_valueFROMdish as dLEFT JOINdish_flavor as df ON d.id = df.dish_idWHEREd.category_id = #{categoryId}
      </select>
      

3. 总结

可以通过批量查询或join联表查询来获取。使用批量查询需要分两次sql来查;使用join需要额外构建类来接收结果。如果效率需进一步提升,可使用redis缓存。

以上为个人学习分享,如有问题,欢迎指出:)

相关文章:

解决n+1查询数据库问题

文章目录 1. 问题描述2. 解决方法3. 总结 1. 问题描述 在写项目中&#xff0c;可能会碰到一个问题&#xff1a;通过查询表A得到一个list结果&#xff0c;再对list中的n个元素各查询一次关联的表B。形成对数据库执行n1次查询。这种代码会无形增加数据库的处理负担&#xff0c;影…...

DICOM 基础知识:深入理解DICOM数据结构与标签说明

目录 DICOM 图像概念 DICOM 图像关键特性&#xff1a; DICOM 文件结构 常见数据元素&#xff1a; 数据元素示例详解 DICOM-VR 数据类型说明 DICOM 标准支持的数据集 结语 DICOM 图像概念 DICOM&#xff08;Digital Imaging and Communications in Medicine&…...

Git - 如何删除 push 过一次的文件链路追踪?

&#xff08;以 target 文件夹为例&#xff09;如果你已经在 .gitignore 中添加了 target/ 目录&#xff0c;但 target 文件夹仍然出现在 Git 的变更列表中&#xff0c;可能是因为它之前已经被添加到 Git 仓库中。即使你更新了 .gitignore&#xff0c;Git 仍然会跟踪这些文件。…...

软件测试学习总结

一.软件测试概念和目的 软件测试的概念: 测试模型(V模型) 软件测试就是在软件投入运行前,对软件需求分析、设计规格说明和编码实现的最终审查,它是软件质量保证的关键步骤。 通常对软件测试的定义有两种描述: 定义1:软件测试是为了发现错误而执行程序的过程 定义2:…...

c语言错题——#define对应的查找替换

文章目录 一、题目 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、题目 分析 结构体向最长的char对齐&#xff0c;前两个位段元素一共42位&#xff0c;不足8位&#xff0c;合起来占1字节&#xff0c;最后一个单独1字节&#xff0c;一共3字节。另外…...

Visual Basic介绍及简单例子

Visual Basic(简称 VB)是一种由微软公司开发的包含协助开发环境的事件驱动编程语言。 一、主要特点 易于学习和使用: Visual Basic 具有直观的可视化开发环境,使用户可以通过拖放控件和设置属性的方式快速创建用户界面。对于初学者来说,这种方式非常容易上手,无需深入了…...

Matlab学习01-矩阵

目录 一&#xff0c;矩阵的创建 1&#xff0c;直接输入法创建矩阵 2&#xff0c;利用M文件创建矩阵 3&#xff0c;利用其它文本编辑器创建矩阵 二&#xff0c;矩阵的拼接 1&#xff0c;基本拼接 1&#xff09; 水平方向的拼接 2&#xff09;垂直方向的拼接 3&#xf…...

【复旦微FM33 MCU 外设开发指南】外设篇1——硬件除法器

前言 本系列基于复旦微FM33LC0系列单片机的DataSheet编写&#xff0c;旨在提供一些开发指南。 本文章及本系列其他文章将持续更新&#xff0c;本系列其它文章请跳转【复旦微FM33 MCU 外设开发指南】总集篇 本文章最后更新日期&#xff1a;2024/10/24 文章目录 前言用途工作流…...

在元神操作系统启动时自动执行任务脚本

1. 背景 本文主要介绍让元神操作系统启动时自动执行任务脚本的方法&#xff0c;适用于无人化任务执行目的。将任务脚本及相关的应用程序准备好之后&#xff0c;把装有元神操作系统的U盘插入目标电脑&#xff0c;然后打开电脑电源就会自动完成所设置的任务。 2. 方法 &#x…...

JAVA学习-练习试用Java实现“判断是否为等边三角形的方法”

问题&#xff1a; 定义一个三角形类&#xff08;Triangle&#xff09;&#xff0c;包含三个边长&#xff08;a, b, c&#xff09;属性&#xff0c;并实现一个判断是否为等边三角形的方法。 解答思路&#xff1a; 下面是一个简单的 Triangle 类定义&#xff0c;其中包含了三个…...

Leetcode 140 Word Break II

题意&#xff1a;给定一个string以及一个wordDict,要求返回一个vector<string> &#xff0c;这个vector中的string都是word Dict中的组合 Input: s “catsanddog”, wordDict [“cat”,“cats”,“and”,“sand”,“dog”] Output: [“cats and dog”,“cat sand dog”…...

文理学院数据库应用技术实验报告0

文理学院数据库应用技术实验报告0 实验内容 打开cmd,利用MySQL命令连接MySQL服务器。 mysql -u root -p查看当前MySQL服务实例使用的字符集(character)。 SHOW VARIABLES LIKE character_set_server;查看当前MySQL服务实例支持的字符序(collation)。 SHOW VARIABLES LIKE c…...

Bootstrap 4 按钮

Bootstrap 4 按钮 Bootstrap 4 是一个流行的前端框架,它提供了大量的组件和样式,用于快速开发响应式和移动设备优先的网页。在本文中,我们将重点讨论 Bootstrap 4 中的按钮组件,包括它们的基本用法、样式选项和自定义方法。 基本按钮 在 Bootstrap 4 中,创建一个基本按…...

【笔记】LLM位置编码之标准位置编码

标准位置编码 起源原理证明&#xff1a;对于任何固定的偏移量 k k k&#xff0c; P E p o s k PE_{posk} PEposk​可以表示为 P E p o s PE_{pos} PEpos​的线性函数。计算 P E p o s k 与 P E p o s PE_{posk} 与PE_{pos} PEposk​与PEpos​的内积结论 通俗理解缺点 起源 由…...

环 境 配 置

01 Ubuntu18.04中QT环境 1. 下载安装包 官网 http://download.qt.io/archive/qt/5.9/5.9.1/qt-opensource-linux-x64-5.9.1.run 国内镜像服务器 ​https://mirrors.tuna.tsinghua.edu.cn/qt/archive/qt/5.9/5.9.1/qt-opensource-linux-x64-5.9.1.run QQ群 ...... 2. 安装 把下载…...

理解dbt artifacts及其实际应用

dbt是数据分析领域的一种变革性工具&#xff0c;它使数据专业人员能够对仓库中的数据进行转换和建模。它的强大功能之一是生成dbt artifacts&#xff1a;dbt运行的结构化输出&#xff0c;提供对dbt项目及其操作的深入了解。 dbt 构件介绍 dbt构件是每次dbt运行时生成的JSON文…...

100种算法【Python版】第15篇——KMP算法

本文目录 1 算法原理1.1 部分匹配表2 实现步骤3 示例说明4 python实例5 算法应用领域1 算法原理 KMP(Knuth-Morris-Pratt)算法是一种用于高效字符串匹配的算法。它通过预处理模式字符串,构建一个部分匹配表(前缀函数),以避免重复比较,从而提高匹配效率。KMP 算法通过利…...

【软件工程】软件项目管理/工程项目管理复习资料

第一章 软件项目管理概述习题 一. 填空题 实现项目目标的制约因素有&#xff08; 项目范围 &#xff09;、&#xff08; 成本 &#xff09;、&#xff08; 进度计划 &#xff09;、&#xff08; 客户满意度 &#xff09;等。 项目管理&#xff08; 启动过程组 &#xff09;、…...

C语言基础题(大合集2)

1. 时间转换 给定秒数 --> 输出秒数 转化成 时/分/秒 //时间转换 //给定秒数 --> 转换成 小时/分/秒 int main() {//输入int seconds 0;int h 0;//小时int m 0;//分钟int s 0;//秒scanf("%d", &seconds);//计算h seconds / 60 / 60;m seconds / 60…...

Stable Diffusion视频插件Ebsynth Utility使用方法

在Stable Diffusion中安装完Ebsynth Utility后&#xff0c;就可以开始试用了。 启动Stable Diffusion后&#xff0c;出面画面&#xff1a; 1、步骤1&#xff1a;视频分帧及生成蒙板帧 填入工程目录&#xff0c;选择上传所用的视频文件&#xff1a;注意对目录命名的要求-不能有…...

uniApp离线打包实战避坑指南

1. 离线打包前的环境准备 第一次接触uniApp离线打包时&#xff0c;我踩过的第一个坑就是环境配置。当时以为只要安装了Android Studio就能万事大吉&#xff0c;结果编译时各种报错接踵而至。后来才发现&#xff0c;离线打包对开发环境的版本匹配要求极为严格&#xff0c;差一个…...

ArduPilot电机控制逻辑与PWM输出机制剖析

1. ArduPilot电机控制基础概念 当你第一次接触无人机飞控时&#xff0c;最让人困惑的莫过于电机控制逻辑了。想象一下&#xff0c;你手里拿着遥控器&#xff0c;轻轻推动摇杆&#xff0c;无人机就能平稳地上升、下降或者转向。这背后到底发生了什么&#xff1f;让我用最直白的…...

实战构建c盘清理桌面应用,快马ai生成可部署完整解决方案

今天想和大家分享一个实战项目&#xff1a;用Python开发一个C盘清理桌面应用。这个工具不仅能解决日常C盘空间不足的烦恼&#xff0c;还具备完整的图形界面和实用功能。最近在InsCode(快马)平台上尝试了快速生成和部署&#xff0c;整个过程特别顺畅。 项目背景与核心功能 开发这…...

YOLOv11分割模型实战:用C++和ONNXRuntime解析‘output0’和‘output1’双输出,实现像素级颜色分析

YOLOv11分割模型实战&#xff1a;C与ONNXRuntime双输出解析与像素级颜色分析 在计算机视觉领域&#xff0c;目标检测与实例分割技术的结合正成为工业应用的新标准。YOLOv11作为YOLO系列的最新成员&#xff0c;不仅延续了其高效检测的特性&#xff0c;更通过双输出结构实现了精准…...

华为MatePad 11鸿蒙2.0平板变身编程本:保姆级AidLux+VSCode配置避坑指南

华为MatePad 11鸿蒙平板编程环境搭建实战&#xff1a;AidLux与VSCode高效配置指南 在移动办公与碎片化学习成为主流的今天&#xff0c;将华为MatePad 11这样的高性能平板转变为便携式编程工作站&#xff0c;正成为越来越多开发者的现实需求。鸿蒙系统2.0的分布式能力与AidLux的…...

PyTorch张量拼接实战:torch.stack()与torch.cat()的5个典型场景对比

PyTorch张量拼接实战&#xff1a;torch.stack()与torch.cat()的5个典型场景对比 在深度学习项目中&#xff0c;数据维度的操作就像乐高积木的拼装——选错连接方式可能导致模型结构崩塌。作为PyTorch中高频使用的两种拼接操作&#xff0c;torch.stack()和torch.cat()常被混淆使…...

告别树莓派溢价!Radxa ROCK 5A 8GB版开箱实测,652元真香体验与避坑指南

652元平替树莓派4B&#xff1f;Radxa ROCK 5A深度体验与实战避坑手册 当树莓派4B的价格突破900元大关时&#xff0c;许多开发者开始寻找更具性价比的替代方案。Radxa ROCK 5A的出现恰逢其时——这款搭载RK3588S芯片的单板计算机不仅性能翻倍&#xff0c;价格却仅为652元&#…...

海康WEBSDK无插件版实战:零基础构建WEB端网络摄像机实时监控系统

1. 环境准备&#xff1a;5分钟搞定基础配置 第一次接触海康WEBSDK无插件版时&#xff0c;我也被那些专业术语吓到过。但实际操作后发现&#xff0c;只要准备好三样东西就能开工&#xff1a;一台能联网的电脑、海康网络摄像机、以及从官网下载的开发包。这里分享几个新手容易踩的…...

双摆控制系统:LQR、LQG、LQI控制器及龙伯格观测器文件清单

移动小车上双摆的LQR、LQG、LQI控制器和龙伯格观测器文件列表&#xff1a; LQG.m LQG_non_linear.m LQI.m LQR.m LQR_Non_linear.m Luenberger_observer.m Observer_non_linear.m 最近蹲在实验室的工位上啃移动小车双摆的控制代码&#xff0c;翻来覆去调了快两周&#xff0c;终…...

为什么你的Mojo-Python FFI在M1芯片上必崩?苹果Silicon专属ABI陷阱与跨架构符号绑定修复指南(含Clang插件源码)

第一章&#xff1a;为什么你的Mojo-Python FFI在M1芯片上必崩&#xff1f;Mojo-Python FFI&#xff08;Foreign Function Interface&#xff09;在 Apple M1 及后续 ARM64 架构芯片上崩溃&#xff0c;根源并非配置疏忽&#xff0c;而是底层 ABI 不兼容与运行时符号解析机制的双…...