解决 MyBatis 一对多查询中,出现每组元素只有一个,总组数与元素数总数相等的问题

文章目录

  • 问题简述
  • 场景描述
  • 问题描述
  • 问题原因
  • 解决办法

问题简述

  笔者在使用 MyBatis 进行一对多查询的时候遇到一个奇怪的问题。对于笔者的一对多的查询结果,出现了这样的一个现象:原来每个组里有多个元素,查询目标是查询所查的组,以及每个组中的元素。但查询的结果却是变成了这样:每组元素变得只有一个,且总组数与元素数总数相等。举个例子,假设一共有 3 个组,每组 4 个元素。而现在的查询结果却是,显示出了 12 个组,每组 1 个元素。

场景描述

  笔者原来的表的情况比这要复杂很多,这里为了便于说明,简单抽象出这样一个情景。数据库中有很多用户(User),每个用户有他的好友分组(Folder),每个分组下面有该用户的好友(Contact)。现在需要查找这个用户所有的分组及好友,返回的数据结构需要是一个一个 List 分组,且一个分组中包含一个 List 好友。(List 指的是 Java 的一个内置的数据结构。)

  • User 表建表示例代码如下:

    CREATE TABLE User (id VARCHAR(64) NOT NULL,name VARCHAR(64) NOT NULL,# ...为了简化说明,此表省略其它字段...PRIMARY KEY (id)
    );
  • Folder 表建表示例代码如下:

    CREATE TABLE Folder (id VARCHAR(64) NOT NULL,userId VARCHAR(64) NOT NULL,name VARCHAR(64) NOT NULL,# ...为了简化说明,此表省略其它字段...PRIMARY KEY (userId, id),# 因为上面的是复合主键,所以自动创建的是联合索引,而其它表的外键引用需要的是单个索引INDEX idIndex (id),FOREIGN KEY (userId) REFERENCES User (id)
    );
  • Contact 表建表示例代码如下:

    CREATE TABLE Contact (id VARCHAR(64) NOT NULL,# 表示此联系人属于谁的好友userId VARCHAR(64) NOT NULL,# 表示此联系人对应 User 中的 idlinkedUserId VARCHAR(64) NOT NULL,folderId VARCHAR(64) NOT NULL,# ...为了简化说明,此表省略其它字段...PRIMARY KEY (userId, id),# 因为上面的是复合主键,所以自动创建的是联合索引,而其它表的外键引用需要的是单个索引INDEX idIndex (id),# 同一个用户,不能拥有两个相同 ID 的 ContactUNIQUE (userId, linkedUserId),# 当复合主键成为外键时,必须整个复合主键一起作为外键,不能只引用复合主键其中的某个属性FOREIGN KEY (userId) REFERENCES Folder (userId),FOREIGN KEY (folderId) REFERENCES Folder (id),FOREIGN KEY (linkedUserId) REFERENCES User (id)
    );
    
  • 建表示意图如下:

    在这里插入图片描述

    在这里插入图片描述

  • 查询之后的 Java 数据结构如下:

    @Getter
    @Setter
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @EqualsAndHashCode(callSuper = false)
    @Accessors(chain = true)
    public class FolderWithContacts {private Folder folder;private List<Contact> contacts;
    }
    

    其中,

    @Setter
    @Getter
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @EqualsAndHashCode(callSuper = false)
    @Accessors(chain = true)
    public class Folder {private String id;private String userId;private String name;
    }
    
    @Setter
    @Getter
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @EqualsAndHashCode(callSuper = false)
    @Accessors(chain = true)
    public class Contact {private String id;private String userId;private String linkedUserId;private String folderId;
    }
    
  • DAO 类代码如下:

    public interface ContactDao {List<FolderWithContacts> getFolderWithContacts(String userId);
    }
    
    <?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="XXX.xxx.ContactDao"><resultMap id="folderResultMap" type="XXX.xxx.Folder"><!-- property 指的是 Java 的字段名,column 指的是 SQL 的属性名 --><id property="id" column="folder_id"/><id property="userId" column="folder_user_id"/><result property="name" column="folder_name"/></resultMap><resultMap id="contactResultMap" type="XXX.xxx.Contact"><id property="id" column="contact_id"/><id property="userId" column="contact_user_id"/><result property="folderId" column="contact_folder_id"/><result property="name" column="contact_name"/></resultMap><resultMap id="folderWithContactsResultMap" type="XXX.xxx.FolderWithContacts"><!-- association 表示这是一个普通 Java 对象,而不是 Java 内置类型 --><association property="folder" resultMap="folderResultMap"/><!-- collection 表示这是一个 Java 集合。javaType 指的是 Java 集合的类型 --><collection property="contacts" javaType="java.util.ArrayList" resultMap="contactResultMap"/></resultMap><select id="getFolderWithContacts" resultMap="folderWithContactsResultMap">SELECT Folder.id           AS folder_id,Folder.userId       AS folder_user_id,Folder.name         AS folder_name,Folder.sequence     AS folder_sequence,Contact.id          AS contact_id,Contact.userId      AS contact_user_id,Contact.folderId    AS contact_folder_id,Contact.name        AS contact_name,Contact.description AS contact_descriptionFROM Contact,FolderWHERE Contact.folderId = Folder.idAND Contact.userId = #{userId}AND Folder.userId = #{userId}ORDER BY folder_sequence ASC</select></mapper>
    

