这篇文章我们将主要讲一下Mybatis的缓存。
目录
1.为什么要有Mybatis缓存
2,什么是缓存
2.1 什么是缓存
2.2 为什么使用缓存
2.3 什么样的数据能使用缓存
3.Mybatis缓存
3.1 概述
3.2 SQLSession工厂的说明
3.3 一级缓存
3.4 测试一级缓存
3.5 一级缓存失效的情况
3.5.1 SqlSession不同
3.5.2 sqlSession相同,查询条件不同
3.5.3 sqlSession相同,两次查询之间执行了增删改操作!
3.5.4 sqlSession相同,手动清除一级缓存
3.6 二级缓存
3.7 测试二级缓存
3.7.1 在核心配置文件中,设置全局属性caheEnable="true"。
3.7.2 在UserDao.xml配置文件声明使用二级缓存
3.7.3 查询数据所转换的实体类类型必须实现序列化接口
3.7.4 二级缓存必须在SqlSession关闭或提交之后有效
3.7.5 测试结果
3.8 Catch参数的具体细节
4. Mybatis缓存查询的顺序
在前面的文章中说过,Mybatis是基于jdbc而来的,是对jdbc的一种优化。因为jdbc存在一些问题,所以我们需要使用Mybatis。那么jdbc存在哪些问题?
jdbc存在的问题:
对于第二个问题,Mybatis是通过xml文件也就是配置文件的方式来解决的。之前,我们的sql是写在servlet类中的,是一个.java文件,每次修改后需要重新编译、解析,而Mybatis的sql语句是写在xml文件也就是配置文件中的,修改后不需要重新编译
对于第三个问题,Mybatis是通过Javabean对象来解决的。Mybatis是通过Javabean对象来完成对结果集的解析的,是Javabean对象的一种交互,就不是硬解析了。
对于第一个问题,Mybatis是通过缓存来解决的,这就是这篇文章主要要讲的内容。
我们可以看一下下面这张图片:
为了减少和数据库的交互次数,减少系统开销,提高系统效率
经常查询并且不经常改变的数据
多说一句题外话:数据库的数据是存储在硬盘中的
mybatis包含了一个非常强大的查询缓存特性,他可以非常方便的定制和配置缓存。缓存可以极大的提高查询的效率
mybatis系统当中默认定义了两级缓存:一级缓存和二级缓存
默认情况之下,只有一级缓存开启(sqlSession级别的缓存),二级缓存需要手动开启配置,需要局域namespace级别的缓存。
现在,对上述图片做一个解析。
第25行,@Before是一个注解,是前置通知,表示下面内容是在Test之前运行的,@After和这个一个道理。
第28行,加载主配置文件,即sqlMapConfig.xml文件,这个文件里有driver,有url,有用户名,有密码,以及我们所关联的xml配置文件。加载成什么?加载成字节流文件,看第21行的定义。
第30行,将第28行所加载的字节流文件交给SqlSessionFactory工厂进行解析,仔细观察这个类的创建。SqlSessionFactory是怎么解析的?是通过硬匹配进行解析的,这个与SqlSessionFactory源码有关,这里不过多赘述,有兴趣的可以点进去看源码。
第32行,我们将最终解析出来的内容生成一个session对象
第34行,我们通过session对象来调用相关的接口来生成代理对象mapper。
其中,和数据库进行交互的还是我们的SqlSession对象,SqlSession中是有许多方法的。
题外话:这一块特别有意思,我前面也写过对这部分的一些理解,现在理解加深了,就又写了点,这块要多想,同时还要结合前面的那张图。
一级缓存也叫本地缓存。
我们书写以下代码(当然,相关的内容你得有):
@Test
public void find() throws IOException {
//加载主配置文件,目的是为了构建SqlSessionFactory对象
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//通过SqlSessionFactory工厂对象创建SqlSesssion对象
session = factory.openSession();
//通过Session创建UserDao接口代理对象
mapper = session.getMapper(UserDao.class);
User user1 = mapper.findById(1);
System.out.println(user1.toString());
System.out.println("-----------------");
User user2 = mapper.findById(1);
System.out.println(user2.toString());
System.out.println(user1 == user2);
//释放资源
session.close();
in.close();
}
然后,截图如下:
重点关注第60行
然后,我们运行,看下结果:
当我们第一次执行sql语句时,SqlSession会从数据库中拿出数据,放入local catch中(就是本地缓存),然后当我们第二次执行此sql语句时,SqlSession直接从local catch中拿数据,所以,两次执行sql的地址是一样的,所以为true,这就是Mybatis的一级缓存。
以下四种情况,一级缓存会失效:
很好理解,SQLSession都不是同一个了嘛,最后的地址怎么可能是同一个
sqlSession相同,查询条件不同,会导致一级缓存失效,但是,多次查询不同的情况,不会导致缓存失效
很简单,因为你拿的数据都不一样,执行的sql都不一样,怎么可能相同嘛
因为你数据库改变了,缓存刷新了(我自己瞎猜的),所以就是false咯
废话,你清除了,它当然就没有了,很简单的啦。
Mybatis的二级缓存非常强大,它不同于一级缓存只存在于sqlSession的生命周期中,而是可以理解为存在于SQLSessionFactory的生命周期中。
二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactroy创建SqlSession查询结果会被缓存;此后若再次执行相同的查询语句,结果会从一个缓存中获取。
二级缓存的开启有以下四个条件:
①:在核心配置文件中,设置全局属性caheEnable="true"。
②:在映射件中置
③:查询数据所转换的实体类类型必须实现序列化接口
④:二级缓存必须在SqlSession关闭或提交之后有效
下面,我们依次执行这四个条件。
在SqlMapConfig.xml配置文件中开启二级缓存
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
// get set方法 .....
}
@Test
public void findById() throws IOException {
// 1.加载SqlMapConfig配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建sqlSessionFactory工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//3.sqlSessionFactory创建sqlSession
SqlSession sqlSession = factory.openSession();
SqlSession sqlSession2 = factory.openSession();
//4.通过Session创建UserDao接口代理对象
UserDao mapper = sqlSession.getMapper(UserDao.class);
User user1 = mapper.findById(1);
System.out.println(user1.toString());
// 将其一级缓存的数据放进二级缓存中,并清空一级缓存
sqlSession.close();
System.out.println("-----------------");
UserDao mapper2 = sqlSession2.getMapper(UserDao.class);
User user2 = mapper2.findById(1);
System.out.println(user2.toString());
// 将其一级缓存的数据放进二级缓存中,并清空一级缓存
sqlSession2.close();
System.out.println(user1 == user2);
resourceAsStream.close();
}
重要结论:打印发现2个对象的地址值不一样,但是确实只发送了一次SQL语句的查询,二级缓存中存储的是数据,不是对象。
eviction(收回策略)
(session1在catch中注入一条缓存,session2在catch中注入一条缓存,大家都注入,满了怎么办?所以要回收,从catch中回收缓存的策略就叫(缓存)收回策略)
flushinterval(刷新间隔) 可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。
(数据库中的数据发生变化后,缓存如果不发生变化,那会出问题的,所以缓存也需要发生变化,要保持和数据库中数据的一致性,怎么办?刷新扫描呗)
size(引用数目)
readOnly(只读) 属性可以被设置为 true / false。
配置结果: