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

MyBatis入门到精通(Mybatis学习笔记)

一.mybatis简介

MyBatis是一个优秀的基于ORM半自动轻量级持久层框架,它对jdbc的操作数据库的过程进行封装, 使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建 statement、手动设置参数、结果集检索等jdbc繁杂的过程代码

在开始之前我想先给大家解释一下ORM框架:

ORM 的全称是 Object-Relational Mapping,中文叫 “对象-关系映射”。顾名思义,它的核心就是在 对象(Object) 和 关系型数据库(Relational Database) 之间建立一个映射(Mapping)桥梁。

举个例子:

想象一下,你是一个只会说中文的老板(程序员),你要管理一个仓库(数据库)。仓库里的货物都按照一套非常严格的规则摆放,记录员(数据库系统,如 MySQL)只听得懂一种特定的外语指令(SQL 语言)来存取货物。

没有 ORM 的时候(原始方式):
每次你想查一下“这个月卖了多少个苹果”,你需要:

  1. 先自己费劲地写好那段外语指令(手写 SQL):                                                           SELECT quantity FROM products WHERE name = '苹果' AND ...

  2. 然后把这条指令交给记录员。

  3. 记录员执行完,给你一份外文报告。

  4. 你再把这份外文报告翻译成你能看懂的中文表格。

这个过程非常繁琐,容易写错,而且你很可能会因为不精通这门“外语”而搞出乱子。

有了 ORM 之后:

现在你雇了一个“智能快递员”(ORM 框架)。这个快递员特点如下:

  1. 他会双语:他既懂你的中文(编程语言,如 Python/Java),也懂记录员的外语(SQL)。

  2. 他有个对照表:你提前告诉他,你的“苹果”这个概念,对应仓库里 products 表格里 name 是 '苹果' 的那一行。这个对照关系就是 “映射”

也就是说这个聪明的快递员(ORM)会:

  1. 听懂你的中文指令。

  2. 根据“对照表”,自动帮你生成那段复杂的外语指令(SQL)。

  3. 跑去交给仓库记录员执行。

  4. 拿到外文报告后,自动翻译成你代码里能直接使用的对象(比如一个 Product 对象)。

  5. 最后,你直接操作这个对象的属性(.销量)就行了。

二.写第一个mybatis程序

示例:

这是我们的一个整体的架构(建包注意我们的三层架构):
1.创建数据库以及user表
2.创建maven工程,在pom.xml文件中导入依赖(MySQL驱动、mybatis、junit)
3.编写User实体:在这里编写完基本的属性之后要记得用快捷键生成get()和set(),以及toString()方法.
4.编写UserMapper映射文件(UserMapper.xml)
5.在Recources目录下编写MyBatis核心配置文件(mybatis-config.xml)
6.编写测试类
运行结果如下:
恭喜你,已经完成第一个mabatis程序!

三.mybatis映射文件的概述

四.实现CRUD

 

parameterType与resultType:

parameterType = 你给方法的"输入参数"类型
resultType = 方法返回给你的"输出结果"类型

示例:

(1)新增

1.编写映射文件UserMapper.xml

2.测试类

这里需要注意的是,DML语句须手动提交事务!

运行结果:

(2)删除

1.编写映射文件UserMapper.xml

2.测试类

运行结果:

id为12的被删除了

(3)修改

1.编写映射文件UserMapper.xml

2.测试类

(4)小结:

 

五.Mybatis核心文件概述

1.MyBatis核心配置文件层级关系

2.MyBatis常用配置解析

1) environments标签

2properties标签

实际开发中,习惯将数据源的配置信息单独抽取成一个properties文件,该标签可以加载额外配置的 properties,例如:

注:配置好properties文件后要在mybatis_config.xml文件中配置<properties> 属性

3typeAliases标签

类型别名是为 Java 类型设置一个短的名字。(往后我们再细说)
为了简化映射文件 Java 类型设置,mybatis框架为我们设置好的一些常用的类型的别名:

4mappers标签

