延迟加载
如果一个对象关联另一个对象,那么在查询 A 对象的时候,会去关联查询 B 对象。
何时查询(加载)B 对象分为三种时机:
- 立即加载
- 激进式延迟加载
- 延迟加载
#立即加载
MyBaits 默认是立即加载,即在查询 A 对象的时候,会立即查询其关联的 B 对象。如果,B 对象也有关联对象,例如 C 对象,那么还会立即查询 C 对象,... 因此类推,直到把所有有关联关系的数据全部查询出来。
#激进式延迟加载
通过设置,可以启用延迟加载:
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
启用延迟加载之后,Mybatis 又是默认的激进地延迟加载。
Mybatis 内部会进行某种规则判断,从而使得激进式的延迟加载,有时候等同于立即加载,有时候等同于普通的延迟加载。
#真-延迟加载
可以再通过配置关闭掉激进地延迟加载,从而进入普通的延迟加载:
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
普通的延迟加载只会在你真正用到 A 对象的 B 属性时,再去查询/加载 B 对象。
Mybatis 的内置缓存
#1. Mybatis 一级缓存
#概述
MyBatis 提供了查询缓存来缓存数据,从而达到提高查询新能的要求。MyBatis 的缓存分为一级缓存和二级缓存。
MyBatis 的一级缓存是 SqlSession 级别的缓存(在操作数据库时需要构造 SqlSession 对象),每个 SqlSession 对象中都有一个 HashMap 对象用于缓存数据,不同的 SqlSession 之间缓存的数据互不影响。
提前说明一下,当 mybatis 与 spring 整合后,如果没有事务,一级缓存是失效的!
原因就是两者结合后,sqlsession 如果发现当前没有事务,那么每执行一个 mapper 方法之后,sqlsession 就被关闭了(
session.close()
)。所以记得给 Service 的方法的脑袋上面加 @Transactional 。
在参数和 SQL 完全相同的情况下,使用同一个 SqlSession 对象调用同一个 Mapper 方法,往往只执行一次 SQL 语句。因为如果没有声明需要刷新缓存并且缓存没有超时,SqlSession 都只会取出当前缓存的数据,而不是执行 SQL 语句。
如果 SqlSession 执行了 DML 操作,并提交数据库,Mybatis 会清空 SqlSession 中的一级缓存,这样做的目的是保证缓存中存储的数据是最新的,避免出现脏读现象。
#刷新缓存的时机
-
如果 SqlSession 调用了 close 方法,则一级缓存不可用/销毁。
-
如果 SqlSession 调用了 clearCache 方法,则一级缓存中缓存的数据被清空。
-
如果 SqlSession 执行了一个 DML 操作,则一级缓存中缓存的数据被清空。
#2. Mybatis 二级缓存
二级缓存是 Mapper 级别(也叫 namespace 级别)的缓存,同样使用 HashMap 进行数据存储。
-
二级缓存是以 namespace 为单位的,不同的 namespace 下的操作相互隔离。
-
增删改操作会清空 namespace 下的全部缓存。
如果开启了二级缓存,那么在关闭 sqlsession 后,会把该 sqlsession 一级缓存中的数据添加到 namespace 的二级缓存中。
二级缓存比一级缓存作用域更大,多个 sqlsession 可以共用二级缓存,即,二级缓存是跨 sqlsession 的。
例如,关闭一个 sqlsession 之后,打开一个新的 sqlsession,执行同一条 sql 语句,会利用上一次的缓存数据。
mybatis 默认没有开启二级缓存 ,需要在配置中进行配置才能使用。打开二级缓存分为三步:
-
打开二级缓存总开关
-
打开需要使用二级缓存的 mapper 的开关。
-
POJO 序列化
#打开二级缓存总开关
打开总开关,只需要在 mybatis 总配置文件中加入一行设置
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
#打开需要使用二级缓存的 mapper 的开关。
在需要开启二级缓存的 mapper.xml 中加入 caceh 标签
<cache />
#POJO 序列化
让需要使用二级缓存的 POJO 类实现 Serializable 接口,如
public class Department implements Serializable {
...
}
通过之前三步操作就可以使用二级缓存了。