问题描述

  以上就是笔者用于某个用户的好友分组及每个分组下的好友的示例代码。但使用上面的代码的查询会出现问题。如果一个用户有 3 个好友,每组 4 个好友,则上述代码的查询结果会变成,该用户有 12 个好友分组,每个分组 1 个好友。而且,上面的整个查询过程在运行中都是正常的,不会发生报错。而且返回结果的每个字段都没有出现 null 值。

  可以看出,上面的代码会导致无法区分好友与分组,把好友当成分组返回了。

问题原因

  是什么原因出现上述问题呢?由于上面的整个查询过程都没有发生报错,且返回数据没有 null 值。因此不会是笔者的语法编写出现问题。

  于是,笔者将上面的 SQL 单独在 MySQL 客户端命令行运行了一下,运行输出是正常的,确实是一个一对多查询的输出。一个一对多查询的输出,输出结果的数量应该和元素总个数相等,且同一个分组的所有元素关于这个分组的属性列的值应该也都是相等的

  这就说明并不是笔者 SQL 代码的问题,所以问题出现在 MyBatis 对 MySQL 输出结果的解析上。笔者非常确定,MyBatis 是肯定支持一对多查询的,因此一定是笔者关于 MyBatis 的 mapper 文件的编写出现问题。

  笔者之后在不断地建新的更基本的表,进行一对多查询,终于让笔者发现了问题所在。

  MyBatis 对于多表查询,要求组元素的字段必须是基本类型,而笔者编程时非常喜欢隔离、封装、解耦,擅自在上面将组元素的字段封装成了一个单独的类,然后把这个类的对象作为组元素的字段。在这种情况下,虽然 MyBatis 注入数据没有出问题,但它却没能识别出这是一对多查询的数据,因此将其当成一对一的数据来注入了。

  可以看出,笔者在上面使用了 <association.../> 来映射一个 Java 对象,因此引发了上述问题。

解决办法

  知道原因就好办了。可以直接将上面类 Folder 的字段合并在类 FolderWithContacts,然后去掉类 Folder。

  改进后的相关代码如下:

@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class FolderWithContacts {private String id;private String userId;private String name;private List<Contact> contacts;
}
<?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="XXX.xxx.ContactDao"><resultMap id="contactResultMap" type="XXX.xxx.Contact"><id property="id" column="contact_id"/><id property="userId" column="contact_user_id"/><result property="folderId" column="contact_folder_id"/><result property="name" column="contact_name"/></resultMap><resultMap id="folderWithContactsResultMap" type="XXX.xxx.FolderWithContacts"><id property="id" column="folder_id"/><id property="userId" column="folder_user_id"/><result property="name" column="folder_name"/><!-- collection 表示这是一个 Java 集合。javaType 指的是 Java 集合的类型 --><collection property="contacts" javaType="java.util.ArrayList" resultMap="contactResultMap"/></resultMap><select id="getFolderWithContacts" resultMap="folderWithContactsResultMap">SELECT Folder.id           AS folder_id,Folder.userId       AS folder_user_id,Folder.name         AS folder_name,Folder.sequence     AS folder_sequence,Contact.id          AS contact_id,Contact.userId      AS contact_user_id,Contact.folderId    AS contact_folder_id,Contact.name        AS contact_name,Contact.description AS contact_descriptionFROM Contact,FolderWHERE Contact.folderId = Folder.idAND Contact.userId = #{userId}AND Folder.userId = #{userId}ORDER BY folder_sequence ASC</select></mapper>

  现在,这段代码运行起来,查询到的数据就是正常的了。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue使用高德地图轨迹活动效果demo(整理)

在html页面引入您自己的key <script language"javascript" src"https://webapi.amap.com/maps?v1.4.15&key6b26c2c58770d13a4ecf2b96615dbaee"></script><template><div class"index"><div id"amapContain…

小程序-uni-app:将页面(html+css)生成图片/海报/名片,进行下载 保存到手机

一、需要描述 本文实现&#xff0c;uniapp微信小程序&#xff0c;把页面内容保存为图片&#xff0c;并且下载到手机上。 说实话网上找了很多资料&#xff0c;但是效果不理想&#xff0c;直到看了一个开源项目&#xff0c;我知道可以实现了。 本文以开源项目uniapp-wxml-to-can…

Kotlin中的比较运算符

在Kotlin中&#xff0c;我们可以使用比较运算符进行值的比较和判断。下面对Kotlin中的等于、不等于、小于、大于、小于等于和大于等于进行详细介绍&#xff0c;并提供示例代码。 等于运算符&#xff08;&#xff09;&#xff1a; 等于运算符用于判断两个值是否相等。如果两个值…

Leetcode刷题详解——长度最小的子数组

1. 题目链接&#xff1a;209. 长度最小的子数组 2. 题目描述&#xff1a; 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl1, ..., numsr-1, numsr] &#xff0c;并返回其长度**。**如果不…

纽交所上市公司安费诺宣布将以1.397亿美元收购无线解决方案提供商PCTEL

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;纽交所上市公司安费诺(APH)宣布将以每股7美元现金&#xff0c;总价格1.397亿美元收购无线解决方案提供商PCTEL(PCTI)。 该交易预计将在第四季度或2024年初完成。 Lake Street Capital Markets担任…

python打开.npy文件的常见报错及解决

import numpy as npdata np.load("texture_data_256.npy") print(data) 解决办法&#xff1a; import numpy as npdata np.load("texture_data_256.npy",allow_pickleTrue) print(data) 再次运行后出现乱码&#xff01;&#xff01;&#xff01; 由于…

猿创征文|分布式国产数据库 TiDB 从入门到实战

写在前面 本文讲解的是目前欢迎程度最高分布式国产数据库 TiDB&#xff0c;详细讲解了 TiDB 的由来、架构、SQL 基本操作、SpringBoot 整合 TiDB 等内容。 目录 写在前面一、概述二、与 MySQL 兼容性对比三、安装使用四、SQL 基本操作4.1、库操作4.2、表操作4.3、索引操作4.4、…

【网络协议】聊聊ifconfig

我们知道在linux是ifconfig查看ip地址&#xff0c;但是ip addr也可以查看 IP 地址是一个网卡在网络世界的通讯地址&#xff0c;相当于我们现实世界的门牌号码。 从IP地址的划分来看&#xff0c;C类地址只可以容纳254个&#xff0c;而B类6W多&#xff0c;那么又没有一种折中的…

【Python数据分析工具】