1. 使用相对于类路径的资源引用,例如:
<mapper resource="org/mybatis/builder/userMapper.xml"/>
2. 使用完全限定资源定位符(URL),例如:
<mapper url="file:///var/mappers/userMapper.xml"/>
《下面两种mapper代理开发中使用:暂时了解》
3. 使用映射器接口实现类的完全限定类名,例如:
<mapper class="org.mybatis.builder.userMapper"/>
4. 将包内的映射器接口实现全部注册为映射器,例如:
<package name="org.mybatis.builder"/>

六.MybatisAPI概述

1.Mybatis基本原理介绍:

MyBatis 作为一个框架,它自己并不知道你的数据库在哪里、SQL语句怎么写,这些关键信息都必须由你(开发者)来告诉它。配置文件就是 MyBatis 获取这些“行动指令”的唯一途径。没有配置文件,MyBatis 就寸步难行。

于是首先,SqlSessionFactoryBuilder 会读取 SqlMapConfig.xml 这个全局配置文件,将其中的数据库连接、事务等配置信息转化为 Java 对象,并以此创建出 SqlSessionFactory(会话工厂)。当我们需要操作数据库时,SqlSessionFactory 会为我们生产出一个 SqlSession(会话),这是我们程序员与之交互的核心接口。

但 SqlSession 本身并不直接执行 SQL,它内部会委托给 Executor(执行器)来负责具体的数据库操作。执行器会根据 SQL 语句的标识,找到对应的 xxxMapper.xml 配置文件中的 SQL 语句。此时,一个关键的角色 Mapped Statement 登场了,它将这些 SQL 语句、以及你传入的输入参数(可以是简单类型、POJO、Map 等)进行封装,并交给执行器去执行。

执行器操作数据库得到结果后,Mapped Statement 会再次发挥作用,根据配置中定义的输出参数类型(同样是简单类型、POJO、Map 等),自动地将数据库返回的结果映射成我们指定的 Java 对象,最终通过 SqlSession 返回给我们。这就是 MyBatis 将数据库操作和 Java 对象映射起来的完整流程。

于是我们必须要在测试类里面的通过加载mybatis的核心文件的输入流的形式构建一个SqlSessionFactory对象,再用SqlSessionFactory 创建一个SqlSession会话对象

注:一个环境对应一个数据库,一个数据库对应一个SqlSessionFactory对象,再通过sqlSessionFactory对象可以开启多个会话

七.Mapper代理开发

Mapper 接口开发方法只需要程序员编写Mapper 接口(相当于Dao 接口),由Mybatis 框架根据接口 定义创建接口的动态代理对象,代理对象的方法体同Dao接口实现类方法。

注:

Mapper.xml映射文件中的namespacemapper接口的全限定名相同

Mapper接口方法名和Mapper.xml映射文件中定义的每个statementid相同

Mapper接口方法的输入参数类型和mapper.xml映射文件中定义的每个sqlparameterType的类 型相同

Mapper接口方法的输出参数类型和mapper.xml映射文件中定义的每个sqlresultType的类型相 同

 

示例:

1.编写UserMapper接口
2.编写UserMapper.xml
3.测试

八.mybatis高级查询

1.ResutlMap 属性

resultType:
如果实体的属性名与表中字段名一致,将查询结果自动封装到实体类中
ResutlMap(结果映射):
如果实体的属性名与表中字段名不一致,可以使用ResutlMap 实现手动封装到实体类中

示例:

1.编写UserMapper 接口

2.编写UserMapper.xml

3.代码测试

4.运行结果:

2.多条件查询(三种)

1.使用 #{arg0}-#{argn} 或者 #{param1}-#{paramn} 获取参数

示例:

1.UserMapper 接口

2.UserMapper.xml

3.测试

4.运行结果

注:
1.#{arg0}-#{argn}:基于参数的位置索引获取参数,
arg0 表示第一个参数,arg1 表示第二个参数,以此类推(索引从 0 开始)。
仅在没有使用 @Param 注解时有效。
2.#{param1}-#{paramn}:基于参数的参数顺序获取参数,
param1 表示第一个参数,param2 表示第二个参数,以此类推(编号从 1 开始)。
无论是否使用 @Param 注解都有效。
3.底层实现:
mybatis底层会创建一个mao集合:
map.put(arg0,name)
map.out(arg1,sex)
map.put(param1,name)
map.out(param2,sex)
混着用:arg0,param2 可以
  arg0,param1 不可以

