07 MyBatis之高级映射 + 懒加载(延迟加载)+缓存

1. 高级映射

例如有两张表, 分别为班级表和学生表
自然, 一个班级对应多个学生

像这种数据 , 应该如果如何映射到Java的实体类上呢? 这就是高级映射解决的问题

以班级和学生为例子 , 因为一个班级对应多个学生 , 因此学生表中必定有一个班级编号字段cid

但我们在学生的实体类中不需要加入这个字段, 而是通过另一个方法实现一对多映射/多对一映射

1.1 高级映射的分类

  1. 关联映射(Association Mapping)
    关联映射用于处理对象之间的一对一关系。例如,一个订单对象可能包含一个关联的客户对象。通过使用 MyBatis 的关联映射,你可以在查询订单的同时,自动填充每个订单所关联的客户信息。

  2. 集合映射(Collection Mapping)
    集合映射用于处理一对多关系。例如,一个客户可能有多个订单。在 MyBatis 中,你可以定义映射规则来自动将客户的所有订单作为一个集合属性加载到客户对象中。

1.2 前置知识

如何区分主表和副表?
原则: 谁在前谁就是主表

例如
多对一: 多(学生)在前, 多(学生)就是主表
一对多: 一(班级)在前, 一(班级)就是主表

1.2 多对一关系的实现

首先 既然是多(学生)对一(班级)关系 , 此时学生是主表(主对象),

那么学生实体类中应当加入班级对象的声明

private Clazz clazz;

1.2.1 多对一映射的第一种方式 一条SQL语句 , 级联属性映射

仅在studentMapper接口中声明一个方法

	<!--id为"studentResultMap"的resultMap的数据 , 按照以下规则映射到实体上 -->
<resultMap id="studentResultMap" type="student"/><!--主键映射 -->
<id property="sid" column="sid"/>
<result property="sname" column="sname"/><!--嵌套的班级对象映射 -->
<result property="clazz.cid" column="cid"/>
<result property="clazz.cname" column="cname"/>
</resultMap><!--id为"selectById"的查询语句, 查询结果放到id为"studentResultMap"的resultMap中  -->
<select id="selectById" resultMap="studentResultMap">selects.sid,s.sname,c.cid,c.cnamefrom <!--多表连接, 主表在前 -->t_student s left join t_clazz c on s.cid= c.cidwheres.sid={sid}  
</select>

测试

sout(student.getSid());
sout(student.getClazz().getCid());
sout(student);

1.2.2 多对一映射的第二种方式 , 一条SQL语句 , 采用association标签

在StudentMapper中定义一个新接口方法

Student selectByIdAssociation(Integer id);
	<!--id为"studentResultMapAssociation"的resultMap的数据 , 按照以下规则映射到实体上 -->
<resultMap id="studentResultMapAssociation" type="student"/><!--主键映射 -->
<id property="sid" column="sid"/>
<result property="sname" column="sname"/><!--班级属性映射采用Association标签. 一个Student对象关联一个Clazz对象
property指定映射的具体对象  -->
<association property="clazz" javaType="com.sunsplanter.pojo.Clazz"><id property="cid" column="cid"/><result property="cname" column="cname"/>	
</association></resultMap><!--id为"selectByIdAssociation"的查询语句, 查询结果放到id为"studentResultMapAssociation"的resultMap中 -->
<select id="selectByIdAssociation" resultMap="studentResultMapAssociation">selects.sid,s.sname,c.cid,c.cnamefrom <!--多表连接, 主表在前 -->t_student s left join t_clazz c on s.cid= c.cidwheres.sid={sid}  
</select>

1.2.3 多对一映射的第三种方式 两条SQL语句 ,分步查询

常用 优点是可复用, 且支持懒加载

基本思路是: 既然是多对一, 那么先查询多(学生)的信息, 从中拿到cid , 然后再用cid另外查询一次班级表

两条SQL语句自然要有两个接口方法 ,分别位于StudentMapper和ClazzMapper中

