Mybatis缓存
延迟(懒)加载 和 立即加载
-
延迟(懒)加载
在真正使用数据时才发起查询,不用的时候不查询,按需加载(也叫 懒加载)
比如:
在查询班级信息,每个班级都会有很多的学生(假如每个班有100个学生),如果我 们只是查看班级信息,但是学生对象也会加载到内存中,会造成浪费。
所以我门需要进行懒加载,当确实需要查看班级中的学生信息,我门在进行加载班级 中的学生信息
通常: 一对多,或者多对多的是需要使用延迟加载
-
立即加载
不管用不用信息,只要调用,马上发起查询并进行加载
当我们查询学生信息时,就需要知道学生在哪个班级中,所以就需要立马去查询班级 的信息
通常:当 一对一或者 多对一的时候需要立即加载
案例:
例如现在对学生表进行查询
mapper.java
@Select("select * from student")
public List<Student> findStudentAndClass();
实现类:
List<Student> slist = stuMapper.findStudentAndClass();
Student s = slist.get(0);
System.out.println(s);
实现类中只需要集合中的第一个数据,应该只需要去查询该学生对应的班级,但它会把全部班级查出来,这就是立即加载。立即加载就是不管是否需要数据,只要一进行查询,就会把相关联的数据一并查询出来
懒加载只会去查询第一个数据对应的班级,用的时候才查询,不用的时候不查询
设置懒加载
在主配置文件中添加<settings>
,该标签要在<properties>
之下,<typeAliases>
之上
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 开启懒加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 关闭层级加载 -->
</settings>
层级加载:层级加载是在开启懒加载之后才会有(3.4.1之前默认开启),它会在空闲时间把其他不用的加载到内存中,实现全部加载
开启懒加载后只会查询用的数据
动态sql中开启懒加载:fetchType属性
@Results({
@Result(column = "classid", property = "classid"),
@Result(property = "bj",column = "classid",
one = @One(select = "com.ape.mapper.BanjiMapper.findBanjiByClassid",fetchType = FetchType.LAZY) )
})
@Select("select * from student")
public List<Student> findStudentAndClass();
fetchType属性的值是三个枚举值,赋值eg:fetchType = FetchType.LAZY(FetchType.)
- DEFAULT:主配置文件是什么加载默认就是什么加载
- EAGER:立即加载
- LAZY:懒加载,延迟加载
xml中在<association>
中添加属性 fetchType=" "
<association property="" select="" fetchType="lazy" >
</association>
缓存
-
缓存
缓存(cache),数据交换的缓冲区,当应用程序需要读取数据时,先从数据库中将数据取出,放置在缓冲区中,应用程序从缓冲区读取数据
特点:数据库取出的数据保存在内存中,具备快速读取和使用
限制:读取时无需再从数据库获取,数据可能不是最新的
-
命中和未命中
-
命中:需要的数据在缓存中找到结果
-
未命中:需要的数据在缓存中未找到结果
-
-
mybatis缓存的意义
减少对数据库的访问,降低时间资源,提升效率
-
缓存的适用性
-
适合使用缓存
经常查询并且不经常改变的
数据的正确与否对最终结果影响不大的
比如:一个公司的介绍,新闻等
-
不适合适用缓存
经常改变的数据
数据的正确与否对最终结果影响很大
比如商品的库存,股市的牌价等
-
一级缓存
-
会话 session 级别的缓存,针对一次会话操作内
-
默认开启,一级缓存只是相对同一个 SqlSession 对象而言。
例如:同一个sqlsession对象中,执行了两条相同的sql,第二次不会再去查询数据库,直接调用第一次查询缓存的数据
Mapper.java
@Select("select * from student where sid = #{v}")
public Student findStudentBySid(int sid);
实现类
SqlSession sqlSession = DaoUtil.getSqlSession();
StudentMapper stuMapper = sqlSession.getMapper(StudentMapper.class);
// 查询5号学生
Student s = stuMapper.findStudentBySid(5);
System.out.println(s);
// 业务逻辑...
for(int i = 0; i< 100; i++) {
System.out.print(i);
}
// 再次查询5号学生
Student s2 = stuMapper.findStudentBySid(5);
第二次查询s2不会执行sql语句,直接返回缓存中的结果
输出System.out.print(s1 == s2); 为true,说明s2拿的是缓存中的数据
一级缓存查询时,先找缓存cache,没有去查数据库,查询到结果先缓存,再返回给用户
工作流程
注意事项:一级缓存失效的情况
-
使用不同的sqlsession
-
同一个sqlsession单查询条件不同
-
同一个sqlsession两次查询期间执行了任何一次增删改,commit(),close()操作
-
同一个sqlsession两次查询期间手动清空了缓存
sqlsession.clearCache(); // 清空缓存
二级加载(基本不用)
一级缓存最大的范围就是一个sqlsession,那么如果多个 SqlSession 需要共享缓存,此时就需要二级缓存
-
二级缓存:Mapper 级别的,只要是同一个 Mapper,无论使用多少个 SqlSession 来操作,数据都是共享的,多个不同的 SqlSession 可以共用二级缓存,MyBatis 二级缓存默认是关闭的,需要使用时可手动开启,二级缓存也可以使用第三方的缓存,比如,使用 Ehcache 作为二级缓存
-
二级缓存配置
-
主配置文件
-
注解
-
-
二级缓存总结
- Mybatis 的二级缓存相对于一级缓存来说,实现了缓存数据的共享,可控性也更强
- 极大可能会出现错误数据,有设计上的缺陷,安全使用的条件比较苛刻
- 分布式环境下,必然会出现读取到错误数据,所以不推荐使用
自定义缓存
-
自定义缓存分类
-
实现缓存接口
实现 org. apache. ibatis. cache. Cache 接口自定义缓存
-
引入第三方缓存
引入 Redis 等第三方内存库作为 MyBatis 缓存
-
一级缓存和二级缓存的区别
- 一级缓存是 SqlSession 级别的缓存,它的作用域是同一个 SqlSession,同一个SqlSession 中的多次查询会共享同一个缓存。二级缓存是 Mapper 级别的缓存,它的作用域是同一个 Mapper,同一个 Mapper 中的多次查询会共享同一个缓存
- 一级缓存是默认开启的,不需要手动配置。二级缓存需要手动配置,需要在 Mapper.xml 文件中添加 标签
- 一级缓存的生命周期是和 SqlSession 一样长的,当 SqlSession 关闭时,一级缓存也会被清空。二级缓存的生命周期是和 MapperFactory 一样长的,当应用程序关闭时,二级缓存也会被清空
- 一级缓存只能用于同一个 SqlSession 中的多次查询,不能用于跨 SqlSession 的查询。二级缓存可以用于跨 SqlSession 的查询,多个 SqlSession 可以共享同一个二级缓存
- 一级缓存是线程私有的,不同的 SqlSession 之间的缓存数据不会互相干扰。二级缓存是线程共享的,多个 SqlSession 可以共享同一个二级缓存,需要考虑线程安全问题