2.使用注解,引入 @Param() 注解获取参数

 

示例:

1.UserMapper 接口

2.UserMapper.xml

3.测试

4.运行结果

注:@Param("id")括号里的要与select里的#{}字段保持一致

3.使用pojo 对象传递参数

 

示例:

1.UserMapper 接口

2.UserMapper.xml

3.测试

4.运行结果

注:resultType:指定 SQL 语句的返回结果类型  | parameterType:指定 SQL 语句的输入参数类型

九.模糊查询

根据username模糊查询user表

方式一

示例

1.UserMapper接口

2.UserMapper.xml

3.测试

4.运行结果

方式二

示例

1.UserMapper接口

 

2.UserMapper.xml

3.测试

4.运行结果

concat:在 MyBatis 中,concat是一个 SQL 函数(非 MyBatis 自带函数),用于将多个字符串拼接成一个字符串。它通常在 XML 映射文件的 SQL 语句中使用,用于动态拼接查询条件、字段值等。

使用方法:

例一:

concat('%',#{name},'%'}会将传入的name参数前后各加一个%,例如参数为张三时,拼接结果为%张三%,实现全模糊查询。

例二:

concat(a,' ',b)这个用法就拼接了一个字符串:a+' '+b,

特殊:

concat(concat('%',#{username}),'%'),使用嵌套的方式,先拼接 % 和 #{username} 得到 %参数值,再将结果与 % 拼接,最终得到 %参数值%。(这里适用于早期版本的 Oracle,在这里多参数拼接必须嵌套使用。)

十.返回主键

我们很多时候有这种需求,向数据库插入一条记录后,希望能立即拿到这条记录在数据库中的主键
值。

1.useGeneratedKeys

2 selectKey

测试
运行结果(没找到):

useGeneratedKeys与selectKey

useGeneratedKeys 和 selectKey 都是用于获取数据库自动生成的主键值

1.useGeneratedKey通过数据库自身的 “自动生成主键” 机制(如 MySQL 的自增 ID),在执行 INSERT 操作后,自动将数据库生成的主键值回写到 Java 实体类的对应属性中。

2.selectKey通过自定义 SQL 语句手动查询生成的主键值(适用于不支持自动生成主键的数据库,如 Oracle 序列),并回写到 Java 实体类的属性中。

维度useGeneratedKeysselectKey
适用数据库支持自动生成主键的数据库(MySQL、SQL Server)所有数据库(尤其 Oracle 等依赖序列的数据库)
实现方式依赖数据库自身的自动生成机制,无需额外 SQL通过自定义 SQL 手动查询主键,灵活性更高
配置复杂度简单(仅需两个属性)较复杂(需嵌套标签,配置 order 等)
主键生成时机数据库自动生成,插入后获取可在插入前(BEFORE)或插入后(AFTER)获取

十一.动态SQL

动态 SQL 主要用于根据不同条件动态生成 SQL 语句

场景:用户管理系统

  • 用户可能会用不同条件搜索:

    • 只按名字搜

    • 按名字+年龄搜

    • 按邮箱搜

    • 什么条件都不填,查看所有用户

没有动态 SQL:你要为每种情况写一个单独的 SQL

// 要写4个不同的方法!
findUsersByName(String name);
findUsersByNameAndAge(String name, int age);  
findUsersByEmail(String email);
findAllUsers();

有动态 SQL:只需要一个方法搞定所有情况

// 一个方法应对所有搜索条件
findUsers(String name, Integer age, String email);

1.<if>

if标签中test属性的必须的,值为boolean类型,如果为true则把标签内容拼接到sql语句中,反之不拼接(在mybatis的动态sql中,不能使用&&,只能使用and)

示例1:

当使用了@Param注解时,则使用@Param注解指定的参数名

接口:

.xml文件:

where 1=1,防止你传过去的东西全为空的时候报错

<if>里的and不建议省,防止其为空,报错

测试

运行结果

示例2:

当使用了pojo类,则使用pojo类的属性名

接口:

.xml文件

测试

运行结果

2.<where>

让where子句更加动态智能。

  • 所有条件都为空时,where标签保证不会⽣成where⼦句(即它会自动省略where子句,避免生成无效的 SQL 语法)。

  • ⾃动去除某些条件前⾯多余的and或or。

示例:

.xml文件

运行结果

3.<choose>

如果有id 只使用id 做查询,没有 id 的话看是否有username,有username 就根据username 做查询, 如果都没有,就不带条件。
有且仅有⼀个分⽀会被选择!!!!
<choose><when></when><when></when><when></when><otherwise></otherwise>
</choose>

等同于

if () {
} else if () {
} else if () {
} else if () {
} else {
}

示例:

接口:

.xml文件

测试

运行结果

4.<set>

主要使⽤在update语句当中,⽤来⽣成set关键字,同时去掉最后多余的“,”,如:动态更新user 表数据,如果该属性有值就更新,没有值不做处理。

示例:

接口:

.xml文件

测试

运行结果

5.<foreach>

foreach 主要是用来做数据的循环遍历

1)集合

示例:

接口:

.xml文件

如果查询条件为普通类型 List 集合,collection 属性值为:collection 或者 list

代码解析:

1.基本标签信息:

<select id="findByList"  <!-- 方法唯一标识,需与 Mapper 接口的方法名一致 -->parameterType="list"  <!-- 传入参数类型为 List 集合 -->resultType="user"  <!-- 查询结果的返回类型,这里是 User 实体类(别名) -->>

2.SQL 主体与<where>标签

SELECT * FROM `user`  <!-- 基础 SQL:查询 user 表的所有字段 -->
<where>  <!-- 动态生成 WHERE 子句,自动处理多余的关键字 --><!-- foreach 标签用于遍历集合,生成 IN 条件 --><foreach collection="collection"  <!-- 要遍历的集合名称(参数为 List 时,默认用 "collection" 或 "list" 引用) -->open="id in("  <!-- 遍历开始时拼接的字符串(这里生成 "id in(") -->close=")"  <!-- 遍历结束时拼接的字符串(这里生成 ")") -->item="id"  <!-- 遍历过程中每个元素的别名(当前元素用 #{id} 引用) -->separator=","  <!-- 元素之间的分隔符(多个 ID 用逗号分隔) -->>#{id}  <!-- 引用当前遍历到的元素(即集合中的每个 ID) --></foreach>
</where>

3.执行逻辑:

<foreach> 标签会遍历集合 [23,14,11],(自动排序)并按以下规则拼接:
open="id in(" → 开头添加 id in(
每个元素 item="id" → 分别生成 #{11}、#{14}、#{23}
separator="," → 元素之间用逗号分隔 → #{11}, #{14}, #{23}
close=")" → 结尾添加 )
最终拼接的 SQL 片段为:id in(#{12}, #{14}, #{23})

测试

运行结果

2)数组

示例:

接口:

xml文件:

如果查询条件为普通类型 Array 数组,collection 属性值为:array

测试:

运行结果:

3)pojo

示例:

创建QueryVo类:

为什么要再创建一个类?

1.解决 “多参数传递” 的痛点

假设你的查询需求不仅需要 ids 集合,还需要其他条件(如用户名、性别、年龄范围等),例如:

  • 根据 ids 集合批量查询
  • 同时要求 username 包含 “张”
  • 并且 age 大于 18

如果不使用 QueryVo,你可能需要在 Mapper 接口中定义这样的方法:

// 不使用 QueryVo 时,多参数传递需要 @Param 逐个声明,参数越多越繁琐
List<User> findUsers(@Param("ids") List<Integer> ids,@Param("username") String username,@Param("age") Integer age
);

而使用 QueryVo 后,只需将所有参数封装到一个对象中:

public class QueryVo {private List<Integer> ids;    // 批量IDprivate String username;      // 用户名条件private Integer age;          // 年龄条件// getters/setters
}// Mapper 接口只需接收一个 QueryVo 参数,简洁清晰
List<User> findByPojo(QueryVo queryVo);

2. 统一处理 “包含集合 / 数组的参数”

代码中需要传递 List<Integer> ids 集合来实现 id in(...) 查询。如果不使用 QueryVo,有两种传递方式:

  • 直接传递 List:此时 MyBatis 中 <foreach> 的 collection 属性必须用 list 或 collection(不够直观,且容易和其他集合参数混淆)。
  • 用 @Param 声明集合:如 findByList(@Param("ids") List<Integer> ids),此时 collection 用 ids,但如果有多个集合参数(如同时传递 ids 和 roles),仍需逐个声明。

而使用 QueryVo 时,集合是 QueryVo 的一个属性(ids),此时 <foreach> 的 collection 直接指定为属性名 ids 即可,语义清晰且不易出错:

<!-- 明确指向 QueryVo 中的 ids 属性,可读性高 -->
<foreach collection="ids" ...>...</foreach>

即使 QueryVo 中有多个集合(如 idsdeptIds),也能通过属性名明确区分,避免混淆。

 

接口

xml文件

如果查询条件为复杂类型pojo 对象,collection 属性值为:集合或数组的属性名

测试

运行结果

6.SQL片段

映射文件中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的

1.<sql>

定义可复用的 SQL 片段:用于将一段 SQL 代码(可以是字段列表、表名、条件片段等)定义为一个可复用的 “模板”,供其他 SQL 语句引用。

语法:

<sql id="片段唯一标识"><!-- 可复用的 SQL 片段 -->例如:字段列表、表名、WHERE 条件片段等
</sql>例如:<!-- 定义查询用户表的字段列表片段 -->
<sql id="userColumns">id, username, age, sex, email
</sql><!-- 定义查询条件的公共片段 -->
<sql id="commonWhere">WHERE status = 1  <!-- 假设 "status=1" 是多个查询的共同条件 -->
</sql>

 

2.<include>

引用已定义的 SQL 片段:在 SQL 语句中引用通过 <sql> 定义的片段,实现代码复用。

<include refid="需要引用的片段id" />例如
<!-- 引用字段列表片段 -->
<select id="findUserById" resultType="User">SELECT <include refid="userColumns" /> FROM user WHERE id = #{id}
</select><!-- 引用字段列表 + 公共条件片段 -->
<select id="findActiveUsers" resultType="User">SELECT <include refid="userColumns" /> FROM user<include refid="commonWhere" />
</select><!-- 定义带参数的 SQL 片段(表名动态传入) -->
<sql id="dynamicTable">SELECT * FROM ${tableName}  <!-- 使用 ${} 接收参数(表名不能用 #{}) -->
</sql><!-- 引用时传递参数 -->
<select id="findByTable" resultType="User"><include refid="dynamicTable"><!-- 传递参数 tableName,值为 "user" 或 "admin" 等 --><property name="tableName" value="user" /></include>WHERE id = #{id}
</select>

 

抽取SQL片段:

<sql id="selectUser">
SELECT * FROM `user`
</sql>
<select id="findByList" parameterType="list" resultType="user" >
<!--引入 sql 片段-->
<include refid="selectUser"></include>
<where>
<foreach collection="collection" open="id in(" close=")" item="id"
separator=",">
#{id}
</foreach>
</where>
</select>
<select id="findByArray" parameterType="integer[]" resultType="user">
<!--引入 sql 片段-->
<include refid="selectUser"></include>
<where>
<foreach collection="array" open="id in(" close=")" item="id"
separator=",">
#{id}
</foreach>
</where>
</select>

十二.plugins 标签

<plugins>标签用于配置插件(Plugins),插件是 MyBatis 提供的一种扩展机制,允许开发者在 SQL 执行的关键节点(如参数处理、SQL 语句生成、结果集映射等)插入自定义逻辑,实现功能增强(如分页、日志记录、数据加密等)。

配置方式:配置在 MyBatis 核心配置文件(如 mybatis-config.xml)中,用于注册自定义插件或第三方插件(如分页插件 PageHelper)。

基本语法:

<configuration><!-- 其他配置(如 properties、settings 等) --><plugins><!-- 注册第一个插件 --><plugin interceptor="插件全类名(必填)"><!-- 可选:设置插件属性(通过 set 方法注入) --><property name="属性名" value="属性值"/></plugin><!-- 注册第二个插件(可配置多个) --><plugin interceptor="另一个插件全类名"><!-- 插件属性 --></plugin></plugins><!-- 其他配置(如 environments、mappers 等) -->
</configuration>

示例

PageHelper:

分页插件,将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据

导入通用 PageHelper 坐标

mybatis 核心配置文件中配置PageHelper 插件

  • dialect指定数据库类型,确保生成正确的分页语法;
  • reasonable开启分页合理化,增强代码健壮性。

测试分页代码实现

第一页:

第二页:

获得分页相关的其他参数

十三.多表查询

   一次性从多个相关联的表中获取数据

1.一对一/多对一

用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户

示例:

查询一个订单,与此同时查询出该订单所属的用户

1.Order实体

2.OrderMapper 接口

3.OrderMapper.xml 映射

方式一:直接字段映射

方式二:使用 <association> 标签的关联映射

运行结果:

2.一对多

用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户

示例:

查询一个用户,与此同时查询出该用户具有的订单

1.User实体:

2.UserMapper 接口

3.UserMapper.xml 映射

测试

3.多对多

用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用

示例:

查询用户同时查询出该用户的所有角色

1.User Role 实体

2.UserMapper 接口

3.UserMapper.xml 映射

测试

运行结果:

总结:

* 一对一配置:使用<resultMap>+<association>做配置
* 一对多配置:使用<resultMap>+<collection>做配置
* 多对多配置:使用<resultMap>+<collection>做配置
* 多对多的配置跟一对多很相似,难度在于SQL 语句的编写

十四.嵌套查询

什么是嵌套查询?

嵌套查询就是将原来多表查询中的联合查询语句拆成单个表的查询,再使用 mybatis 的语法嵌套在一 起。

什么情况下会用到嵌套查询?

1. 关联关系复杂,一次性关联查询 SQL 过于繁琐

例如:查询用户→订单→订单详情→商品信息,多层级关联用嵌套查询可分步实现,避免 JOIN 多个表导致的 SQL 臃肿。

2.按需加载关联数据(懒加载)

例如:查询用户列表时,默认不加载每个用户的订单;当代码中调用user.getOrderList()时,才触发嵌套查询加载订单,节省资源。

3.处理聚合结果作为条件

当需要用聚合函数(如 AVGMAXCOUNT)的结果作为查询条件时,嵌套查询是自然的选择。

1.一对一

示例

查询一个订单,与此同时查询出该订单所属的用户

 1)OrderMapper 接口 

2OrderMapper.xml 映射

3)UserMapper 接口

4UserMapper.xml 映射

5)测试