public interface StudentMapper{//分布查询的第一步, 先根据学生的sid查出学生信息 Student selectByIdStep1(Integer sid);}public interface ClazzMapper{//分布查询的第一步, 先根据学生的sid查出学生信息 Clazz selectByIdStep2(Integer cid);}

两个mapperxml文件分别为

<!--id为"studentResultMapAssociation"的resultMap的数据 , 按照以下规则映射到实体上 -->
<resultMap id="studentResultMapByStep" type="com.sunsplanter.pojo.Student"/>
<!--主键映射-->
<id property="sid" column="sid"/>
<result property="sname" column="sname"/><!--班级属性映射采用Association标签. 一个Student对象关联一个Clazz对象
property指定映射的具体对象 -->
<association property="clazz" ><id property="cid" column="cid"/><!--指定第二步SQL语句的ID --><!--将cid字段作为查询传入第二步SQL语句--><select="com.sunsplanter.mapper.ClazzMapper.selectByIdStep2"><column="cid">
</association>
</resultMap><select id="selectByIdStep1"  resultMap="studentResultMapByStep">selectsid, sname cid  from  t_studen where sid = #{sid}
</select>
<!--由于查询的结果与实体属性完全一致, 不需要再写resultMap标签进行结果映射--><select id="selectByIdStep2" resultType="com.sunsplanter.pojo.Clazz">selectcid,cname  from  t_clazz where cid = #{cid}
</select>

测试结果: 可以发现确实是先查了学生的信息得到cid, 再以cid去查班级信息 ,最终拼接起来输出的
在这里插入图片描述

1.2.4 多对一的懒加载

表连接里有一个概念叫笛卡尔积.

越多的表越多的匹配次数.

通过在association标签中增加fetchType="lazy"属性来开启懒加载

或在mybati config文件中的全局的setting标签中开启

<settings>
<setting name="lazyLoadingEnabled" value="true/">
</settings>

实际开发中往往是这样:

先在全局开启懒加载 , 对于特定需要全部加载的语句
通过在association标签中增加fetchType="eager"属性来关闭懒加载

1.2.5 测试

在上例中 , 假如我们没有开启懒加载

此时我们只需要完整的学生信息.
sout(Student);
仍会执行两条语句, 查询两张表, 即使根本没用到第二张表

开启懒加载后 , 便只会执行第一条selectByIdStep1的SQL语句

1.3 一对多关系的实现

需求: 根据班级ID查询指定班级下的所有学生信息

一(班级)对多(学生) , 因此班级是主表

一对多的实现 ,通常是在一(班级)的一方声明一个List集合属性
在班级类中增加

private List<Student> stus;

1.3.1 一对多映射的第一种实现 collection

与多对一并无本质区别, 核心是resultMap标签中的association标签换为collection标签

<!--id为"clazzResultMap"的resultMap的数据 , 按照以下规则映射到实体上 -->
<resultMap id="clazzResultMap" type="com.sunsplanter.pojo.clazz"/>
<!--主键映射-->
<id property="cid" column="cid"/>
<result property="cname" column="cname"/><!-- property属性指定Clazz实体类中定义的List的名称 , ofType指定Clazz实体类中定义的List中的存储对象-->
<collection property="stus" ofType="com.sunspalnter.pojo.Student" ><id property="sid" column="sid"/><result property="sname" column="sname"/>
</collection>
</resultMap>

测试结果, 可以看到班级表(主表)左外连接学生表
在这里插入图片描述

1.3.2 一对多映射的第一种实现 分步查询

<!--id为"clazzResultMapStep"的resultMap的数据 , 按照以下规则映射到实体上 -->
<resultMap id="clazzResultMapStep" type="com.sunsplanter.pojo.clazz"/>
<!--主键映射-->
<id property="cid" column="cid"/>
<result property="cname" column="cname"/><!-- property属性指定Clazz实体类中定义的List的名称 , ofType指定Clazz实体类中定义的List中的存储对象-->
<collection property="stus" <!--select指定第二步的SQL语句ID , column指定将cid字段作为参数传入第二步-->select="com.sunsplanter.mapper.StudentMapper.selectByCidStep2"	column="cid"/>
</resultMap><select id="selectByIdStep1" resultMap="clazzResultMapStep">selectcid,cname  from  t_clazz where cid = #{cid}
</select>
<select id="selectByCidStep2" resultType="com.sunsplanter.pojo.Student">selectsid,sname  from  t_student where sid = #{sid}
</select>

测试结果
在这里插入图片描述

1.4 多对多和一对一

多对多实际就是分解为两个一对多


2. 缓存

缓存的作用: 通过减少IO的方式,来提高程序的执行效率

常用的缓存技术有: 字符串常量池 , 整型数常量池 , 线程池 , 连接池

mybats的缓存存:将select语的查询结果放到到缓存(内存)
下次还是这条select的话,直接从缓存(内存)中取,不再从外存中查.

mybatis缓存包括:

