MyBatis复杂查询——一对一、一对多

目录

(一)复杂查询:1对1关系

【任务】数据库里有学生表(student)和学生证信息表(student_card),表结构如下所示,要求使用MyBatis框架查询所有的学生信息以及每位学生的学生证信息

解决方案1:关联查询实现

定义实体类

定义接口

配置SQL并完成数据映射

执行SQL

解决方案2:嵌套查询实现

定义实体类和接口

配置SQL并完成数据映射

执行SQL

(二)复杂查询:1对多关系

【任务】数据库里有客户表(customer)和订单表(orders),表结构如下所示,要求用MyBatis框架查询所有的客户信息以及每位客户的订单信息

解决方案1:关联查询实现

定义实体类

定义接口

配置SQL并完成数据映射

执行SQL

解决方案2:嵌套查询实现


(一)复杂查询:1对1关系

在前面的课程中,我们已经学习了使用MyBatis框架实现单表的数据查询。在实际的开发中,我们遇到的需求更多的是需要多表进行关联查询获取需要的数据。

【任务】数据库里有学生表(student)和学生证信息表(student_card),表结构如下所示,要求使用MyBatis框架查询所有的学生信息以及每位学生的学生证信息

学生表(student)

学生证信息表(student_card)

【分析】

  • 题目中要求我们查出所有的学生以及学生证信息,因此需要涉及两张表的关联查询
  • 通过表结构分析,学生表和学生证表之间是1对1的关系,拥有相同的主键stu_id。
  • 两表关联查询的SQL语句如下:
select stu.*,
card.stu_id as card_stu_id,card.class_info,card.major 
from student stu 
LEFT JOIN student_card card 
on stu.stu_id=card.stu_id
  • 查询结果如下

【实现步骤】

  1. 定义实体类
  2. 定义接口
  3. 配置SQL并完成数据映射
  4. 执行SQL
解决方案1:关联查询实现
定义实体类

基于表结构创建两个实体类:学生类(Student)和学生证类(StudentCard),因为学生实体和学生证实体是1对1的关系,所以在学生类中加入一个学生证类型的属性,表示一个学生拥有一个学生证。

@Data
//学生证实体类
public class StudentCard {private int stuId;private String classInfo;private String major;
}
//学生实体类
@Data
public class Student {private int stuId;private String stuNo;private String stuName;//一个学生拥有一个学生证,体现1对1关系private StudentCard studentCard;
}
定义接口

在com.cg.mapper下新建一个接口StudentMapper,接口中加入方法

public interface StudentMapper {/*** 查询学生信息(包含学生证)* @return 学生信息列表*/List<Student> selectStudents();
}

配置SQL并完成数据映射

在src/main/resources/mapper下新建一个SQL映射文件StudentMapper.xml,完成SQL配置:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cg.mapper.StudentMapper"><!--定义查询结果和Student实体类映射关系--><resultMap id="studentCardMap" type="Student"><!-- 先设置Student自身属性和字段的对应关系 --><id column="stu_id" property="stuId"/><result column="stu_no" property="stuNo"/><result column="stu_name" property="stuName"/><!-- 使用association标签配置“一对一”关联关系,也就是实体类型属性的映射关系 --><!-- property属性:实体类型属性名 --><!-- javaType属性:实体类型属性全类名 --><association property="studentCard" javaType="StudentCard"><!-- 配置StudentCard类的属性和字段的对应关系 --><id column="card_stu_id" property="stuId"/><result column="class_info" property="classInfo"/><result column="major" property="major"/></association></resultMap><select id="selectStudents" resultMap="studentCardMap">select stu.*,card.stu_id as card_stu_id,card.class_info,card.majorfrom student stuLEFT JOIN student_card cardon stu.stu_id=card.stu_id</select>
</mapper>

注:

  • 一定要记得通过namespace关联接口
  • 使用association标签配置“一对一”关联关系,也就是实体类型属性的映射关系
  • 新建的SQL映射文件需要在全局配置文件中配置地址