6)运行结果:

总结:

测试启动
    ↓
调用 OrderMapper.selectOrderWithUser(orderId)
    ↓
MyBatis执行OrderMapper.xml中的SQL查询
    ↓
数据库返回订单和用户的JOIN结果集
    ↓
MyBatis根据ResultMap进行对象集映射
    ↓
自动创建Order对象并填充基本属性
    ↓
自动创建User对象并填充到Order.user属性
    ↓
返回完整的Order对象
    ↓
测试代码打印结果验证

2.一对多

示例:

查询一个用户,与此同时查询出该用户具有的订单

1)UserMapper 接口

2)UserMapper.xml 映射

遇到<collection> 标签时,触发关联查询

  • 用当前用户的 idcolumn="id")作为参数,调用 OrderMapper.findByUid 对应的 SQL(例如 select * from orders where user_id = ?

property=:指定 Java 实体类中 “存储关联数据的集合属性名”。

column:指定 “传递给关联查询的参数值”,用该值来自当前查询(主查询)的结果列。

ofType:指定集合中元素的类型

select:指定 “查询关联数据的 SQL 语句位置”,即通过哪个 Mapper 方法查询关联数据。

3)OrderMapper 接口

4)OrderMapper.xml 映射

5)测试

6)运行结果

3.多对多

