MyBatis:使用 MyBatis 实现多表查询(多对一和一对多)、动态 SQL、缓存

news2024/11/13 9:10:58

文章目录

  • MyBatis:Day 03
  • 一、复杂查询的环境搭建
  • 二、多表查询
    • 1. 多对一:关联
      • (1)联表查询
      • (2)子查询
    • 2. 一对多:集合
      • (1)联表查询
      • (2)子查询
    • 3. 总结
  • 三、动态 SQL 的环境搭建
  • 四、动态 SQL
    • 1. if
    • 2. choose、when、otherwise
    • 3. where、set
    • 4. foreach
    • 5. SQL 片段
    • 6. 总结
  • 五、缓存
    • 1. 简介
    • 2. 一级缓存
    • 3. 二级缓存
    • 4. 缓存原理
  • 注意:

MyBatis:Day 03

一、复杂查询的环境搭建

复杂查询的环境搭建:为接下来的多对一和一对多的处理做准备。

总体步骤为:

  1. pom.xml 中导入所需要的依赖;
  2. 建立 MyBatis 核心配置文件:mybatis-config.xml
  3. 工具类:MybatisUtils.java
  4. 实体类:Teacher.javaStudent.java
  5. 接口:TeacherMapper.javaStudentMapper.java
  6. 实现类:TeacherMapper.xmlStudentMapper.xml
  7. 测试。

需要提前建立两张表:studentteacher 表。

-- 创建数据库 mybatis01
CREATE DATABASE IF NOT EXISTS `mybatis01`;
USE `mybatis01`;

-- 创建 teacher 表
CREATE TABLE IF NOT EXISTS `teacher` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;

-- 插入数据
INSERT INTO `teacher` (`id`, `name`) VALUES (1, '张老师'), (2, '王老师'); 