<!--SQL映射文件地址配置--><mappers><mapper resource="mapper/StudentMapper.xml" /></mappers>
  • 配置优化:在以上配置中,很多属性名称和列都是符合驼峰命名规范的,这些属性和列的映射关系可以省略不写,但是需要在全局配置文件(mybatis-config.xml)中配置如下内容
<settings><!--配置控制台日志输出--><setting name="logImpl" value="STDOUT_LOGGING"/><!--开启驼峰式命名自动映射驼峰式命名--><setting name="mapUnderscoreToCamelCase" value="true"/><!--开启自动映射--><setting name="autoMappingBehavior" value="FULL"/></settings>

开启了MyBatis自动映射之后,<resultMap>标签的配置可以简化为如下:

<!--定义查询结果和Student实体类映射关系--><resultMap id="studentCardMap" type="Student"><!--通过association元素定义实体类型属性的映射关系--><association property="studentCard" javaType="StudentCard"><id column="card_stu_id" property="stuId"/></association></resultMap>
执行SQL

在测试方法中调用MyBatis API执行SQL,完成数据查询。

    @Testpublic void test1() throws IOException {//1.加载MyBatis核心配置文件InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");//2.获取SqlSessionFactory对象SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//3.获取SqlSession会话对象SqlSession sqlSession = sqlSessionFactory.openSession();//4.执行SQL配置StudentMapper studentMapper= sqlSession.getMapper(StudentMapper.class);List<Student> students = studentMapper.selectStudents();for (Student student : students) {System.out.println(student);}//6.关闭数据库会话sqlSession.close();}

控制台打印结果如下:

解决方案2:嵌套查询实现

【实现原理】嵌套查询就是将原来多表查询中的关联查询语句拆成单个表的查询,再使用MyBatis的语法嵌套在一起。结合本任务,首先执行主查询“select * from student”,查询出所有的学生信息列表,然后遍历每一条学生记录,根据学生信息的主键列(stu_id)的值执行子查询“select * from student_card where stu_id=?”查询出每位学生的学生证信息,然后将查询出的学生和学生证数据映射到到Student实体类对象的属性中完成数据映射。

定义实体类和接口

实体类和接口的设计内容和解决方案1是一致的。

配置SQL并完成数据映射

结合嵌套查询的实现原理可知,我们需要完成如下两个SQL配置(主查询和子查询),然后将他们关联起来。

  1. 主查询:select * from student
  2. 子查询:select * from student_card where stu_id=?

首先子查询的配置如下,子查询的查询结果需要和StudentCard建立映射关系。

<!--定义映射关系--><resultMap id="cardMap" type="StudentCard"><id column="stu_id" property="stuId"/><result column="class_info" property="classInfo"/><result column="major" property="major"/></resultMap><!--子查询配置,根据stuId查询学生证信息并映射到实体类(StudentCard)上--><select id="selectCardByStuId" resultMap="cardMap">select * from student_card where stu_id=#{stuId}</select>

然后配置主查询,在主查询映射关系配置中,需要在<association>标签中,通过select属性关联要执行的子查询的SQL配置id,通过column属性指定子查询所需参数的来源字段,来源于主查询结果中的stu_id字段的值。

<!--定义映射关系--><resultMap id="studentCardMap" type="Student"><!-- 先设置Student自身属性和字段的对应关系 --><id column="stu_id" property="stuId"/><result column="stu_no" property="stuNo"/><result column="stu_name" property="stuName"/><!-- 使用association标签配置实体类型属性的映射关系通过select关联子查询SQL,通过column指定子查询参数来源字段--><association property="studentCard"javaType="StudentCard"select="selectCardByStuId"column="stu_id"></association></resultMap><!--主查询配置,将查询结果映射到Student实体类上--><select id="selectStudents" resultMap="studentCardMap">select * from student</select>
执行SQL

调用MyBatis API执行接口方法,代码和解决方案1是一样的,控制台打印内容如下,从打印的内容可以看到,MyBatis底层首先执行了一次主查询SQL获取学生信息列表,然后遍历查询结果的每一条记录,根据记录stu_id字段的值执行子查询SQL获取学生证信息,从而完成数据的映射,因为有4个学生记录,因此嵌套查询共执行了1+4次select查询。

==>  Preparing: select * from student
==> Parameters: 
<==    Columns: stu_id, stu_no, stu_name
<==        Row: 5, 189000101, 张哲
====>  Preparing: select * from student_card where stu_id=?
====> Parameters: 5(Integer)
<====    Columns: stu_id, class_info, major
<====        Row: 5, 18级软工1班, 软件工程
<====      Total: 1
<==        Row: 6, 189000102, 王霜
====>  Preparing: select * from student_card where stu_id=?
====> Parameters: 6(Integer)
<====    Columns: stu_id, class_info, major
<====        Row: 6, 18级软工2班, 数字媒体技术
<====      Total: 1
<==        Row: 7, 189000103, 赵锋
====>  Preparing: select * from student_card where stu_id=?
====> Parameters: 7(Integer)
<====    Columns: stu_id, class_info, major
<====        Row: 7, 18级软工3班, 软件工程
<====      Total: 1
<==        Row: 8, 189000104, 李娜
====>  Preparing: select * from student_card where stu_id=?
====> Parameters: 8(Integer)
<====      Total: 0
<==      Total: 4
Student(stuId=5, stuNo=189000101, stuName=张哲, studentCard=StudentCard(stuId=5, classInfo=18级软工1班, major=软件工程))
Student(stuId=6, stuNo=189000102, stuName=王霜, studentCard=StudentCard(stuId=6, classInfo=18级软工2班, major=数字媒体技术))
Student(stuId=7, stuNo=189000103, stuName=赵锋, studentCard=StudentCard(stuId=7, classInfo=18级软工3班, major=软件工程))
Student(stuId=8, stuNo=189000104, stuName=李娜, studentCard=null)

(二)复杂查询:1对多关系

【任务】数据库里有客户表(customer)和订单表(orders),表结构如下所示,要求用MyBatis框架查询所有的客户信息以及每位客户的订单信息

客户表(customer)

订单表(orders)

【分析】

  • 题目中要求我们查出所有的客户以及客户的订单信息,因此需要涉及两张表的关联查询
  • 通过表结构分析,订单表通过外键customer_id与客户表关联,一个客户可以拥有多个订单,因此客户与订单是1对多的关系
  • 两表关联查询的SQL语句如下:
select c.*,o.id as order_id,o.order_no,o.price,o.customer_idfrom customer cleft join orders o on c.id=o.customer_id

【实现步骤】

  1. 定义实体类
  2. 定义接口
  3. 配置SQL并完成数据映射
  4. 执行SQL
解决方案1:关联查询实现
定义实体类

基于表结构创建两个实体类:客户类(Customer)和订单类(Order),因为客户实体和订单实体是1对多的关系,所以在客户类中加入订单类型的集合(List<Order>)属性,表示一个客户拥有多个订单。

@Data
//订单实体类
public class Order {private int id;private String orderNo;private BigDecimal price;private int customerId;
}
//客户实体类
@Data
public class Customer {private int id;private String account;private String username;//一个客户拥有多个订单,体现1对多关系private List<Order> orders;
}
定义接口

在com.cg.mapper下新建一个接口CustomerMapper,接口中加入方法

public interface CustomerMapper {/*** 查询所有的客户列表(包含客户的订单列表)* @return 客户列表*/List<Customer> selectCustomers();
}

配置SQL并完成数据映射

在src/main/resources/mapper下新建一个SQL映射文件CustomerMapper.xml,完成SQL配置:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cg.mapper.CustomerMapper"><!--定义查询结果和Customer实体类映射关系--><resultMap id="customerOrderMap" type="Customer"><!-- 先设置Customer自身属性和字段的对应关系 --><id column="id" property="id"/><result column="account" property="account"/><result column="username" property="username"/><!-- 使用collection标签配置“一对多”关联关系,也就是集合类型属性的映射关系 --><!-- property属性:集合类型属性名 --><!-- ofType属性:集合中的实体全类名 --><collection property="orders" ofType="Order"><!-- 配置Order实体类的属性和字段的对应关系 --><id column="order_id" property="id"/><result column="order_no" property="orderNo"/><result column="price" property="price"/><result column="customer_id" property="customerId"/></collection></resultMap><!--关联查询SQL配置--><select id="selectCustomers" resultMap="customerOrderMap">select c.*,o.id as order_id,o.order_no,o.price,o.customer_idfrom customer cleft join orders oon c.id=o.customer_id</select>
</mapper>

注:

  • 一定要记得通过namespace关联接口
  • 使用collection标签配置“一对多”关联关系,也就是集合类型属性的映射关系
  • 新建的SQL映射文件需要在全局配置文件中配置地址
<!--SQL映射文件地址配置--><mappers><mapper resource="mapper/CustomerMapper.xml" /></mappers>
执行SQL

在测试方法中调用MyBatis API执行SQL,完成数据查询。

@Testpublic void test2() throws IOException {//1.加载MyBatis核心配置文件InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");//2.获取SqlSessionFactory对象SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//3.获取SqlSession会话对象SqlSession sqlSession = sqlSessionFactory.openSession();//4.执行SQL配置CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);List<Customer> customers= customerMapper.selectCustomers();//5.处理结果for (Customer customer : customers) {System.out.println(customer);}//6.关闭数据库会话sqlSession.close();}

控制台打印结果如下,可以看到客户刘丹拥有两个订单,客户李义拥有1个订单,客户王伟拥有两个订单,客户张庆没有订单。

==>  Preparing: select c.*, o.id as order_id,o.order_no,o.price,o.customer_id from customer c left join orders o on c.id=o.customer_id
==> Parameters: 
<==    Columns: id, account, username, order_id, order_no, price, customer_id
<==        Row: 1, 13996065424, 刘丹, 1, 10001, 234.00, 1
<==        Row: 2, 13996068885, 李义, 2, 10002, 115.00, 2
<==        Row: 1, 13996065424, 刘丹, 3, 10003, 214.00, 1
<==        Row: 4, 13996063954, 王伟, 4, 10004, 321.00, 4
<==        Row: 4, 13996063954, 王伟, 5, 10005, 115.00, 4
<==        Row: 3, 13996062222, 张庆, null, null, null, null
<==      Total: 6
Customer(id=1, account=13996065424, username=刘丹, orders=[Order(id=1, orderNo=10001, price=234.00, customerId=1), Order(id=3, orderNo=10003, price=214.00, customerId=1)])
Customer(id=2, account=13996068885, username=李义, orders=[Order(id=2, orderNo=10002, price=115.00, customerId=2)])
Customer(id=4, account=13996063954, username=王伟, orders=[Order(id=4, orderNo=10004, price=321.00, customerId=4), Order(id=5, orderNo=10005, price=115.00, customerId=4)])
Customer(id=3, account=13996062222, username=张庆, orders=[])
解决方案2:嵌套查询实现

【练习】参考1对1关系任务中的解决方案2中嵌套查询的实现原理,思考本任务如何使用嵌套查询来实现?

【提示】结合本任务,首先执行主查询“select * from customer”,查询出所有的客户信息列表,然后遍历每一条客户记录,根据客户信息的主键列(id)的值执行子查询“select * from orders where customer_id=?”查询出每位客户的订单列表,然后将查询出的客户以及订单数据映射到到Customer实体类对象的属性中完成数据映射。

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

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

相关文章

【服务端】使用conda虚拟环境部署Django项目

写在开头 为了与客户端的Deep search配合&#xff0c;需要整一个后台管理来保存和管理deep search的数据资料。选择前端框架Vue-Vben-Admin Django后台服务来实现这个项目。 废话结束&#xff0c;从零开始。。。。 一、环境搭建 1. 安装 Anaconda 下载 Anaconda&#xff1…

Python爬虫-爬取大麦网演出详情页面数据

前言 本文是该专栏的第50篇,后面会持续分享python爬虫干货知识,记得关注。 本文,笔者以大麦网平台为例。基于Python,实现获取演出详情页面的演出信息。 废话不多说,具体实现思路和详细逻辑,笔者将在正文结合完整代码进行详细介绍。接下来,跟着笔者直接往下看正文详细内…

多onnx模型导出合并调研(文本检测+方向分类+文本识别)

👑主页:吾名招财 👓简介:工科学硕,研究方向机器视觉,爱好较广泛… ​💫签名:面朝大海,春暖花开! 多onnx模型合并导出调研(文本检测+方向分类+文本识别) 引言1,尝试合并两个模型(文本方向分类+文本识别模型)(并行合并)(1)文本方向分类(2)文本识别模型(…

Flink介绍——实时计算核心论文之S4论文详解

引入 在上一篇我们对Flink的发展历史有了全局的了解&#xff0c;下面我们会通读几篇分布式实时处理相关的重要论文&#xff0c;从S4到Storm&#xff0c;再从MillWheel到Dataflow&#xff0c;最后到Flink。 通过深入梳理分布式实时处理技术的发展脉络&#xff0c;了解这些年技…

【商城实战(97)】ELK日志管理系统的全面应用

【商城实战】专栏重磅来袭!这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建,运用 uniapp、Element Plus、SpringBoot 搭建商城框架,到用户、商品、订单等核心模块开发,再到性能优化、安全加固、多端适配,乃至运营推广策略,102 章内容层层递进。无论是想…

Linux系统-ls命令

一、ls命令的定义 Linux ls命令&#xff08;英文全拼&#xff1a;list directory contents&#xff09;用于显示指定工作目录下之内容&#xff08;列出目前工作目录所含的文件及子目录)。 二、ls命令的语法 ls [选项] [目录或文件名] ls [-alrtAFR] [name...] 三、参数[选项…

游戏被外挂攻破?金融数据遭篡改?AI反作弊系统实战方案(代码+详细步骤)

一、背景与需求分析 随着游戏行业与金融领域的数字化进程加速,作弊行为(如游戏外挂、金融数据篡改)日益复杂化。传统基于规则的防御手段已难以应对新型攻击,而AI技术通过动态行为分析、异常检测等能力,为安全领域提供了革命性解决方案。本文以游戏反作弊系统和金融数据安…

Node.js 路由 - 初识 Express 中的路由

目录 Node.js 路由 - 初识 Express 中的路由 1. 什么是路由&#xff1f; 2. 安装 Express 3. 创建 server.js 4. 运行服务器 5. 测试路由 5.1 访问主页 5.2 访问用户路由 5.3 发送 POST 请求 6. 结语 1. 什么是路由&#xff1f; 路由&#xff08;Routing&#xff09…

面经-项目

项目 项目(重点)问题1:描述在网页中题目点击提交后到题目结果出现的一系列后台反应【1】如何获取到用户提交的代码的?【2】_1. 题目细节都有哪些?【2】_2. 题目信息怎么存储的?【3】负载均衡算法的实现?【4】oj_server怎么连接对应的compile_server(编译主机)的?【5】oj_…

网络基本概念认识(2)

目录 前言&#xff1a; 局域网协议 MAC/IP地址 Socket编程 TCP/UDP 网络字节序 前言&#xff1a; 本文同样作为博主的二刷网络课程的文章&#xff0c;主要涵盖的主题还是网络基本概念的认识&#xff0c;从上一篇文章遗漏的点加上引入的一些知识点共同组成当前的知识点。…

Kafka中的消息是如何存储的?

大家好&#xff0c;我是锋哥。今天分享关于【Kafka中的消息是如何存储的&#xff1f;】面试题。希望对大家有帮助&#xff1b; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 Kafka 中&#xff0c;消息是通过 日志&#xff08;Log&#xff09; 的方式进行存储的。…

openEuler24.03 LTS下安装ZooKeeper集群

目录 前提条件 ZooKeeper集群规划 下载ZooKeeper 解压 配置环境变量 配置ZooKeeper 配置zoo.cfg 配置myid 分发到其他机器 修改其他机器myid 启动集群 关闭集群 集群启停脚本 前提条件 准备3台Linux机器&#xff0c;并安装好Java8 ZooKeeper集群规划 node2node3…

Python 实现机器学习小项目实战教程*

markdown 复制 Python 实现机器学习小项目实战教程 本教程将通过一个完整的机器学习项目案例&#xff0c;从数据预处理到模型部署&#xff0c;帮助初学者快速掌握机器学习核心流程。项目以经典的鸢尾花分类为例&#xff0c;使用 Scikit-learn 库实现。 1. 项目概述 目标 构…

rust学习笔记21-闭包

在 Rust 中&#xff0c;闭包&#xff08;Closures&#xff09; 是一种可以捕获其环境中的变量的匿名函数。它们非常灵活&#xff0c;既可以作为普通函数使用&#xff0c;也可以捕获和操作定义它们的作用域中的变量。闭包是 Rust 中处理短小逻辑代码块的强大工具&#xff0c;特别…

linux实现rsync+sersync实时数据备份

1.概述 rsync(Remote Sync) 是一个Unix/linux系统下的文件同步和传输工具 2.端口和运行模式 tcp/873 采用C/S模式&#xff08;客户端/服务器模式&#xff09; 3.特点 可以镜像保存整个目录和文件第一次全量备份(备份全部的文件),之后是增量备份(只备份变化的文件) 4. 数…

【第30节】MFC编程:ListCtrl控件和TreeCtrl控件

目录 引言 一、高级控件ListCtrl 二、高级控件TreeCtrl 三、Shell控件 四、CImageList 五、综合代码示例 引言 在MFC编程里&#xff0c;高级控件能大幅提升应用程序的交互性与功能性。接下来&#xff0c;咱们会详细讲讲ListCtrl和TreeCtrl这两个高级控件。不仅会介绍它们…

为什么 ThreadLocalMap 的 key 是弱引用 value是强引用

问题一&#xff1a;为什么 ThreadLocalMap 的 key 是弱引用&#xff1f; 【假设 Entry 的 key 是对 ThreadLocal 对象的强引用】&#xff1a;这个 Entry 又持有 ThreadLocal 对象和 value 对象的强引用。如果在其他地方都没有对这个 ThreadLocla 对象的引用了、然后在使用 Thr…

DeepSeek本地部署(linux)

一、下载并安装Ollama 1.下载Ollama Ollama官网:Ollama 点击"Download",会跳转至下载页面。 1.1在线下载安装 可复制此命令到Linux服务器进行在线下载,如下载速度过慢,可选择离线下载安装。 curl -fsSL https://ollama.com/install.sh | sh1.2离线下载安装 …

基于Halcon仿VM流程列表的执行效果

Halcon本身应用需要一定的门槛&#xff0c;但是也可以封装成类似VM简单易操作的样子 上期文章分享的是连线功能&#xff0c;本期分享数据传参 1&#xff0c;定义通用属性和方法 public class BaseModel {public HObject HInput { get; set; }//图像输入public HObject HOutpu…

打车APP订单系统逻辑梳理与实现

一、逻辑分析 打车 APP 订单系统是整个打车业务的核心&#xff0c;负责处理从乘客下单到行程结束的一系列流程&#xff0c;涉及乘客、司机和平台三方的交互。 乘客端 下单&#xff1a;乘客打开 APP&#xff0c;输入上车地点、目的地&#xff0c;选择车型等信息后提交订单。此时…