示例

查询用户同时查询出该用户的所有角色

1UserMapper 接口

2)UserMapper.xml 映射

3RoleMapper 接口

4)RoleMapper.xml 映射

5)运行结果

 

总结:

嵌套查询:

将一个查询(子查询)的结果作为另一个查询(主查询)的条件或数据源,分步骤完成数据查询。

多表查询:

通过 SQL 的 JOIN 关键字,将多个表 “合并” 为一个虚拟表,通过单条 SQL 语句同时查询多个表的字段,结果集中包含所有关联表的字段。

维度多表查询(JOIN)嵌套查询(子查询)
查询次数单条 SQL 语句完成,仅执行 1 次数据库查询。分步骤执行,主查询 + 1 次或多次子查询(如 MyBatis 一对多可能触发 N 次子查询)。
结果形式结果集是多表字段的 “合并”,需手动区分不同表的字段(如 u.ido.id)。主查询结果与子查询结果分开,通过框架或业务逻辑组装(如 MyBatis 自动封装到对象属性)。
SQL 复杂度SQL 语句可能较长(需写 JOIN 条件和多表字段),但逻辑直观。单条 SQL 较短,但嵌套层级可能复杂(如多层子查询),可读性较差。
性能特点一次查询获取所有数据,减少数据库交互次数,适合关联表少、数据量中等的场景。多次查询可能增加数据库交互开销(尤其子查询次数多的情况),但可通过延迟加载优化(按需查询)。
框架依赖纯 SQL 逻辑,不依赖框架,直接通过 JOIN 实现。若用框架(如 MyBatis)的关联配置(association/collection),需依赖框架的结果映射机制。
适用关联类型适合所有关联类型(一对一、一对多、多对多),但一对多结果集可能有重复数据(需去重)。