-- 创建 student 表
CREATE TABLE IF NOT EXISTS `student` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  `tid` INT(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fktid` (`tid`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;

-- 插入数据
INSERT INTO `student` (`id`, `name`, `tid`) VALUES 
(1, '小明', 1), 
(2, '小红', 1), 
(3, '小张', 2), 
(4, '小李', 2), 
(5, '小王', 2);

环境搭建显示

注意:这里把实现类 Mapper.xml 放入了资源文件夹下,但经过编译后仍在 dao 包下,所以在核心配置文件中的资源路径为:resource="com/Sun3285/dao/StudentMapper.xml"

生成的 target 文件夹


二、多表查询

最佳实践:最好逐步建立结果映射。单元测试可以在这个过程中起到很大帮助。 如果你尝试一次性创建复杂的结果映射,不仅容易出错,难度也会直线上升。 所以,从最简单的形态开始,逐步迭代。而且别忘了单元测试! 有时候,框架的行为像是一个黑盒子(无论是否开源)。因此,为了确保实现的行为与你的期望相一致,最好编写单元测试。 并且单元测试在提交 bug 时也能起到很大的作用。

1. 多对一:关联

举例:查询全部学生(多)对应的老师(少)。

(1)联表查询

思路

  1. 联表查询是按照结果嵌套处理;
  2. 步骤:先写出 SQL 语句,保证可以执行,然后在 Mapper.xml 中用 resultMap 进行结果集映射,最后测试。
  • SQL 语句:
SELECT s.`id` AS '学生id', s.`name` AS '学生姓名', s.`tid` AS '老师id', t.`name` AS '老师姓名' 
FROM `student` AS s 
INNER JOIN `teacher` AS t 
ON s.`tid` = t.`id`;

  • Mapper.xml 中用 resultMap 进行结果集映射:
<select id="getAllStudents" resultMap="studentResultMap">
    SELECT s.`id` AS '学生id', s.`name` AS '学生姓名', s.`tid` AS '老师id', t.`name` AS '老师姓名'
    FROM `student` AS s
    INNER JOIN `teacher` AS t
    ON s.`tid` = t.`id`;
</select>

<resultMap id="studentResultMap" type="Student">
    <result property="id" column="学生id"/>
    <result property="name" column="学生姓名"/>
    <!-- 复杂类型是对象,用 association -->
    <association property="teacher" javaType="Teacher">
        <result property="id" column="老师id"/>
        <result property="name" column="老师姓名"/>
    </association>
</resultMap>

  • 测试:

(2)子查询

思路:子查询是按照查询过程嵌套处理;

<select id="getAllStudents" resultMap="studentResultMap">
    select * from `student`;
</select>

<resultMap id="studentResultMap" type="Student">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>

<select id="getTeacher" resultType="Teacher">
    select * from teacher where `id` = #{tid};
</select>

2. 一对多:集合

举例:查询全部老师(少)包含的学生(多)。

(1)联表查询

思路

  1. 联表查询是按照结果嵌套处理;
  2. 步骤:先写出 SQL 语句,保证可以执行,然后在 Mapper.xml 中用 resultMap 进行结果集映射,最后测试。
  • SQL 语句:
SELECT t.`id` AS '老师id', t.`name` AS '老师姓名', s.`id` AS '学生id', s.`name` AS '学生姓名' 
FROM `teacher` AS t 
INNER JOIN `student` AS s 
ON t.`id` = s.`tid`;

  • Mapper.xml 中用 resultMap 进行结果集映射:
<select id="getAllTeachers" resultMap="teacherResultMap">
    SELECT t.`id` AS '老师id', t.`name` AS '老师姓名', s.`id` AS '学生id', s.`name` AS '学生姓名'
    FROM `teacher` AS t
    INNER JOIN `student` AS s
    ON t.`id` = s.`tid`;
</select>

<resultMap id="teacherResultMap" type="Teacher">
    <result property="id" column="老师id"/>
    <result property="name" column="老师姓名"/>
    <collection property="studentList" ofType="Student">
        <result property="id" column="学生id"/>
        <result property="name" column="学生姓名"/>
    </collection>
</resultMap>

  • 测试:

(2)子查询

This part is too complex, let’s use linked table queries instead!

3. 总结

  1. 当查询出来的字段名属性名不一致时,需要进行结果集映射 resultMap

  2. 使用结果集映射 resultMap 时,属性名和查询到的字段名要一一映射

    • 简单的属性(只是属性名和字段名不一致),直接映射;
    • 复杂的属性(如自定义对象、集合)需要单独处理(嵌套处理):
      • 属性是自定义对象:用 association,其中 javaType 为属性中自定义的类
      • 属性是集合:用 collection,其中 ofType 为属性中集合的泛型约束类型
  3. 保证 SQL 的可读性,通俗易懂。

  4. 排查错误可以使用日志


三、动态 SQL 的环境搭建

动态 SQL 的环境搭建:为接下来的动态 SQL 学习做准备。

总体步骤为:

  1. pom.xml 中导入所需要的依赖;
  2. 建立 MyBatis 核心配置文件:mybatis-config.xml
  3. 工具类:MybatisUtils.java 以及 IDUtils.java(用来产生一个唯一的 ID);
  4. 实体类:Blog.java
  5. 接口:BlogMapper.java
  6. 实现类:BlogMapper.xml
  7. 测试。

需要提前建立一张表:blog 表。

-- 创建数据库 mybatis02
CREATE DATABASE IF NOT EXISTS `mybatis02`;

USE `mybatis02`;

-- 创建表 blog
CREATE TABLE IF NOT EXISTS `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`views` INT(30) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=utf8mb4;

测试:插入数据。

环境搭建过程


四、动态 SQL

动态 SQL:根据不同的条件生成不同的 SQL 语句,在 SQL 层面执行一些逻辑代码

实质拼接 SQL 语句,只要保证 SQL 的正确性,按照 SQL 的格式去排列组合就可以。

建议:先写出完整的 SQL,然后再对应地去修改成为动态 SQL。

1. if

<!-- 在实现类 Mapper.xml 中配置 -->
<select id="方法名" parameterType="参数类型" resultType="返回值类型">
    select 查询语句
    <where>
        <if test="判断语句1(结果为布尔值类型)">
            sql 语句
        </if>
        <if test="判断语句2(结果为布尔值类型)">
            sql 语句
        </if>
    </where>
</select>

2. choose、when、otherwise

如果有多个 IF 语句,会从上到下依次对每个条件进行判断。有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

<!-- 在实现类 Mapper.xml 中配置 -->
<select id="方法名" parameterType="参数类型" resultType="返回值类型">
    select 查询语句
    <where>
        <choose>
            <when test="判断语句1(结果为布尔值类型)">
                sql 语句
            </when>
            <when test="判断语句2(结果为布尔值类型)">
                sql 语句
            </when>
            <otherwise>
                sql 语句
            </otherwise>
        </choose>
    </where>
</select>

注意:choose 元素,类似于 Java 中的 switch 语句:

  • choose 等同于 switch;
  • when 等同于 case;
  • otherwise 等同于 default。

3. where、set

where 元素作用

  • 至少有一个子元素的条件返回 SQL 子句的情况下才插入 “WHERE” 子句;
  • 如果子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除

set 元素作用

  • 会动态地在行首插入 SET 关键字;
  • 删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

4. foreach

foreach:用来对集合进行遍历(尤其是在构建 IN 条件语句的时候)。

可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。

当使用可迭代对象或者数组时:

  • collection :指传递的集合参数,如果 parameterType 为 Map 集合,这里就是指键名;

  • item :每次迭代获取到的元素;

  • open :以 xxx 为开始;

  • separator :分隔;

  • close :以 xxx 为结束。

<!-- 在实现类 Mapper.xml 中配置 -->
<select id="方法名" parameterType="map" resultType="返回值类型">
    select 查询语句
    <where>
        <foreach collection="传递的集合参数" item="每次迭代获取到的元素"
                 open="开始" separator="分隔" close="结束">
            `id` = #{id}
        </foreach>
    </where>
</select>

举例:查询指定 ID 的博客信息。

5. SQL 片段

有的时候,我们可能会将一些公共部分抽取出来,方便复用,如下所示:

<!-- SQL 片段 -->
<sql id="SQL 片段的 id(自由取)">
	公共的 SQL 片段
</sql>

引用 SQL 片段时,如下所示:

<!-- 使用 SQL 片段 -->
<select id="方法名" parameterType="map" resultType="返回值类型">
    select 查询语句
    <where>
        <include refid="SQL 片段的 id"></include>
    </where>
</select>

注意

  1. 最好基于单表来定义 SQL 片段;
  2. SQL 片段中不要存在 where 标签。

6. 总结

  1. 多个 IF 语句会从上到下依次对每个条件进行判断;

  2. choose 元素,像 Java 中的 switch 语句,从上到下执行,只要有一个符合就停止执行;

  3. foreach 元素是遍历循环,集合参数为可迭代对象(如 List、Set 等)、Map 对象或者数组对象;

  4. 最好将 SQL 中的 where 用 where 标签代替;

  5. 判断 test 时,里面的语句为 Java 语句,其中对字符串的判断,要用单引号套双引号的形式,因为 MyBatis 是使用 OGNL 表达式来进行解析的。

<where>
    <if test='author == "Sun3285"'>
    	SQL 语句
    </if>
</where>

五、缓存

1. 简介

缓存:Cache,存在内存中的临时数据

  • 作用:将用户经常查询的数据放在缓存中,用户去查询数据就不用从磁盘上查询,而是从缓存中查询;
  • 优点减少了和数据库的交互次数,提高了查询效率,解决了高并发系统的性能问题;
  • 使用缓存的数据经常查询并且不经常改变的数据。

MyBatis 缓存:包含了一个非常强大的缓存特性,可以非常方便地定制和配置缓存。默认定义了两级缓存

  • 一级缓存:也叫本地缓存,是 sqlSession 级别的缓存。默认情况下,只有一级缓存开启,仅仅对一个会话中的数据进行缓存。
  • 二级缓存:是 namespace 级别的缓存。需要手动开启和配置,MyBatis 定义了缓存接口 Cache ,可以实现接口来定义二级缓存。

注意:二级缓存作用于 cache 标签所在的映射文件中的语句。

2. 一级缓存

  • 一级缓存是 sqlSession 级别的缓存,默认情况下,只有一级缓存开启;

  • 仅仅对一个会话中的数据进行缓存(sqlSession 从得到到关闭这一段期间的数据)。

测试一级缓存。

缓存失效的情况:

  1. 查询不同的数据;
  2. 进行了增、删、改操作,必定会刷新缓存,因为可能改变了原来的数据;
  3. 查询不同的实现类 Mapper.xml
  4. 手动清理缓存sqlSession.clearCache();

注意:一级缓存相当于一个 Map 集合。

3. 二级缓存

二级缓存是 namespace 级别的缓存,会在整个 Mapper.xml 中生效。需要手动开启和配置

工作机制:当关闭会话时,一级缓存中的数据会被保存到二级缓存中,新的会话查询信息时,就可以从二级缓存中获取内容。

开启二级缓存步骤

  1. 在 MyBatis 核心配置文件中进行设置(settings),开启全局缓存
  2. 在要使用二级缓存的 Mapper.xml 中加 cache 标签。

测试二级缓存。

注意实体类需要序列化。

4. 缓存原理

在缓存中查询的顺序

  1. 先看二级缓存中有没有之前查过的数据;
  2. 再看一级缓存中有没有之前查过的数据;
  3. 如果都没有,则查询数据库。

举例

查询一个数据 --> 二级缓存中没有 --> 一级缓存中没有 --> 连接数据库,查询数据库 --> 查询到结果,结果保存到一级缓存中 --> 关闭会话,此时一级缓存中的数据保存到二级缓存中;

再次查询同一个数据 --> 二级缓存中存在 --> 从二级缓存中直接拿到数据(不会连接数据库)。


注意:

  1. Java 代码编译为 class 文件,然后对 class 文件进行执行。
  2. 编译前,代码按照功能分类;编译后,代码按照包名分类。
  3. xml 文件中路径和全限名依据编译后 target 文件中的路径和全限名。
  4. 一定要注意 resultTyperesultMap 别写错。
  5. 实体类最好序列化:实体类实现 Serializable 接口。

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

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

相关文章

JavaScript学习(一)

一、JavaScript的背景及知识结构 1、三个问题 什么是JavaScript&#xff1f;JavaScript能干什么&#xff1f;JavaScript是由什么构成的&#xff1f;怎样学习JavaScript&#xff1f; 2、什么是JavaScript&#xff1f; ①JavaScript是一种轻量级的编程语言&#xff1b;借鉴了J…

2023年2月573名学员通过国家信息安全水平考试其中CISP218名,NISP二级355名

北京PER20230217HEBAN3 考试时间&#xff1a;2023年02月17日 地点&#xff1a;北京 王佳俊、王后月、程鹏、王海洋、牛麒盛、张飞、禹晓瑞、李鹏卫、柳位世、李霖、刘志超、刘峰、张广军、刘鑫博、王琳、翟栋、刘高军、康寅哲、夏中玲、冯光豪、程岩辉、白玉强、遆盼明、李淼…

IDEA部署tomcat项目

文章目录 只是部署一下看到这里即可war和war exploded的区别warwar exploded update的动作update resourcesupdate classes and resourcesredeployrestart server 解决了拿到了一个tomcat项目后如何将它部署到IDEA里面的问题。 file->open 选中pom.xml并open as project …

【网站架构】Nginx 4层、7层代理配置,正向代理、反向代理详解

大家好&#xff0c;欢迎来到停止重构的频道。 本期我们讨论网络代理。 在往期《大型网站 安全性》介绍过&#xff0c;出于网络安全的考虑&#xff0c;一般大型网站都需要做网络区域隔离&#xff0c;以防止攻击者直接操控服务器。 网站系统的应用及数据库都会放在这个网络安全…

js实现归并排序(mergeSort)

理解归并排序 归并排序是一种基于分治思想的排序算法&#xff0c;它将一个待排序的序列分为两个子序列&#xff0c;分别对子序列进行排序&#xff0c;然后将排好序的子序列合并成一个有序的序列。这个过程可以递归地进行&#xff0c;直到序列被划分为只有一个元素时停止递归。 …

版本控制 | 如何使用虚幻引擎的多用户编辑(MUE)功能

随着现代虚拟制作技术的发展&#xff0c;电影制片厂不再需要完全依赖实际场景&#xff0c;而是可以在拍摄期间就让虚拟制作设计团队将实景与计算机生成的虚拟内容融合在一起。虚幻多用户编辑&#xff08;Unreal Multi-User Editing&#xff0c;简称MUE&#xff09;可以帮助简化…

【产品方案】后台管理系统设计思路

第一章 前言 相比前端设计&#xff0c;我更喜欢设计后台管理系统。如果说前端设计考验的是共情能力&#xff0c;那后台管理系统设计考研的就是逻辑能力&#xff0c;前者需要站在用户的角度&#xff0c;后者是站在管理者的角度思考。 有幸参与了公司不少业务系统从“0-1”的设计…

使用prometheus时发现mongodb exporter的/metrics数据展示很慢,延迟高

项目场景&#xff1a; 使用prometheusgrafana搭建对mongoDB集群的监控。 问题描述 使用prometheus时发现mongodb exporter的/metrics数据展示接口很慢&#xff0c;延迟高。 看了一下大概是10s 原因分析&#xff1a; 由于是在云服务器上进行搭建的。 经过尝试之后发现创建mo…

Apache Doris学习记录

1. Doris基础学习 中文官网:https://doris.apache.org/zh-CN/docs/dev/summary/basic-summary/ 1.1 doris 简介 Apache Doris 是一个现代化的 MPP(Massively Parallel Processing&#xff0c;即大规模并行处理) 分析型数据库产品 亚秒级响应时间即可获得查询结果 可以支持 10PB…

Python每日一练:最长递增的区间长度(一行代码花样解法)

文章目录 前言一、题目二、一行超人三、分析一下思路 总结 前言 很显然&#xff0c;Python的受众远远大于C&#xff0c;其实笔者本人对Python的理解也是远强于C的&#xff0c;C纯粹是为了假装笔者是个职业选手才随便玩玩的&#xff0c;借着十多年前学的C的功底&#xff0c;强行…

链表(数据结构)

目录 链表 链表的分类 1、单向或者双向 2、带头或者不带头 3、循环或者非循环 总结&#xff1a; 单链表 创建链式结构 创建新节点 尾插 尾删 头插 头删 查找节点 在pos位置后插入 删除pos位置后的节点 销毁 链表 概念&#xff1a; 链表是一种物理结构上非连续的、非顺序的存储结…

c# 数据保存为PDF(二) (Aspose pdf篇)

文章目录 前言关于Aspose PDF使用Aspose.Pdf常用的命名空间和类库1 创建简单的PDF文档2 美化PDF样式2.1 创建测试数据2.2 项目头部样式2.3 全部代码 小结附录参考 前言 项目中需要将数据导出存为PDF格式&#xff0c;试了一下Aspose组件&#xff0c;仅以此记录一下使用感受。 …

设计模式——原型模式(浅拷贝和深拷贝)

是什么&#xff1f; 用一个已经创建的实例作为原型&#xff0c;通过复制该原型对象来创建一个和原型对象相同的新对象&#xff1b; 结构 抽象原型类&#xff1a;规定了具体原型对象必须实现的Clone&#xff08;&#xff09;方法&#xff1b; 具体原型类&#xff1a;实现抽象…

Elasticsearch:NLP 和 Elastic:入门

自然语言处理 (Natural Language Processing - NLP) 是人工智能 (AI) 的一个分支&#xff0c;专注于尽可能接近人类解释的理解人类语言&#xff0c;将计算语言学与统计、机器学习和深度学习模型相结合。 AI - Artificial Inteligence 人工智能ML - Machine Learning 机器学习DL…

集线器、网桥、交换机

一.集线器 集线器&#xff08;HUB&#xff09;&#xff0c;它是工作在物理层的设备&#xff0c; 由于它只是工作在物理层的设备&#xff0c;所以它并不关心也不可能关心OSI上面几层所涉及的&#xff0c;它的工作机制流程是&#xff1a;从一个端口接收到数据包时&#xff0c;会在…

STL基础

目录 一、STL的诞生 二、STL基本概念 三、STL六大组件 大体分为六大组件&#xff1a;容器、算法、迭代器、仿函数、适配器&#xff08;配接器&#xff09;、空间配置器 四、容器、算法与迭代器的认识 容器container&#xff1a;存放数据地方 算法algorithm&#xff1a;解…

中级软件设计师备考---软件工程2

目录 软件测试分类和要求测试用例设计测试阶段McCabe复杂度软件维护软件过程改进---CMMICMM英文版CMM中文版CMMI 软件测试分类和要求 分类&#xff1a; 灰盒测试&#xff1a;多用于集成测试阶段&#xff0c;不仅关注输出、输入的正确性&#xff0c;同时也关注程序内部的情况。…

MySQL:插入,更新与删除、索引

一、学习目标 掌握如何向表中插入数据掌握更新数据的方法熟悉如何删除数据掌握对数据表基本操作的方法和技巧了解什么是索引掌握创建索引的方法和技巧熟悉如何删除索引熟悉掌握索引的常见问题 二、实验内容 创建表books&#xff0c;对数据表进行插入、更新和删除操作&#x…

SkyWalking集成Logback的使用

SkyWalking集成Logback的使用 将微服务的日志框架去集成SkyWalking&#xff0c;我们希望在我们微服务中日志中&#xff0c;能够记录当前调用链路的id&#xff0c;然后我们再根据这个id去SkyWalking的前端界面中进行搜索找到对应的调用链路记录。 因为springboot默认实现的日志…

Ansible 的脚本 之playbook 剧本

目录 第一章.playbooks的组成 1.1playbooks的组成部分 1.2运行playbook 1.3定义、引用变量 1.4.指定远程主机sudo切换用户 1.6.when条件判断 1.7.迭代 第二章.Templates模块 2.1.准备模板 2.2.修改主机清单文件 2.3.编写 playbook 第三章.tags 模块 3.1.yaml文件编…