文章目录
- 1.Mybatis介绍
- 1.1Mybatis历史
- 1.2Mybatis特点
- 1.3与其他持久化框架对比
- 1.4对象关系映射——ORM
- 2.搭建Mybatis
- 2.1引入依赖
- 2.2创建核心配置文件
- 2.3创建表、实体类、mapper接口
- 2.4创建映射文件
- 2.4.1映射文件命名位置规则
- 2.4.2编写映射文件
- 2.4.3修改核心配置文件中映射文件路径
- 2.5Junit测试
- 2.6修改、删除、查询功能测试
- 3.核心配置文件详讲(了解)
- 3.1environment、properties讲解
- 3.2typeAliases讲解
- 3.3mappers
- 3.3.1将UserMapper.xml转移到新目录下
- 3.3.2在核心配置文件中引入映射文件
- 4.MyBatis获取参数值的两种方式(重要)
- 4.1区分${}和#{}
- 4.4.1区分点
- 4.4.2预编译过程
- 4.4.3 ${}的sql注入问题
- 4.2获取参数值的各种情况
- 4.2.1情况一:参数为单个的字面量类型
- 4.2.2情况二:参数为多个时
- (1)使用原生的map
- (2)使用自己的map(${}、#{})
- 4.2.3参数是实体类类型的参数
- 4.2.4使用@Param注解命名参数(以这个和实体类类型两种情况为标准)
- 4.3举例
- 4.3.1mapper接口
- 4.3.2映射文件
- 4.3.3测试
- 4.4小结
- 4.5@Param标识的源码解析
- 5.MyBatis的各种查询功能
- 5.1查询的数据只有一条
- 5.1.1接收情况
- 5.1.2举例
- 5.2查询的数据有多条
- 5.3查询单个数据(聚合函数)
- 6.特殊SQL的执行
- 6.1模糊查询
- 6.2批量删除
- 6.3动态设置表名(会出现sql注入问题)
- 6.4添加功能获取自增的主键
- 7.自定义映射resultMap
- 7.1搭建mybatis框架
- 7.2字段名和属性名不一致
- 7.2.1给字段名起别名
- 7.2.2全局配置mapUnderscoreToCamelCase
- 7.2.3resultMap(建议使用)
- 7.3多对一(从员工到部门)映射关系
- 7.3.1通过级联属性(连接查询)赋值
- 7.3.2通过association标签
- 7.3.3通过分步查询(推荐)
- (1)通过id查询员工信息、并查出did
- (2)通过did查询员工所对应的部门
- (3)测试
- 7.3.4分步查询的好处:延迟加载
- (1)开启延迟加载之前
- (2)开启全局延迟加载之后
- (3)可以局部关闭延迟加载
- 7.4.一对多映射关系
- 7.4.1通过collection解决一对多映射关系
- 8.动态SQL
- 8.1if-where-trim标签
- 8.1.1if标签
- 8.1.2where标签
- 8.1.3trim标签
- 8.2choose-when-ohterwise标签
- 8.3.foreach标签
- 8.3.1实现批量删除方式一
- 8.3.2实现批量删除方式二
- 8.3.3实现批量添加
- 8.4SQL标签
- 9.MyBatis缓存
- 9.1介绍
- 9.1.1一级缓存
- 9.1.2二级缓存
- 9.2一级缓存
- 9.2.1一级缓存生效(默认生效)
- 9.2.2一级缓存失效
- 9.3二级缓存
- 9.3.1二级缓存开启
- 9.3.2二级缓存失效
- 9.3.3二级缓存的相关配置
- 9.4缓存查询的顺序
- 10_MyBaits的逆向工程
- 10.1创建逆向工程的步骤
- 10.2豪华尊享版两种函数的区别
- 10.3QBC查询
- 10.4QBC修改
- 11_MyBatis分页
- 11.1使用步骤
- 11.2使用
框架系列第一篇主要围绕MyBatis展开,从MyBatis介绍、搭建学习环境,再到探讨映射文件获取参数值(${}、#{})@param注解命名参数、各种查询功能(1条、多条、单个数据)、再到特殊SQL的执行(模糊查询、批量删除)、自定义resultMap(重点讲述了多对一、一对多的解决方案)、动态SQL(if、where、foreach标签)、MyBatis的一二级缓存
1.Mybatis介绍
1.1Mybatis历史
- 原是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation 迁移到了Google Code,随着开发团队转投Google Code旗下, iBatis3.x正式更名为MyBatis ,代码于2013年11月迁移到Github(下载地址见后)
- iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。 iBatis提供的持久层框架包括SQL Maps和Data Access Obje
1.2Mybatis特点
- Mybatis是一个半自动的ORM(Object Relation Mapping)框架
- 面向接口编程思想:可以使用简单的XML或注解用于配置和原始映射,将接口和Java的实体类对象映射成数据库中的记录
- 支持定制化SQL、存储过程以及==高级映射(一对多的映射关系)==的持久化框架
- 封装了几乎所有的JDBC代码和手动设置参数以及获取结果集
1.3与其他持久化框架对比
- JDBC
- SQL夹杂在Java代码中耦合度高,导致硬编码内伤。
- 维护不易且实际开发需求中 SQL有变化,频繁修改的情况多见。
- 代码冗长,开发效率低。
- Hibernate和IPA
- 操作简便,开发效率高
- 程序中的长难复杂 SQL需要绕过框架
- 内部自动生产的 SQL,不容易做特殊优化。
- 基于全映射的全自动框架,大量字段的 POJ0 进行部分映射时比较困难
- 反射操作太多,导致数据库性能下降
- MyBatis
- 轻量级,性能出色
- SQL和Java 编码分开,功能边界清晰。Java代码专注业务、SQL语句专注数据。
- 开发效率稍逊于HIbernate,但是完全能够接受
1.4对象关系映射——ORM
ORM即Object Relationship Mapping,对象关系映射!
解释/含义 | |
---|---|
对象 | java的实体类对象 |
关系 | 关系型数据库 |
映射 | 二者之间的对应关系 |
在java概念和数据库概念中的对应关系如下:
java概念 | 数据库概念 |
---|---|
类 | 表 |
属性 | 字段/列 |
对象 | 记录/行 |
2.搭建Mybatis
搭建一个基于Maven的project
2.1引入依赖
<dependencies>
<!-- Mybatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
2.2创建核心配置文件
- 习惯上命名为mybatis-config.xml,这个文件名仅仅只是建议,并非强制要求。
- 将来整合Spring之后,这个配置文件可以省略
- 核心配置文件主要用于配置连接数据库的环境以及MyBatis的全局配置信息核心配置文件存放的位置是src/main/resources目录下
2.3创建表、实体类、mapper接口
MyBatis 中的 mapper 接口相当于以前的 DAO 接口,但是区别在于, mapper 仅仅是接口,我们不需要提供实现类(面向接口编程);
2.4创建映射文件
2.4.1映射文件命名位置规则
- 表所对应的 实体类的类名+Mapper.xml ,例如:表 t_user,映射的实体类为 User,所对应的映射文件为 UserMapper.xml;放到resources目录下
- 因此一个映射文件对应一个实体类,对应一张表的操作;
- MyBatis 映射文件用于编写 SQL,访问以及操作表中的数据;
2.4.2编写映射文件
MyBatis中面向接口编程的两个一致:
-
==映射文件的namespace要和mapper接口的全类名保持一致==
-
==映射文件中SQL语句的id要和mapper接口中的方法名一致==
首先先根据核心配置文件中的来引入映射文件,再根据方法名匹配到SQL语句
2.4.3修改核心配置文件中映射文件路径
若修改核心配置文件中的映射文件路径,则需将UserMapper.xml文件放置在resources/mapper文件夹下
2.5Junit测试
2.6修改、删除、查询功能测试
- UserMapper.java
- UserMapper.xml
3.核心配置文件详讲(了解)
MyBatis核心配置文件中,标签的顺序
properties?,settings?,typeAliases?,typeHandlers?,
objectFactory?,objectWrapperFactory?,reflectorFactory?,
lugins?,environments?,databaseIdProvider?,mappers?
3.1environment、properties讲解
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--
MyBatis核心配置文件中,标签的顺序:
properties?,settings?,typeAliases?,typeHandlers?,
objectFactory?,objectWrapperFactory?,reflectorFactory?,
plugins?,environments?,databaseIdProvider?,mappers?
-->
<!--引入properties文件,此时就可以${属性名}的方式访问属性值-->
<properties resource="jdbc.properties" />
<!--设置类型别名-->
<typeAliases>
<!--
typeAlias:设置某个类型的别名
属性:
type:设置需要设置别名的类型
alias:设置某个类型的别名,若不设置该属性,那么该类型拥有默认的别名,即类名且不区分大小写
-->
<!--<typeAlias type="com.atguigu.User"></typeAlias>-->
<!--以包为单位,将包下所有的类型设置默认的类型别名,即类名且不区分大小写-->
<package name="com.atguigu.mybatis.pojo"/>
</typeAliases>
<!--
environments:配置多个连接数据库的环境
属性:
default:设置默认使用的环境的id
-->
<environments default="development">
<!--
environment:配置某个具体的环境
属性:
id:表示连接数据库的环境的唯一标识,不能重复
-->
<environment id="development">
<!--
transactionManager:设置事务管理方式
属性:
type="JDBC|MANAGED"
JDBC:表示当前环境中,执行SQL时,使用的是JDBC中原生的事务管理方式,事务的提交或回滚需要手动处理
MANAGED:被管理,例如被Spring管理
-->
<transactionManager type="JDBC"/>
<!--
dataSource:配置数据源,与spring整合后不用再设置dataSource
属性:
type:设置数据源的类型
type="POOLED|UNPOOLED|JNDI"
POOLED:表示使用数据库连接池缓存数据库连接,即会将创建的连接进行缓存,下次使用可以从缓存中直接获取,不需要重新创建
UNPOOLED:表示不使用数据库连接池,即每次使用连接都需要重新创建
JNDI:表示使用上下文中的数据源
-->
<dataSource type="POOLED">
<!--设置连接数据库的驱动-->
<property name="driver" value="${jdbc.driver}"/>
<!--设置连接数据库的连接地址-->
<property name="url" value="${jdbc.url}"/>
<!--设置连接数据库的用户名-->
<property name="username" value="${jdbc.username}"/>
<!--设置连接数据库的密码-->
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<!--<mapper resource="mappers/UserMapper.xml"/>-->
<!--
以包为单位引入映射文件
要求:
1、mapper接口所在的包要和映射文件所在的包一致
2、mapper接口要和映射文件的名字一致
-->
<package name="com.atguigu.mybatis.mapper"/>
</mappers>
</configuration>
3.2typeAliases讲解
作用:设置类型别名
<!-- 设置类型别名
方式一:以某一个类为单位(不常用)
方式二:以包为单位,将包下所有的类型设置为默认类型别名,即类型且不区分大小写(常用)
-->
<typeAliases>
<!--
方式一:
typeAlias:设置某个类型的别名
属性:
type:设置需要设置别名的类型
alias:设置某个类型的别名,如果不设置该属性,那么该类型拥有默认的别名,即类名而且不区分大小写
-->
<typeAlias type="com.peng.mybatis.pojo.User" alias="User"></typeAlias>
<!--
方式二:
-->
<package name="com.peng.mybatis.pojo.User"></package>
</typeAliases>
3.3mappers
3.3.1将UserMapper.xml转移到新目录下
在 resources 包下面创建 directory(目录) 和创建 package(包) 是不一样的
不能用com.peng.mybatis.mapper 这种方式创建多个文件夹,这样创建出来的是一个包名;应该用 com/peng/mybatis/mapper
这种方式。
3.3.2在核心配置文件中引入映射文件
<!-- 引入映射文件
以包为单位引入映射文件
要求:mapper接口所在的包要和映射文件所在的包一致
mapper接口要和映射文件的名字一致
-->
<mappers>
<package name="com.peng.mybatis.mapper"/>
</mappers>
4.MyBatis获取参数值的两种方式(重要)
4.1区分${}和#{}
4.4.1区分点
-
MyBatis获取参数值的两种方式:${}和#{}
${}本质为字符串替换
#{}本质为占位符赋值
${}和#{}的相同点为都能获取参数变量的值,在预编译语法上则不同
${} | #{} | |
---|---|---|
预编译 | 不行 | 将#{变量}编译成?,在执行时后再取值(自动加上引号),防止sql注入 |
应用场景 | 传入的参数是sql片段的场景 | 在sql映射文件中动态拼接sql时的开发场景(获取变量) |
传入的参数是sql片段的场景
<select id="getUserPage" resultType="com.ymxx.oa.bean.User">
${sql片段}
</select>
- 对于这样外部传入的sql,就不能使用#{},上面也说了,#{}会进行预编译,检测到该sql片段是个字符串,就会加上引号,即’sql片段’,这样就是字符串了而不是sql,执行会报错。
4.4.2预编译过程
mybatis在预编译过程大体上可以分为 数据类型检查和安全检查 两部分
数据类型检查:若检测到为数值类型,就不加引号,即?;若检测到位字符串类型,就加上引号,即’?'。
安全检查:若变量的值带有引号,会对引号进行转义处理,这样可以防止sql注入
4.4.3 ${}的sql注入问题
用${}时要特别注意sql注入的风险,如果该sql片段是根据用户的输入拼接的,要注意检查sql注入的问题,防止数据的泄露与丢失!
案例:
//sql语句
select * from ${tableName} where name = ${name}
如果tableName的值为 user; delete user; –,该sql最终解析成
select * from user; delete user; -- where name = xxx
结果查询了整张表,然后把user表给删了
4.2获取参数值的各种情况
获取参数值的情况可以分为以下四种情况:
- 参数为单个的字面量类型
- 参数为多个时
- 使用原生的map
- 使用自己的map(${}、#{})
- 参数是实体类类型的参数
- 使用@Param注解命名参数
4.2.1情况一:参数为单个的字面量类型
- 可以通过KaTeX parse error: Expected 'EOF', got '#' at position 4: {}和#̲{}以任意的字符串获取参数值 …{}需要额外加上单引号
4.2.2情况二:参数为多个时
(1)使用原生的map
- mybatis会把形参的值放在一个map集合中,以两种方式进行存储
- 以arg0,arg1为键或者以param0,param1为键,
- 以参数值为值,可以混着用
(2)使用自己的map(${}、#{})
- 使用自己的map可以通过KaTeX parse error: Expected 'EOF', got '#' at position 4: {}和#̲{}以键名的方式获取参数值==…{}需要额外加上单引号
- 可以==通过KaTeX parse error: Expected 'EOF', got '#' at position 4: {}和#̲{}以自己设置的键名的方式获取…{}需要额外加上单引号
4.2.3参数是实体类类型的参数
- 通过${}和#{}以自己设置的键名(实体类里面的属性)的方式获取参数值
- 使用${}需要额外加上单引号
4.2.4使用@Param注解命名参数(以这个和实体类类型两种情况为标准)
- mybatis会把形参的值放在一个map集合中,以两种方式进行存储
- 以自己使用的==@Param注解的参数名为键或者以param1,param2为键==,
- 以参数值为值,可以混着用
4.3举例
4.3.1mapper接口
4.3.2映射文件
4.3.3测试
4.4小结
-
有实体类的时候直接用属性值来访问;
例如,如果有一个
User
实体类,其中包含了id
和name
两个属性,那么在 SQL 语句中就可以直接引用这些属性:<select id="getUserById" resultType="User"> SELECT * FROM users WHERE id = #{id} </select>
-
没有实体类的时候用 @Param 注解命名参数,不管字面量单个还是多个,是不是 map 集合都可以使用自己用注解定义的参数来访问。
当没有实体类或需要额外命名参数时,可以使用
@Param
注解来命名参数。例如,假设我们需要在 SQL 中引用多个参数,可以这样做:- mapper接口:
public interface UserMapper { List<User> getUsersByAgeRange(@Param("minAge") int minAge, @Param("maxAge") int maxAge); }
-
映射文件:
<select id="getUsersByAgeRange" resultType="User"> SELECT * FROM users WHERE age BETWEEN #{minAge} AND #{maxAge} </select>
4.5@Param标识的源码解析
为啥我们能将我们想要的 key 值,以及对应的 value 值设置进去呢?
(1)首先看这个方法的第三步的内部是如何调用的?
(2) mapper 映射的底层使用了代理模式,通过反射执行当前命令对应的方法
- MapperProxy.java
(3)当前命令中,name 对应的是要执行的 sql 语句(唯一标识:mapper 映射文件中 namespace + id),方法对应的是 select 方法,所以 switch - case 直接跳到 select 方法去执行对应的方法:
- MapperMethod.java
这边在 else 模块中,第一个方法是将 args 参数转换成 sql 要求的参数,点进这个方法看一下里面怎么写的?
再进入方法内部,这个方法是一个将注解设定的命名规则的名称设置成 map 容器的 key 值的方法
- ParamNameResolver.java
第一步:name 是一个排序的 map 容器,其中 0 号位置放了我们通过注解命名的第一个参数名称,1 号位置放置了我们通过注解命名的第二个参数名称:
第二步:又因为我们有参数注解而且参数注解的个数不为1,所以跳到 else 中执行,新建一个 map 容器用来放置 (键值,参数值);
第三步:遍历 names 这个 map 容器,取出其中第一个键值对;
第四步:将它的 value 值即放置的自定义的 username 注解名称当作键值,将 args[当前键值对的键值] 即 args[0] 当作 value 值放进 params 这个 map 容器中;
第五步:定义一个字符串 param1,这里的 1 表示的是遍历第一个键值对即 i = 0;
第六步:如果当前 names 这个 map 容器中没有包含当前的这个 param1 这个值(防止我们自己定义了,重复放置),那么我们就将(param1 ,args[0])这一个键值对放进 param 这个 map 容器中,也就是我们既可以用我们自己设定的 username 来访问对应的参数值,也可以用 param1 来访问对应的参数值。
第七步:i++,遍历 names 容器中的下一个键值对,重复执行 4 - 6;
第八步:最后将生成的 param 这个 map 容器返回,我们可以从中任意选择一个键值来访问对应的参数。
5.MyBatis的各种查询功能
5.1查询的数据只有一条
5.1.1接收情况
若查询的数据只有一条,那可以分为以下三种情况:
- 通过实体类对象接收
- 通过list集合接收(建议使用list集合接收)
- 通过map集合接收
5.1.2举例
- mapper 接口中方法的定义:
-
mapper 映射文件中 sql 语句的编写:
注意:通过map集合接收的sql语句的 resultType值 必须修改为map
- 测试类
5.2查询的数据有多条
若查询的数据有多条,那可以分为以下三种情况:
-
通过实体类类型的list集合接收
-
通过map类型的list集合接收
-
通过注解@MapKey注解实现注入
可以在mapper接口的方法上添加@MapKey注解,此时就可以将每条数据转换为map集合作为值,以某个字段的值作为键,放在同一个map集合中
- mapper接口中方法的定义
- mapper 映射文件中 sql 语句的编写:
- 测试
5.3查询单个数据(聚合函数)
- mapper 接口中方法的定义:
- mapper 映射文件中 sql 语句的编写:
- 测试
补充:MyBatis 中设置了默认的类型别名,可以在resultType使用别名
java.lang.Integer --> int,integer
int --> _int,_integer
Map --> map
String --> string
6.特殊SQL的执行
6.1模糊查询
模糊查询语句:
select * from tb_user where username like '%鹏%';
重点在如何解析出鹏字,由前面可知,${}本质为字符串替换、#{}本质为占位符(会进行预编译,若在数据类型检查中,检查为字符串类型,则会自动添加’‘引号)
mapper 映射文件中 sql 语句的编写,一共有三种方法可以实现:
- 使用${}
- 使用 concat和#{} 进行字符串拼接(推荐)
- 使用"%#{}%"
- mapper 接口中方法的定义:
- 映射文件:
-
这里不可以使用
select * from t_user where username like '%#{username}%'
,即将#{}
这种占位符赋值获取参数值的放在 单引号 里面; -
这样直接将单引号里面的三个字符都当作字符串的一部分,而不会解析成占位符,看报错信息可以看出来,?直接放在单引号内部,当成一个==字符?==了,而不会被当成一个占位符。
-
解析结果:
- 测试
6.2批量删除
- mapper 接口中方法的定义:
- mapper 映射文件中 sql 语句的编写,这里只能用 ${} 这种字符串替换的方法来实现:
- 不能使用 #{}这种方式:
#{}
这种占位符赋值的方式,会自动给参数加上单引号
delete from t_user where id in (#{ids});
# in (1,2,3,4)——>in('1,2,3,4'),结果必定报错
- 测试
6.3动态设置表名(会出现sql注入问题)
- mapper 接口中方法的定义:
- mapper 映射文件中 sql 语句的编写
- 测试
6.4添加功能获取自增的主键
- mapper 接口中方法的定义:
- mapper 映射文件中 sql 语句的编写
- 测试
7.自定义映射resultMap
7.1搭建mybatis框架
-
数据库新建两张表t_emp和t_dept,并存入测试数据
-
t_emp表
-
t_dept表和数据
-
-
新建两个类,其中emp_name对应的属性名为empName
7.2字段名和属性名不一致
解决字段名和属性名不一致的情况的三种方式
- 为字段名起别名
- 全局配置mapUnderscoreToCamelCase(驼峰映射)
- resultMap设置自定义映射关系(推荐)
7.2.1给字段名起别名
7.2.2全局配置mapUnderscoreToCamelCase
- mybatis-config.xml
7.2.3resultMap(建议使用)
7.3多对一(从员工到部门)映射关系
定义:多对一的映射关系指的是一个对象(或实体)在关联另一个对象时,该对象拥有多个对应关联对象的情况。具体来说,多对一关系表示多个对象共同指向同一个对象
举例:假设有两个实体类 Department
和 Employee
,一个部门可以有多名员工,而一名员工只能属于一个部门。在这种情况下,从 Employee
到 Department
的关系就是多对一的映射关系,因为多名员工可以指向同一个部门。
处理多对一的映射关系可以有以下几种方法:
通过 级联属性 赋值
通过 association标签
通过 分步查询
7.3.1通过级联属性(连接查询)赋值
- 员工对象
- 部门对象
- mapper接口中方法定义
- mapper映射文件
7.3.2通过association标签
- mapper映射文件
- 测试
7.3.3通过分步查询(推荐)
(1)通过id查询员工信息、并查出did
- EmpMapper接口方法定义
- EmpMapper.xml映射文件
解释:
- association 标签中的 column 属性就将两个查询到的信息关联了起来;
- select 属性中写的是 mapper 接口的全类名(唯一标识);
(2)通过did查询员工所对应的部门
- DeptMapper接口方法定义
- DeptMapper.xml映射配置文件,注意字段名和属性名并不相同
(3)测试
7.3.4分步查询的好处:延迟加载
-
可以实现 延迟加载,但是必须在核心配置文件中设置全局配置信息(配置如下两个信息):没有全局设置的话,默认的是立即加载;
-
lazyLoadingEnabled
:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载;
需要 设置为true,默认为false。
-
aggressiveLazyLoading
:设置为false,默认为false。当开启时,任何方法的调用都会加载该对象的所有属性。 ,需要关闭他。关闭后每个属性会按需加载。
(1)开启延迟加载之前
(2)开启全局延迟加载之后
- mybatis-config.xml
(3)可以局部关闭延迟加载
条件:全局延迟加载开启
7.4.一对多映射关系
- 定义:一对多关系表示一个对象指向多个相关联的对象
- 假设有两个实体类
Department
和Employee
,一个部门可以有多名员工,而一名员工只能属于一个部门。在这种情况下,从Department
到Employee
的关系就是一对多的映射关系,因为一个部门可以指向多名员工。
7.4.1通过collection解决一对多映射关系
- DeptMapper接口方法定义
- DeptMapper.xml映射配置文件
- 测试
8.动态SQL
在 MyBatis 中,可以使用动态 SQL 来根据条件生成不同的 SQL 语句
-
if:根据标签中test属性所对应的表达式决定标签中的内容是否需要拼接到 SQL 语句中
-
where:
-
当where标签中有内容时,会自动生成where关键字,并且将内容前多余的and或者or去掉
-
当where标签中没有内容时,此时where标签没有任何效果
-
注意:where标签不能将其中内容后面多余的and或者or去掉,只能将内容前多余的and或者or去掉
-
-
trim:
- prefix|suffix:将trim标签中内容前面或者后面添加指定内容
- prefixOverrides|suffixOverrides:将trim标签中内容前面或者后面去掉指定内容
- 若标签中没有任何内容时,trim标签也没有任何效果
-
choose、when、otherwise
- 相当于if…else if…else if…otherwise
- 其中when至少要有一个,otherwise最多只能有一个
-
foreach
- collection:设置需要循环的数组或者集合
- item:表示数组或者集合中的每一个数据
- separator:循环体之间的分隔符\
- open:foreach标签所循环的所有内容的开始符
- close:foreach标签所循环的所有内容的结束符
8.1if-where-trim标签
8.1.1if标签
8.1.2where标签
8.1.3trim标签
8.2choose-when-ohterwise标签
choose、when、otherwise,相当于if…else if…else
8.3.foreach标签
8.3.1实现批量删除方式一
8.3.2实现批量删除方式二
8.3.3实现批量添加
8.4SQL标签
-
设置SQL片段:
<sql id="empColumns">eid,emp_name,age,sex,email</sql>
-
引用SQL片段:
<include refid="empColumns"></include>
9.MyBatis缓存
9.1介绍
MyBatis 中的缓存主要分为一级缓存和二级缓存,它们的作用是提高数据库访问效率,减少不必要的数据库查询操作。
9.1.1一级缓存
- 一级缓存是指在同一个 SqlSession 中执行相同的查询操作时,第二次及以后的查询会直接从缓存中获取结果,而不会再次查询数据库。
- 一级缓存是默认开启的,且无法关闭。它的范围是 SqlSession 级别的,即在同一个 SqlSession 中共享缓存。
- 一级缓存可以通过清除缓存的方式来刷新数据,比如调用
clearCache()
方法。
9.1.2二级缓存
- 二级缓存是指多个 SqlSession 共享相同的缓存,可以跨 SqlSession 进行数据共享。
- 使用二级缓存需要在映射文件中配置
<cache>
标签,并在 MyBatis 配置文件中开启二级缓存功能。 - 二级缓存的范围是 Mapper 级别的,即同一个 Mapper 接口下的多个 SqlSession 可以共享缓存。
- 二级缓存对于经常被访问且不经常改变的数据非常有效,但在数据频繁更新的情况下,需要考虑缓存的有效性和数据一致性。
9.2一级缓存
9.2.1一级缓存生效(默认生效)
一级缓存是 SqlSession 级别 的,通过 同一个 SqlSession 查询 的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问。
- mapper接口方法定义
- mapper接口映射文件
- 测试,同一个sqlSession是去缓存中取数据
9.2.2一级缓存失效
使得一级缓存失效的四种情况
- 不同的SqlSession对应不同的一级缓存
- 同一个SqlSession但是查询条件不同
- 同一个SqlSession两次查询期间执行了任何一次增删改操作,数据发生了变化
- 同一个SqlSession两次查询期间手动清空了缓存
9.3二级缓存
二级缓存是 SqlSessionFactory 级别,通过 同一个 SqlSessionFactory 创建的 SqlSession 查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取。
9.3.1二级缓存开启
(1)在核心配置文件中,设置全局配置属性 cacheEnabled=“true”,默认为 true,不需要设置;
(2)在映射文件中设置标签 <cache />
;
(3)二级缓存必须在 SqlSession 关闭或提交之后有效;
(4)查询的数据所转换的实体类类型必须实现序列化的接口。
- 测试
9.3.2二级缓存失效
失效情况:两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
9.3.3二级缓存的相关配置
9.4缓存查询的顺序
- 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用
- 如果二级缓存没有命中,再查询一级缓存
- 如果一级缓存也没有命中,则查询数据库
- SqlSession关闭之后,一级缓存中的数据会写入二级缓存
10_MyBaits的逆向工程
10.1创建逆向工程的步骤
(1)添加依赖和插件,这里的 mybatis 和 JDBC 换成你自己对应的版本:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.peng.mybatis</groupId>
<artifactId>MyBatis_demo04</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!-- 依赖MyBatis核心包 -->
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.ibatis</groupId>
<artifactId>ibatis-core</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
</dependencies>
<!-- 控制Maven在构建过程中相关配置 -->
<build>
<!-- 构建过程中用到的插件 -->
<plugins>
<!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.0</version>
<!-- 插件的依赖 -->
<dependencies>
<!-- 逆向工程的核心依赖 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
(2)创建 MyBatis 的核心配置文件 mybatis-config.xml
;
(3)创建逆向工程的配置文件,文件名必须是:generatorConfig.xml
(修改数据库名称和密码、修改包名、修改对应的数据库表名)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--
targetRuntime: 执行生成的逆向工程的版本
MyBatis3Simple: 生成基本的CRUD(清新简洁版),只有增删改查(查所有和根据 Id 查)5个方法
MyBatis3: 生成带条件的CRUD(奢华尊享版)
-->
<context id="DB2Tables" targetRuntime="MyBatis3">
<!-- 数据库的连接信息 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis"
userId="root"
password="123456">
</jdbcConnection>
<!-- javaBean的生成策略-->
<javaModelGenerator targetPackage="com.peng.mybatis.pojo"
targetProject=".\src\main\java">
<!--true生成的是子包,false生成的是一个文件夹名-->
<property name="enableSubPackages" value="true"/>
<!-- 去掉字符串前后的空格-->
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- SQL映射文件的生成策略 -->
<sqlMapGenerator targetPackage="com.peng.mybatis.mapper"
targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!-- Mapper接口的生成策略 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.peng.mybatis.mapper" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!-- 逆向分析的表 -->
<!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
<!-- domainObjectName属性指定生成出来的实体类的类名,会自动在后面补上Mapper -->
<table tableName="t_emp" domainObjectName="Emp"/>
<table tableName="t_dept" domainObjectName="Dept"/>
</context>
</generatorConfiguration>
- MyBatis3Simple: 生成基本的CRUD(清新简洁版),只有增删改查(查所有和根据 Id 查)5个方法
- MyBatis3: 生成带条件的CRUD(奢华尊享版)
(5)执行MBG插件的generate目标
10.2豪华尊享版两种函数的区别
10.3QBC查询
- 报了错误Error:(6, 37) java: 程序包org.apache.ibatis.annotations不存在,网上找不到解决方法,怀疑是配置文件里面版本问题
10.4QBC修改
- and 条件都是链式添加的;
- or 条件需要换行,调用 or() 方法之后再调用 and() 方法。
11_MyBatis分页
11.1使用步骤
- 在核心配置文件中配置分页插件
<plugins>
<!--设置分页插件-->
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
- 在pom.xml中添加依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
11.2使用
public class PageHelperTest {
/**
* limit index,pageSize
* index:当前页的起始索引
* pageSize:每页显示的条数
* pageNum:当前页的页码
*
* index=(pageNum-1)*pageSize
*
* 使用MyBaits的分页插件实现分页功能
* 1.需要在查询功能之前开启分页,2代表了当前页码,4代表当前页显示的数据
* PageHelper.startPage(2,4);
* 2.在查询功能之后获取分页相关信息
* PageInfo<Emp> page=new PageInfo<>(list,5);
* list代表分页数据
* 5代表当前导航分页的数量
*/
@Test
public void testPageHelper(){
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//开启分页功能
//查询详细分页信息方式一:
// Page<Object> page = PageHelper.startPage(2, 4);
// System.out.println(page);
PageHelper.startPage(2,4);
//查询所有数据
List<Emp> list = mapper.selectByExample(null);
list.forEach(emp-> System.out.println(emp));
//查询详细分页信息方式二,其中5代表导航数
PageInfo<Emp> page=new PageInfo<>(list,5);
System.out.println(page);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Page<Object> page = PageHelper.startPage(2, 4);
,这个函数返回的 page 是包含少量信息的 page数据;PageInfo<Emp> page = new PageInfo<>(list, 5);
,展示非常详细的 pageInfo 信息,里面包含 page 信息。
常用数据:
- pageNum:当前页的页码
- pageSize:每页显示的条数
- size:当前页显示的真实条数(有可能每页显示 5 条,但是最后一页只剩 2 条数据这样子)
- total:总记录数
- pages:总页数
- prePage:上一页的页码
- nextPage:下一页的页码
- isFirstPage/isLastPage:是否为第一页/最后一页
- hasPreviousPage/hasNextPage:是否存在上一页/下一页
- navigatePages:导航分页的页码数
- navigatepageNums:导航分页的页码,[1,2,3,4,5]