更适合一对多、多对多关联(通过子查询避免主表数据重复)。

使用场景:                                                                                                                             多表查询是 “一次性合并多表”,适合简单关联、需高效获取完整数据的场景;                     嵌套查询是 “分步依赖查询”,适合复杂关联、需按需加载或框架自动封装的场景。

十五.延迟加载

延迟加载是一种数据加载策略,是一种优化关联查询性能的机制。在查询主对象时,不立即加载其关联的子对象(如用户详情、订单列表等),而是等到真正需要使用子对象时,才发起数据库查询加载数据。

举个例子

* 在一对多中,当我们有一个用户,它有个100个订单

在查询用户的时候,要不要把关联的订单查出来?

在查询订单的时候,要不要把关联的用户查出来?

* 回答

在查询用户时,用户下的订单应该是,什么时候用,什么时候查询。

在查询订单时,订单所属的用户信息应该是随着订单一起查询

1.局部延迟加载

1.修改或添加associationcollection标签中的fetchType属性

fetchType="lazy" 懒加载策略

fetchType="eager" 立即加载策略(默认)

2.设置触发延迟加载的方法:在配置文件中使用lazyLoadTriggerMethods配置项(equalsclonehashCodetoString等)

再次运行一对多查询的测试类:

没开延迟加载的一对多嵌套运行结果:
开了之后:
你会发现用户后面对应的订单不显示了,延迟加载成功!!