文章目录 概要整体架构流程技术名词解释 概要 数据分析是一种通过收集、处理、分析和解释大量数据&#xff0c;以发现有价值信息、洞察趋势、制定决策并解决问题的过程。在现代科技和互联网的推动下&#xff0c;数据分析变得日益重要。它不仅仅是对数字和图表的简单解释&#…

【Hello Algorithm】暴力递归到动态规划(四)

动态规划的数组压缩技巧 - 机器人走格子问题 题目是leetcode62题目原题 表示如下 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中…

牛客:FZ12 牛牛的顺时针遍历

FZ12 牛牛的顺时针遍历 文章目录 FZ12 牛牛的顺时针遍历题目描述题解思路题解代码 题目描述 题解思路 通过一个变量来记录当前方向&#xff0c;遍历矩阵&#xff0c;每次遍历一条边&#xff0c;将该边的信息加入到结果中 题解代码 func spiralOrder(matrix [][]int) []int {…

“Flex弹性布局、轮播图mock遍历数据和首页布局解析与实践“

目录 引言1. Flex弹性布局介绍及使用什么是Flex弹性布局&#xff1f;Flex容器与Flex项目Flex属性详解 2. 轮播图mock遍历数据简述轮播图的作用和意义处理mock数据的重要性使用Mock模拟数据遍历 3. 首页布局总结 引言 在现代网页开发中&#xff0c;灵活性和响应式布局是至关重要…

网络安全—小白自学笔记

1.网络安全是什么 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一、是市场需求量高&#xff1b; 二、则是发展相对成熟…

个人博客系统的总结

个人博客系统 1、项目背景&#xff1a; 个人博客系统的兴起和发展是与信息技术和互联网的迅猛发展密切相关的。随着互联网的普及和数字化时代的到来&#xff0c;越来越多的人开始使用互联网平台来表达自己的观点、分享知识和展示个人创作。个人博客系统作为一种在线的个人信息…

计算机网络-计算机网络体系结构-数据链路层

目录 *一、组帧 1.1字符计数法 1.2字符填充法 1.3零比特填充法 1.4违规编码 *二、差错控制 2.1检错编码 2.2.1奇偶校验码 2.2.2 CRC循环冗余码 2.2纠错编码-海明码 *三、流量控制和可靠传输机制 流量控制 停止-等待协议 ​编辑 后退n帧协议的滑动窗口(GBN) 选择…

Mac下通过nvm管理node

背景 本地有两个项目&#xff0c;老项目需要用到node 14&#xff0c;新项目需要用node 16&#xff0c;所以只能通过nvm来管理node了 卸载原始的node 我的node是通过官网的.pkg文件安装的&#xff0c;可以通过以下命令进行删除 sudo rm -rf /usr/local/{bin/{node,npm},lib/…

阿里云2023年双十一优惠活动整理

随着双十一的临近&#xff0c;阿里云也为大家准备了一系列优惠活动。作为国内知名的云服务提供商&#xff0c;阿里云在双十一期间推出了多种优惠政策和福利&#xff0c;让用户在享受优质云服务的同时&#xff0c;也能节省一些费用。本文将对阿里云双十一优惠活动进行详细整理&a…

项目经理每天,每周,每月的工作清单

很多不懂项目管理的伙伴问&#xff0c;项目经理每天每周每个月的工作是什么呢&#xff1f; 仿佛他们什么都管&#xff0c;但是又没有具体的产出&#xff0c;但是每天看他们比谁都忙&#xff0c;其实很简单&#xff0c;项目中的每个环节负责具体的事情&#xff0c;但是每个环节…

Nginx:动静分离(示意图+配置讲解)

示意图&#xff1a; 动静分离 动静分离是指将动态内容和静态内容分开处理的一种方式。通常&#xff0c;动态内容是指由服务器端处理的&#xff0c;例如动态生成的网页、数据库查询等。静态内容是指不需要经过服务器端处理的&#xff0c;例如图片、CSS、JavaScript文件等。通过…