  • 一级缓存:将查询到的数据存储到SqlSession中。
  • 二级缓存:将查询到的数据存储到SqlSessionFactory中 , 缓存空间更大
  • 或者集成其它第三方的缓存: 比如EhCache[Java语言开发的]、Memcache[C语言开发的]

缓存只针对于DQL语句,也就是说缓存机制只对应select语句.

2.1 一级缓存

一级缓存默认是开启的。不需要做任何配置

2.1.1 一级缓存生效

原理:只要使用同一个SqlSession对象执行同一条SQL语句,就会走缓存

可以看到 ,当使用同一个sqlSession对象执行相同的SQL语句时, 后台实际只执行了一次, 却输出了两条结果
在这里插入图片描述
在这里插入图片描述

2.1.1 一级缓存失效

  • sqlSession对象不是同一个肯定不走缓存
  • 查询条件不一样肯定不走缓存
  • 即使上述同时两个条件, 如果在第一次DQL和第二次DQL之间发生以下两件事情的任意一件, 会令缓存清空
    a. 执行了sqlSession的clearCache()方法 , 这会手动清空一级缓存
    b. 执行了INSERT/DELETE/UPDATE任意一个语句时 , 不管是操作哪张表 都会直接清空一级缓存(思想是避免修改了数据后 , 输出缓存中的假数据)

2.2 二级缓存

2.2.1 二级缓存生效

使用二级缓存必须同时具备以下条件

2.2.1 二级缓存失效

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

HarmonyOS学习--三方库

文章目录 一、三方库获取二、常用的三方库1. UI库&#xff1a;2. 网络库&#xff1a;3. 动画库&#xff1a; 三、使用开源三方库1. 安装与卸载2. 使用 四、问题解决1. zsh: command not found: ohpm 一、三方库获取 在Gitee网站中获取 搜索OpenHarmony-TPC仓库&#xff0c;在t…

Day20_网络编程(软件结构,网络编程三要素,UDP网络编程,TCP网络编程)

文章目录 Day20 网络编程学习目标1 软件结构2 网络编程三要素2.1 IP地址和域名1、IP地址2、域名3、InetAddress类 2.2 端口号2.3 网络通信协议1、OSI参考模型和TCP/IP参考模型2、UDP协议3、TCP协议 2.4 Socket编程 3 UDP网络编程3.1 DatagramSocket和DatagramPacket1、Datagram…

强大的文本绘图——PlantUML

PlantUML是一款开源工具&#xff0c;它允许用户通过简单的文本描述来创建UML图&#xff08;统一建模语言图&#xff09;。这种方法可以快速地绘制类图、用例图、序列图、状态图、活动图、组件图和部署图等UML图表。PlantUML使用一种领域特定语言&#xff08;DSL&#xff09;&am…

PostMan使用自带js库base64编码、sha256摘要、环境变量的使用

目录 1、环境变量的使用2、base64编码、sha256摘要、以及脚本的使用3、脚本代码 在请求调试接口的过程中&#xff0c;因为要使用大量相同的参数&#xff0c;使用变量的方式能很大程度上减轻接口调用的工作量 版本说明&#xff1a;Postman for Windows&#xff0c;Version&#…

BUGKU-WEB 备份是个好习惯

题目描述 题目截图如下&#xff1a; 进入场景看看&#xff1a; 解题思路 看源码看提示&#xff1a;备份是个好习惯扫描目录md5弱比较 相关工具 御剑md5解密&#xff1a;https://www.somd5.com/ 解题步骤 看到的这串字符&#xff0c;有点像md5&#xff1f; d41d8cd98…

【PCL】(十二)使用ConditionalRemoval或RadiusOutlierRemoval滤波器对点云进行滤波

&#xff08;十二&#xff09;使用ConditionalRemoval 或 RadiusOutlierRemoval滤波器对点云进行滤波 RadiusOutlierRemove滤波器删除PointCloud中在指定半径的邻域范内&#xff0c;邻点没能达到指定数量的点。下图中&#xff0c;如果指定了邻点数为1&#xff0c;则黄色点将从…

亿道丨三防平板电脑厂家推荐丨三防平板PAN智能化赋能

随着科技的不断进步和人们对智能化产品的需求日益增长&#xff0c;三防平板迎来了智能化赋能的时代。通过融合创新科技&#xff0c;三防平板实现了更高的性能、更智能的功能以及更广泛的应用场景&#xff0c;引领着未来的发展潮流。 一、智能化技术提升性能 随着技术的进步&…

Nginx网络服务四-----日志、Nginx压缩和ssl

1.自定义访问日志 如果访问出错---404&#xff0c;可以去看error.log日志信息 访问日志是记录客户端即用户的具体请求内容信息&#xff0c;而在全局配置模块中的error_log是记录nginx服务器运行时的日志保存路径和记录日志的level&#xff0c;因此两者是不同的&#xff0c;而且…

openssl 生成nginx自签名的证书

1、命令介绍 openssl req命令主要的功能有&#xff0c;生成证书请求文件&#xff0c; 查看验证证书请求文件&#xff0c;还有就是生成自签名证书。 主要参数 主要命令选项&#xff1a; -new :说明生成证书请求文件 -x509 :说明生成自签名证书 -key :指定已…

计算机网络面经-从浏览器地址栏输入 url 到显示主页的过程?

大概的过程比较简单&#xff0c;但是有很多点可以细挖&#xff1a;DNS解析、TCP三次握手、HTTP报文格式、TCP四次挥手等等。 DNS 解析&#xff1a;将域名解析成对应的 IP 地址。TCP连接&#xff1a;与服务器通过三次握手&#xff0c;建立 TCP 连接向服务器发送 HTTP 请求服务器…

JS基础(一)

一 JS概述 1. 历史 1995年 JS出现在网景浏览器中 1996年 IE也开始出现了JS 1997年 指定了JS的标准规范ECMAScript 目前是ES6 2009年 JS开始向后端发展&#xff0c;出现了Node.js 二 JS入门 1. JS的运行方式 1. 内部写法 <script>J…

查看navicat保存的数据库连接密码

背景 经常使用navicat的朋友可能会碰到忘记数据库连接密码的情况&#xff0c;自然会想到navicat连接配置中就保存了密码。 个人经验&#xff0c;按以下步骤可查看密码明文 本人在mac上使用的navicat版本 1&#xff0c;导出connection_local.ncx 点击OK导出保存为connection_l…

iOS面试:4.多线程GCD

一、多线程基础知识 1.1 什么是进程&#xff1f; 进程是指在系统中正在运行的一个应用程序。对于电脑而已&#xff0c;你打开一个软件&#xff0c;就相当于开启了一个进程。对于手机而已&#xff0c;你打开了一个APP&#xff0c;就相当于开启了一个进程。 1.2 什么是线程&am…

【电子书】人工智能

资料 wx&#xff1a;1945423050&#xff0c;备注来源和目的 个人整理了一些互联网电子书 人工智能 Julia机器学习核心编程&#xff1a;人人可用的高性能科学计算.epubKeras深度学习实战.epubMATLAB图像与视频处理实用案例详解.epubMATLAB金融算法分析实战&#xff1a;基于机器…

Java学习笔记2024/2/23

今日内容 抽象类 接口 内部类 教学目标 能够写出抽象类的格式能够写出抽象方法的格式能说出抽象类的应用场景写出定义接口的格式写出实现接口的格式说出接口中成员的特点能说出接口的应用场景能说出接口中为什么会出现带有方法体的方法能完成适配器设计模式 第一章 抽象类…

c语言--typedef关键字

目录 一、typedef关键字是用来干嘛的&#xff1f;二、用法 一、typedef关键字是用来干嘛的&#xff1f; typedef 是⽤来类型重命名的&#xff0c;可以将复杂的类型&#xff0c;简单化。 二、用法 比如&#xff0c;你觉得 unsigned int 写起来不方便&#xff0c;如果能写成 u…

[网鼎杯 2020 白虎组]PicDown

网鼎杯的&#xff0c;这应该是送分的那种 界面很普通&#xff0c;就长这样。源代码也没什么&#xff0c;随便输入试试 出现了"/page?url1" 这可能是ssrf题目。 但是尝试了一些payload发现下载了一张图片&#xff0c;并且url里自动补齐了127.0.0.1。使用记事本打开…

MySQL数据库基础(十三):关系型数据库三范式介绍

文章目录 关系型数据库三范式介绍 一、什么是三范式 二、数据冗余 三、范式的划分 四、一范式 五、二范式 六、三范式 七、总结 关系型数据库三范式介绍 一、什么是三范式 设计关系数据库时&#xff0c;遵从不同的规范要求&#xff0c;设计出合理的关系型数据库&…

SQL Server——建表时为字段添加注释

在 MySQL 中&#xff0c;新建数据库表为字段添加注释可以使用 comment 属性来实现。SQL Server 没有 comment 属性&#xff0c;但是可以通过执行 sys.sp_addextendedproperty 这个存储过程添加扩展属性来实现相同的功能。 这个存储过程的参数定义如下&#xff1a; exec sys.s…

Shell变量类型和运算符

一、Shell变量类型 1、变量类型 Shell的3种变量&#xff1a; &#xff08;1&#xff09;局部变量&#xff1a;除了本地变量外&#xff0c;还有shell脚本中定义的变量。 &#xff08;2&#xff09;全局变量&#xff1a;和局部变量相对。比如环境变量就是一种全局变量。 &am…