2.全局延迟加载

修改或添加associationcollection标签中的fetchType属性之后(同上)使用setting标签修改全局的加载策略
注:局部的加载策略优先级高于全局的加载策略。

十六.缓存

什么是缓存?

缓存是位于应用程序和数据库之间的数据存储层,用于临时存储查询结果。当相同的查询再次执行时,MyBatis会先从缓存中获取数据,而不是直接访问数据库。

为什么使用缓存?

当用户频繁查询某些固定的数据时,第一次将这些数据从数据库中查询出来,保存在缓存中。当用户再 次查询这些数据时,不用再通过数据库查询,而是去缓存里面查询。减少网络连接和数据库查询带来的损耗,从而提高我们的查询效率,减少高并发访问带来的系统性能问题。

什么情况下用到缓存?

1. 高频查询且数据稳定的场景

  • 例如:系统中的字典数据(如性别、学历、订单状态的枚举值)、地区数据(省 / 市 / 区列表)、静态配置数据(如系统参数)。

  • 这类数据极少修改,但会被频繁查询(如每次页面加载都需要字典数据),缓存可显著减少数据库访问。

2. 数据修改频率低的业务场景

  • 例如:电商中的商品详情(非实时库存)、新闻资讯(发布后很少修改)、用户基础信息(如用户名、头像,不频繁变更)。

  • 若数据修改后能接受短暂的缓存不一致(或通过手动清除缓存保证一致性),缓存能有效提升查询效率。

3. 并发查询压力大的场景

  • 例如:秒杀活动中的商品列表、热门文章的阅读页,短时间内有大量用户同时查询相同数据。

  • 缓存可分流数据库压力,避免因高频查询导致数据库连接耗尽或响应超时。

