MyBatis
1 MyBatis 概念
为什么要使用MyBatis
:- 编码繁琐
- 需要我们自己将结果集映射成对象
- 性能不太好 连接池 缓存
- SQL语句和java代码的耦合度特别高
- ….
介绍
- MyBatis 本是Apache的一个开源项目iBatis, 2010年这个项目由Apache Software Foundation 迁移到了Google Code,且改名为MyBatis
- 2013年11月迁移到GitHub。iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架
- MyBatis 是一款优秀的
持久层
框架,它支持定制化 SQL、存储过程以及高级映射
- MyBatis 避免了几乎所有的
JDBC
代码和手动设置
参数以及获取结果集 - MyBatis 可以使用简单的
XML
或注解
来配置和映射原生信息,将接口和 Java 的POJOs(Plain Ordinary Java Object,普通的 Java对象)
映射成数据库中的记录
1.1 认识框架
框架(Framework)
是一个框子——指其约束性,也是一个架子——指其支撑性。是一个基本概念上的结构,用于去解决或者处理复杂的问题。框架这个广泛的定义使用的十分流行,尤其在软件概念框架(Framework)
对于Java
来说,就是一系列为了解决特定问题而定义的一系列接口和实现类,在组织框架代码时,使用了一系列优秀的设计模式,使代码无论在性能上还是API操作上得到很大提升.优点
:- 减少开发时间、降低开发难度,并且还保证设计质量
- 降低程序员之间沟通以及日后维护的成本
总结
:- 总之,框架是一个半成品,已经对基础的代码进行了封装并提供相应的API,开发者在使用框架是直接调用封装好的API可以省去很多代码编写,从而提高工作效率和开发速度
1.2 认识ORM
传统 JDBC 的缺点
:- 需要手动的完成面向对象的Java语言、面向关系的数据库之间数据的转换,代码繁琐无技术含量,影响了开发效率
- 比如查询需要手动的将结果集的列数据转换为Java对象的属性;而添加操作时需要手动将Java对象的属性转换为数据库表的列字段
ORM,Object-Relationl Mapping
,对象关系映射,它的作用是在关系型数据库和对象之间作一个映射优点
- 操作数据库的时候,只要像平时操作对象一样操作它,ORM框架会根据映射完成对数据库的操作,不需要写复杂的SQL语句
1.2.1 持久化
持久(Persistence)
,即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)持久化的主要应用
- 是将内存中的数据存储在关系型的数据库中,当然也可以存储在磁盘文件中、XML数据文件中等等
1.2.2 持久层
持久层(Persistence Layer)
,即专注于实现数据持久化应用领域
的某个特定系统的一个逻辑层面,将数据使用者和数据实体相关联
- 之前使用JDBC访问数据库的DAO层,后面采用MyBatis访问数据库的mapper层,就是持久层
1.3 认识 MyBatis
MyBatis
是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射
MyBatis
避免了几乎所有的JDBC
代码和手动设置
参数以及获取结果集MyBatis
可以使用简单的XML
或注解
来配置和映射原生信息,将接口和 Java 的POJOs(Plain Ordinary Java Object,普通的 Java对象)
映射成数据库中的记录
2 MyBatis 快速入门
2.1 项目结构
2.2 pom 依赖
2.3 准备实体类与数据库
数据库导入即可
实体类:
2.4 添加映射文件
在 resource 目录下 创建 top/mikevane 目录,然后添加DeptMapper.xml映射文件
resources目录下准备sqlMapConfig.xml 核心配置文件
2.5 测试代码
- 在 test 目录下 创建 top/mikevane 目录并新建 Test1 类
3 MyBatis 配置详解
3.1 log4j1 和log4j2的使用
导入依赖
- 可以在MyBatis的配置文件中 配置选择使用的具体的日志实现
3.1.1 log4j 配置文件
在网上可以找到对应的配置文件
将配置文件置于
resource
的根目录即可log4j1 配置文件(properties文件)
log4j2 配置文件(xml文件)
log4j 的日志等级级别
级别 | 描述 |
---|---|
ALL | 所有级别包括自定义级别。 |
DEBUG | 调试消息日志。 |
ERROR | 错误消息日志,应用程序可以继续运行。 |
FATAL | 严重错误消息日志,必须中止运行应用程序。 |
INFO | 信息消息。 |
OFF | 最高可能的排名,旨在关闭日志记录。 |
TRACE | 高于DEBUG。 |
WARN | 用于警告消息。 |
3.2 事务配置(transactionManager)
在 MyBatis 中有两种类型的事务管理器(也就是 type=”[JDBC|MANAGED]”):
JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。例如:
3.3 映射文件的加载方式(mappers)
- 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件
- mapper映射文件的文件路径导入 使用的mapper标签的resource属性
- 网络资源路径 使用的mapper标签的url属性
- 接口的全限定名导入 使用的是mapper标签的class属性 (基于接口的代理模式开发)
- 包扫描形式加载所有的mapper映射文件 使用的是 package标签
3.4 实体类别名(typeAliases)
- 类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
这样 当这样配置时,
dept
可以用在任何使用top.mikevane.pojo.Emp
的地方。也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
每一个在包
top.mikevane.pojo
中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如top.mikevane.pojo.Dept
的别名为dept
若有注解,则别名为其注解值。见下面的例子:
3.4.1 常见的 Java 类型内建的类型别名
别名 映射的类型 _byte byte _long long _short short _int int _integer int _double double _float float _boolean boolean string String byte Byte long Long short Short int Integer integer Integer double Double float Float boolean Boolean date Date decimal BigDecimal bigdecimal BigDecimal object Object map Map hashmap HashMap list List arraylist ArrayList collection Collection iterator Iterator
3.5 环境配置
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。
- 例如,开发、测试和生产环境需要有不同的配置;
- 或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射
- 还有许多类似的使用场景。
尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境
3.5.1 SqlSessionFactory 实例
每个数据库对应一个 SqlSessionFactory 实例
为了指定创建哪种环境,只要将它作为可选的参数传递给 SqlSessionFactoryBuilder 即可。可以接受环境配置的两个方法签名是:
- 如果忽略了环境参数,那么将会加载默认环境,如下所示:
3.5.2 引入 properties 属性来导入配置文件
- 在
resource
目录下准备jdbc.properties
属性配置文件,内容如下:
在
MyBatis
配置文件中引入jdbc.properties
配置文件
4. MyBatis 普通模式开发
- 普通模式,也称为传统DAO模式,就是在传统DAO模式下,定义接口和实现类,如 interface EmpDao class EmpDaoImpl implements EmpDao.
- 在实现类中,用SQLSession对象调用 select insert delete update 等方法实现.目前极为少见.
- 在传统模式下,我们需要知道SqlSession对象 实现CURD和 参数传递的处理
4.1 MyBatis 查询的三种方式
SqlSession对象本身的API中就有三个查询方法,分别能够实现如下查询方式
返回单个对象 selectOne
resultType
相当于返回值类型paramaterType
参数类型测试代码
:
返回对象List集合 selectList
测试代码
:
返回对象Map集合 selectMap
测试代码
:
sqlSession.selectMap
传入的参数:第一个为方法名,第二个参数为主键名
4.2 MyBatis 参数传递的三种方式
- MyBatis 在SQL语句上可以使用${} #{} 代表参数的占位
- 如果参数是单个基本数据类型,{}中名字可以随便写,最好
见名知意
${}
:代表 mybatis 底层使用Statment语句对象
,参数是以字符串拼接
的形式设置#{}
:代表mybatis底层使用的preparedStatment
语句对象,参数使用?作为占位符处理
- 如果参数是单个基本数据类型,{}中名字可以随便写,最好
- 单个基础数据类型作为参数
- 多个基础数据类型的map 集合作为参数
传入参数设置
:
接受参数设置
:
#{}中写的是map集合中,参数的键
< > 推荐进行转译处理,参照HTML转译
- 引用类型作为参数
参数是我们自定义的类型,那么 #{}中写的是参数的属性名
4.3 MyBatis 完成 CURD 全部操作
增加
:增删方法的返回值类型都是
int
,resultType
就无需指定了insert update delete
标签中没有resultType
但是仍然可以有
paramaterType
修改
:
删除
:
5 MyBatis 基于接口代理模式开发
前面操作的缺点
:- 不管是selectList()、selectOne()、selectMap(),都是通过SQLSession对象的API完成增删改查,都 只能提供一个查询参数。如果要多个参数,需要封装到
JavaBean
或者Map
中,并不一定永远是一个好办法 返回值类型
较固定- 只提供了
映射文件
,没有提供数据库操作的接口
,不利于后期的维护扩展
- 不管是selectList()、selectOne()、selectMap(),都是通过SQLSession对象的API完成增删改查,都 只能提供一个查询参数。如果要多个参数,需要封装到
解决办法
:- 在MyBatis中提供了另外一种成为Mapper代理(或称为接口绑定)的操作方式,
在实际开发中多使用该方式
- 之后多使用Mapper代理的方式来实现对Emp表的CRUD操作,还包括多个参数传递、模糊查询、自增主键回填等更多的技术实现
- 在MyBatis中提供了另外一种成为Mapper代理(或称为接口绑定)的操作方式,
优点
:- 接口与模块更加规范
- 参数处理变得多样,接口中的方法参数列表由程序员自主决定
- 通过
代理模式
由mybatis
提供接口的实现类对象,可以不写实现类了
5.1 使用Mapper代理方式实现查询
项目结构
:Emp 接口
:
EmpMapper.xml映射文件
- 在
sqlMapConfig.xml
核心配置文件中使用包扫描
形式加载所有的映射文件
测试类
:
6 代理模式下的参数传递问题
6.1 多参数传递
单个基本数据类型
mapper 接口
:
xml 映射文件
:
测试代码
:- ```java
@Test
public void selectByEmpNoTest(){
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.selectByEmpNo(7902);
System.out.println(emp);
}
- ```java
xml 映射文件
:
测试代码
:
单个引用数据类型
mapper 接口
:
xml 映射文件
:
测试代码
:
map集合数据类型
mapper 接口
:
xml 映射文件
:
测试代码
:
多个引用数据类型
mapper 接口
:
xml 映射文件
:
测试代码
:
6.2 模糊查询
- 在进行模糊查询时,在映射文件中可以使用concat()函数来连接参数和通配符。另外注意对于特殊字符,比如<,不能直接书写,应该使用字符实体替换
mapper 接口
xml 映射文件
测试代码
6.3 自增主键回填
- MySQL支持主键自增。有时候完成添加后需要立刻
获取刚刚自增的主键
,由下一个操作来使用。比如结算构造车后,主订单的主键确定后,需要作为后续订单明细项的外键存在。如何拿到主键呢,MyBatis提供了支持,可以非常简单的获取。 mapper 接口
xml 映射文件
测试代码
方法一
useGeneratedKeys
:表示要使用自增的主键keyProperty
:表示把自增的主键赋给JavaBean的哪个成员变量。以添加Dept对象为例,添加前Dept对象的deptno是空的,添加完毕后可以通过getDeptno() 获取自增的主键。
方法二
order
:取值AFTER|BEFORE,表示在新增之后|之前执行<selectKey>中的SQL命令keyProperty
:执行select @@identity
后结果填充到哪个属性中resultType
:结果类型
技术扩展
在很多应用场景中需要新增数据后获取到新增数据的主键值,针对这样的需求一般由三种解决方式:
主键自定义,用户通过
UUID或时间戳
等方式生成唯一主键,把这个值当做主键值。在分布式场景
中应用较多。查询后通过
select max(主键) from 表
获取主键最大值
。这种方式在多线程访问
情况下可能出现问题。查询后通过
select @@identity
获取最新生成主键。要求这条SQL必须在insert操作之后
,且数据库连接没有关闭
。
6.4 实现 CURD 操作
mapper 接口
xml 映射文件
测试代码
7 动态SQL
问题提出
- 经常遇到很多按照很多查询条件进行查询的情况,比如京东根据不同的条件筛选商品。其中经常出现很多条件不取值的情况,在后台应该如何完成最终的SQL语句呢?
- 如果采用JDBC进行处理,需要根据条件是否取值进行SQL语句的拼接,一般情况下是使用StringBuilder类及其append方法实现,还是有些繁琐的
- 如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦
动态SQL优点
- MyBatis 在简化操作方法提出了动态SQL功能,将使用
Java代码拼接
SQL语句,改变为在XML映射文件中截止标签
拼接SQL语句。相比而言,大大减少了代码量,更灵活、高度可配置、利于后期维护。 - MyBatis 中动态SQL是编写在
mapper.xml
中的,其语法和JSTL
类似,但是却是基于强大的OGNL表达式实现
的。 - MyBatis 也
可以在注解中配置SQL
,但是由于注解功能受限
,尤其是对于复杂的SQL语句,可读性很差,所以较少使用
- MyBatis 在简化操作方法提出了动态SQL功能,将使用
7.1 if 标签
设置好需要接收的变量集,根据给定的变量来拼接 if 字符串 (避免出现每一种情况都需要写对应的sql语句)
mapper 接口
xml 映射文件
测试代码
7.2 Where 标签
- 用于处理where关键字和and (用于避免出现无参数或其他情况时出现错误)
mapper 接口
xml 映射文件
测试代码
7.3 Choose 标签
- 类似于 java 中的 switch 语句
mapper 接口
xml 映射文件
测试代码
测试结果
- 分析可得,此时只查询了 mgr 为7698的值,设置的条件 dept=20 并没有生效,说明 MyBatis 的
choose
语句确实与 Java 中的switch
语句很类似,都是满足了前一个条件后,其他条件就不再进行判断了
7.4 Set 标签
- 在我个人看来,设置
set
标签的理由与if
标签很像:如果有传入的数据有各种情况,那么就不可能针对每一种情况进行书写 SQL,所以就诞生了类似于if/set
标签 mapper 接口
xml 映射文件
测试代码
测试结果
- 结果正确,可以根据传入参数来进行动态拼接SQL
7.5 Trim 标签
- 对于
Trim标签
来说,set标签
更像是trim
的子集,因为trim标签
不仅可以做到set
的操作,还可以做其他操作 Trim标签的属性
prefix
要增加什么前缀prefixOverrides
要去除什么前缀suffix
要增加什么后缀suffixOverrides
要去除什么后缀
mapper 接口
xml 映射文件
测试代码
测试结果
- 结果显示修改正确
7.6 Bind标签
一般用于处理
模糊查询
mapper 接口
xml 映射文件
测试代码
测试结果
- 根据运行结果可知,对
M
进行模糊查询的结果与期望结果一致
7.7 Sql标签
- 在我个人看来,sql标签 与 方法/函数 的作用类似,都是将重复的代码进行封装,减少代码的冗余
- 比如:
- 对某个表进行多次查询
- 多次查询,返回的列都相同的情况
例子
:- 将
7.2 where标签
的例子复用 - 接口与测试代码不变,仅将
where标签
之前的语句修改为 <include refid=”baseSelect”/>
- 将
7.8 Foreach 标签
- 对传入数组与集合进行操作
foreach 标签的属性
collection=""
:遍历的集合或者是数组- 参数是数组,collection中名字指定为array
- 参数是List集合,collection中名字指定为list
separator=""
:多个元素取出的时候 用什么文字分隔open=""
:以什么开头close=""
:以什么结尾item=""
:中间变量名
mapper 接口
xml 映射文件
测试代码
测试结果
8 实现多表查询
- 前面已经使用 MyBatis 完成了对Emp表的CRUD操作,不管是使用 SqlSession 直接操作,还是使用 Mapper 代理方式,都只是完成了对
单个数据库表
的操作。这肯定是远远不够的。 - 在实际开发中,经常会将来自
多张表
的数据在一个位置显示。比如查询并显示的员工信息中会有来自部门表、岗位表的数据,而后台一般是定义一个方法
8.1 关联查询
8.1.1 手动处理映射关系 resultMap
手动处理
数据库查询字段和封装实体类属性
之间的映射关系主键一般使用id属性
当属性名和查询出的数据表字段名相同
可以不写映射关系
mapper 接口
xml 映射文件
测试代码
8.1.2 各表关系
8.1.3 一对一关联查询 association
association
封装一对一信息关系的标签property
类的属性名javaType
用哪个类的对象给属性赋值
需求:
根据编号查询员工信息及所在的部门信息mapper 接口
xml 映射文件
测试代码
测试结果
- 由结果可知,查询到的
dept
数据存入到了emp
对象中
8.1.4 一对多关联查询 collection
collection
封装一对多信息关系的标签ofType
类路径
,用于接收连接
后的数据
需求
:根据部门编号查询部门信息及该部分的所有员工信息mapper 接口
xml 映射文件
测试代码
测试结果
8.1.5 多对多关联查询
- 运用前面的一对多与一对一的方法实现多对多
需求
:根据项目编号查询一个项目信息及参与该项目的所有员工信息- 项目的具体信息为一个表,项目与参与的员工信息为一个中间表,员工具体信息为一个表
- 利用项目号根据中间表找到对应的员工编号(一对多),然后再根据员工编号找到具体员工信息(一对一)
mapper 接口
xml 映射文件
测试代码
测试结果
8.2 级联查询
- 级联查询,顾名思义,就是利于数据库表间的外键关联关系进行自动的级联查询操作。使用MyBatis实现级联查询,除了
实体类
增加关联属性外,还需要在映射文件
中进行配置 个人理解
:将一个xml比喻为一个类,类中的 select 等标签为类的属性,所以级联查询更像是一个类去调用其他类中的方法,只不过多了映射等操作(映射这些操作也可以理解为类中添加属性)
8.2.1 立即加载
需求
:根据部门编号查询员工信息emp mapper 接口
dept mapper 接口
emp xml 映射代码
dept xml 映射代码
collection 属性
:select=""
调用的另一个SQL语句javaType=""
实体类的属性数据类型column=""
给另一个SQL语句传入的参数列jdbcType=""
参数对应JDBC的数据类型fetchType=""
加载方式 eager 积极加载 lazy延迟加载
测试代码
测试结果
8.2.2 延迟加载
- 延迟加载,又称按需加载: 延迟加载的内容等到真正使用时才去进行加载(查询)。多用在关联对象或集合中
优点
:- 先单表查询、需要时再从关联表去关联查询,大大降低数据库在单位时间内的查询工作量,将工作在
时间上的分配更加均匀
- 单表要比关联查询多张表
速度要快
- 先单表查询、需要时再从关联表去关联查询,大大降低数据库在单位时间内的查询工作量,将工作在
设置延迟加载
第一步
:打开
全局开关
,在sqlMapConfig.xml
中打开延迟加载的开关。配置完成后所有的association
和collection
元素都生效- ```xml
- ```xml
eager:表示立刻加载;lazy:表示延迟加载。
将覆盖全局延迟设置
8.3 多表查询总结与拓展
resultMap中的常见属性
属性 | 描述 |
---|---|
property | 需要映射到JavaBean 的属性名称。 |
javaType | property的类型,一个完整的类名,或者是一个类型别名。如果你匹配的是一个JavaBean,那MyBatis 通常会自行检测到。 |
column | 数据表的列名或者列别名。 |
jdbcType | column在数据库表中的类型。这个属性只在insert,update 或delete 的时候针对允许空的列有用。JDBC 需要这项,但MyBatis 不需要。 |
typeHandler | 使用这个属性可以覆写类型处理器,实现javaType、jdbcType之间的相互转换。一般可以省略,会探测到使用的什么类型的typeHandler进行处理 |
fetchType | 自动延迟加载 |
select | association、collection的属性,使用哪个查询查询属性的值,要求指定namespace+id的全名称 |
ofType | collection的属性,指明集合中元素的类型(即泛型类型) |
级联查询和多表查询的比较及其选择
级联查询 | 多表查询 | |
---|---|---|
SQL语句数量 | 多条 | 一条 |
性能 | 性能低 | 性能高 |
延迟加载 | 立即加载、延迟加载 | 只有立即加载 |
灵活性 | 更灵活 | 不灵活 |
SQL****难易度 | 简单 | 复杂 |
选择依据 | 简单、灵活 | 高性能 |
ResultType和ResultMap使用场景
- 如果你做的是单表的查询并且封装的实体和数据库的字段一一对应 resultType
- 如果实体封装的属性和数据库的字段不一致 resultMap
- 使用N+1级联查询的时候 resultMap
- 使用的是多表的连接查询 resultMap
一对一关联映射的实现
- 实例:学生和学生证、雇员和工牌
- 数据库层次:主键关联或者外键关联(参看之前内容)
- MyBatis层次:在映射文件的设置双方均使用association即可,用法相同
多对多映射的实现
- 实例:学生和课程、用户和角色
- 数据库层次:引入一个中间表将一个多对多转为两个一对多
- MyBatis层次
- 方法1:在映射文件的设置双方均使用collection即可,不用引入中间类
- 方法2:引入中间类和中间类的映射文件,按照两个一对多处理
自关联映射
- 实例:Emp表中的员工和上级。一般是一对多关联
- 数据库层次:外键参考当前表的主键(比如mgr参考empno)
- MyBatis层次:按照一对多处理,但是增加的属性都写到一个实体类中,增加的映射也都写到一个映射文件中
9 MyBatis 注解开发
使用场景
- 使用Auto Mapping时使用注解非常简单,不需要频繁的在接口和mapper.xml两个文件之间进行切换
- 但是必须配置resultMap时使用注解将会变得很麻烦,这种情况下推荐使用mapper.xml进行配置
- MyBatis支持纯注解方式,支持纯mapper.xml方式,也支持注解和mapper.xml混合形式。当只有接口没有mapper.xml时在mybatis.cfg.xml中可以通过<mapper class=””></mapper>加载接口类。如果是混合使用时,使用<package name=””/>。此方式一直是官方推荐方式。
- 如果某个功能同时使用两种方式进行配置,XML方式将覆盖注解方式
9.1 使用注解完成 CRUD
mapper 接口
测试代码
9.2 注解和XML的优缺点
XML | 注解 | |
---|---|---|
优点 | 1.类和类之间的解耦 2.利于修改。直接修改XML文件,无需到源代码中修改。 3.配置集中在XML中,对象间关系一目了然,利于快速了解项目和维护 4.容易和其他系统进行数据交交换 |
1.简化配置 2.使用起来直观且容易,提升开发效率 3.类型安全,编译器进行校验,不用等到运行期才会发现错误。 4.注解的解析可以不依赖于第三方库,可以直接使用Java自带的反射 |
10 缓存
概念
:缓存是一种临时存储少量数据至内存或者是磁盘的一种技术.,它可以减少数据的加载次数,降低工作量,提高程序响应速度- MyBatis允许使用缓存,缓存一般放置在高速读/写的存储器上,比如服务器的内存,能够有效的提供系统性能
- MyBatis分为
一级缓存和二级缓存
,同时也可配置关于缓存设置
一级缓存与二级缓存
:- 一级存储是SqlSession上的缓存
- 二级缓存是在SqlSessionFactory(namespace)上的缓存
- 默认情况下,MyBatis开启一级缓存,没有开启二级缓存。当数据量大的时候可以借助一些第三方缓存框架或Redis缓存来协助保存Mybatis的二级缓存数据
10.1 一级缓存
- 一级存储是SqlSession上的缓存,默认开启,是一种内存型缓存,不要求实体类对象实现Serializable接口
缓存中的数据使用键值对形式存储数据namespace+sqlid+args+offset>>> hash值作为键, 查询出的结果作为值
注意
:当中间发生了增删改
或者SqlSession
调用了commit
,会自动清空缓存
10.1.1 证明一级缓存是SqlSession上的缓存
- 首先,设置一个简单的查询:根据员工编号查询员工信息
- 通过
mapper
执行两次查询操作,并判断两次查询得到的对象是否为同一对象
- 输出结果为:
true
,说明两次查询到的对象为同一对象,但两次查询的操作都是同一个 mapper
调用,所以还需要证明不同 mapper 查询到的对象仍为同一对象
- 输出结果为:
false true
,证明了不同 mapper 调用同一查询方法,获取到的对象是同一对象
- 现在只是证明了
mapper
与查询到的结果无关,不能说明一级缓存是SqlSession上的缓存,所以对SqlSession
进行证明
- 输出结果为:
false false
,说明分别获取了SqlSession
后再进行同样的操作后,获取到的emp对象是不同的
,这证明了一级缓存是SqlSession上的缓存
10.2 二级缓存
二级缓存是以
namespace
为标记的缓存,可以是由一个SqlSessionFactory
创建的SqlSession
之间共享缓存数据默认不开启
- 要求实体类必须实现
序列化接口
开启二级缓存
全局开关
:在sqlMapConfig.xml
文件中的<settings>标签配置开启二级缓存
分开关
:在要开启二级缓存的mapper文件中开启缓存
序列化对象
:二级缓存未必完全使用内存,有可能占用硬盘存储,缓存中存储的JavaBean对象必须实现序列化接口
注意:在
commit()或者close()
的时候数据才会被放入到二级缓存
示例
:使用二级缓存实现通过员工编号查询员工信息的功能mapper 接口
xml 映射文件
测试代码
测试结果
- 可以发现,只查询了一次数据库,说明实现了二级缓存
注意
:MyBatis的二级缓存的缓存介质有多种多样,而并不一定是在内存中,所以需要对
JavaBean对象实现序列化接口
二级缓存是以
namespace 为单位
的,不同 namespace 下的操作互不影响加入Cache元素后,会对相应命名空间所有的
select
元素查询结果进行缓存,而其中的insert、update、delete 在操作是会清空整个namespace的缓存
cache 有一些可选的属性 type, eviction, flushInterval, size, readOnly, blocking
属性 含义 默认值 type
自定义缓存类,要求实现org.apache.ibatis.cache.Cache接口 null readOnly
是否只读
true:给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
false:会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全false eviction
缓存策略 LRU(默认)
– 最近最少使用:移除最长时间不被使用的对象FIFO
– 先进先出:按对象进入缓存的顺序来移除它们SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象LRU flushInterval
刷新间隔,毫秒为单位
默认为null,也就是没有刷新间隔,只有执行update、insert、delete语句才会刷新null size
缓存对象个数 1024 blocking
是否使用 阻塞性缓存BlockingCache
true:在查询缓存时锁住对应的Key,如果缓存命中了则会释放对应的锁,否则会在查询数据库以后再释放锁,保证只有一个线程到数据库中查找指定key对应的数据
false:不使用阻塞性缓存,性能更好false
如果在加入Cache元素的前提下让个别select 元素不使用缓存,可以使用useCache属性,设置为false
useCache
控制当前sql语句是否启用缓存flushCache
控制当前sql执行一次后是否刷新缓存
10.3 三方缓存
分布式缓存框架
:我们系统为了提高系统并发 和性能,一般对系统进行分布式部署(集群部署方式)不适用分布缓存, 缓存的数据在各个服务单独存储,不方便系统开发。所以要使用分布式缓存对缓存数据进行集中管理.ehcache,redis ,memcache缓存框架Ehcache
:是一种广泛使用的开源java分布式缓存。主要面向通用缓存,javaEE 和 轻量级容器。它具有内存和磁盘存储功能。被用于大型复杂分布式web application的- 这里的三方缓存是作为二级缓存使用的
配置步骤
- 导入依赖
- 在sql映射文件里,开启二级缓存,并把缓存类型指定为EhcacheCache
- 在资源目录下放置一个缓存配置文件,文件名为: ehcache.xml 内容如下
Cache配置
name
:Cache的唯一标识maxElementsInMemory
:内存中最大缓存对象数。maxElementsOnDisk
:磁盘中最大缓存对象数,若是0表示无穷大。eternal
:Element是否永久有效,一但设置了,timeout将不起作用。overflowToDisk
:配置此属性,当内存中Element数量达到maxElementsInMemory时,Ehcache将会Element写到磁盘中。timeToIdleSeconds
:设置Element在失效前的允许闲置时间。仅当element不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。timeToLiveSeconds
:设置Element在失效前允许存活时间。最大时间介于创建时间和失效时间之间。仅当element不是永久有效时使用,默认是0.,也就是element存活时间无穷大。diskExpiryThreadIntervalSeconds
:磁盘失效线程运行时间间隔,默认是120秒。diskSpoolBufferSizeMB
:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。memoryStoreEvictionPolicy
:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)
11 逆向工程
逆向工程
:可以针对单表自动生成
MyBatis执行所需要的代码(包括mapper.xml,mapper.java,pojo)- 一般在开发中,常用的逆向工程方式是通过
数据库的表生成代码
- 一般在开发中,常用的逆向工程方式是通过
开发步骤
- 创建maven项目
导入逆向工程依赖
配置逆向工程配置文件
:在resources目录下放置一个名为generatorConfig.xml的配置文件,文件内容如下
- 配置log4j(检查是否运行成功)
- 运行逆向工程代码
- 生成文件后,根据需要使用
12 main目录下XML文件编译问题
- 有时候,可能会选择将
配置文件或其他文件
与java代码
放置到同一个目录
下,这时候maven不会编译src目录
下非java的文件,需要配置相关属性才可以编译相应后缀的文件 - 在
pom.xml
中配置以下信息
- 这样maven就可以编译
src目录
下的指定文件了