4. 多级缓存配合使用的场景

  • 一级缓存:适合单会话内的多次查询(如一个用户的单次操作中,多次查询其个人信息)。

  • 二级缓存:适合跨会话的共享数据(如多个用户查询同一商品详情)。

1.一级缓存

一级缓存是SqlSession级别的缓存,是默认开启的(不需要任何配置)
所以在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用一个Mapper方法,往往 只执行一次SQL,因为使用SelSession第一次查询后,MyBatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession都会取出当前缓存的数据,而不会再次发送SQL到数据库。(即只要使用同一个SqlSession对象执行同一条SQL语句,就会走缓存)
我们来验证一下:
没有清除缓存
运行结果
从运行结果里面的日志信息我们可以看见,我们与数据库只进行了一次交互。
 分析:
一级缓存是SqlSession范围的缓存,执行SqlSessionC(增加)U(更新)D(删除)操作,或者调 用clearCache()commit()close()方法,都会清空缓存。
清除:
测试类中
xml.配置文件中:
运行结果都是一样的,进行了两次交互:

2.二级缓存

二级缓存是namspace级别(跨sqlSession)的缓存,是默认不开启的
二级缓存的开启需要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的。
也就是要求实现Serializable接口,配置方法很简单,只需要在映射XML文件配置 <cache/> 就可以开启 二级缓存了。
验证:
1)配置核心文件:

2)配置UserMapper.xml映射

运行结果:

小结:

  1. 一级缓存(SqlSession 级缓存)

    • 作用范围:仅限当前 SqlSession(MyBatis 与数据库的会话对象),不同 SqlSession 之间的缓存相互独立。

    • 生命周期:随 SqlSession 创建而生效,随 SqlSession 关闭(close())或提交事务(commit())而失效。

    • 触发逻辑:默认自动开启,当多次执行相同的 SQL 语句和参数时,第一次查询后结果会存入一级缓存,后续查询直接从缓存获取。

  2. 二级缓存(Mapper 级缓存)

    • 作用范围:针对整个 Mapper 接口(如 UserMapper),所有使用该 Mapper 的 SqlSession 共享缓存数据。

    • 生命周期:独立于 SqlSession,随应用启动而创建,随应用关闭而销毁(或手动清除)。

    • 触发逻辑:需在 Mapper XML 中添加 <cache/> 标签开启,且实体类需实现 Serializable 接口(支持序列化存储)。

 

http://www.dtcms.com/a/503500.html

相关文章:

  • 一次渗透测试的全过程:从扫描到提权
  • 英语作文网站济南专业做公司网站的机构
  • 织梦后台做的网站怎么绑定域名做瞹视频网站
  • 网站悬浮代码做柱状图饼状图好看的网站
  • 2510d,C++与d互操作
  • 移动端漂亮网站今天出入济南最新通知
  • UV紫外相机的简单介绍和场景应用
  • 做公众号用什么网站吗404错误页面放在网站的哪里
  • uni-app 入门学习教程,从入门到精通, uni-app常用API的详细语法知识点(上)(5)
  • 设计模式篇之 访问者模式 Visitor
  • 疾控网站建设宗旨和目的wordpress设置为繁体字
  • 免费视频素材网站有哪些游戏制作公司
  • 09_Windows平台Redis开发环境配置完整指南
  • 小谈:数据地图在制造企业的应用
  • 网站建设行业分析报告学校为什么要做网站
  • 手机特殊网站wordpress 环境
  • 使用Linux系统函数递归遍历指定目录
  • h5游戏免费下载:龟兔再跑
  • opendds初入门之qos策略初了解(有遗留)
  • 多视图几何--立体匹配--Gipuma
  • C++智能指针全面解析:原理、使用场景与最佳实践
  • C++指针使用
  • 内江规划建设教育网站国家企业信用公示信息网官网
  • 深入理解 lscpu 命令:如何准确查看 CPU 信息
  • 网站建设需要什么人希腊网站后缀
  • DSync for Mac 文件对比同步工具
  • 「日拱一码」123 内嵌神经网络ENNs
  • C++与易语言开发的基础要求分享
  • 上海市住宅建设发展中心网站建设网站有何要求
  • 广州企业网站建设公司哪家好wordpress改html5