精品专题:
01.《C语言从不挂科到高绩点》课程详细笔记
https://blog.csdn.net/yueyehuguang/category_12753294.html?spm=1001.2014.3001.5482
02. 《SpringBoot详细教程》课程详细笔记
https://blog.csdn.net/yueyehuguang/category_12789841.html?spm=1001.2014.3001.5482
03.《SpringBoot电脑商城项目》课程详细笔记
https://blog.csdn.net/yueyehuguang/category_12752883.html?spm=1001.2014.3001.5482
04.《VUE3.0 核心教程》课程详细笔记
https://blog.csdn.net/yueyehuguang/category_12769996.html?spm=1001.2014.3001.5482
================================
|| 持续分享系列教程,关注一下不迷路 ||
|| 视频教程:小破站:墨轩大楼 ||
================================
一、缓存描述
对于一些我们需要经常查询的并且不经常改变的数据,如果每次查询都要与数据库进行交互,那么大大降低了效率。此时我们可以使用缓存,将一些对结果影响不大且经常查询的数据存放在内存中,从而减少与数据库的交互来提高效率。
Mybatis中也提供了对缓存的支持,分为一级缓存和二级缓存,一级缓存是sqlSession级别的,二级缓存是mapper级别的。在默认情况下,MyBatis只开启一级缓存,而当使用同一个mapper时,查询到的数据可能是二级缓存。
二、一级缓存
MyBatis 的一级缓存的作用域是session,当openSession()后,如果执行相同的SQL(相同语句和参数),MyBatis执行查询时首先取缓存区查找,如果能找到则直接返回,如果没有则执行sql语句从数据库中查询。在MyBatis张,一级缓存默认时开启的,并且无法关闭。
2.1 同一个SqlSession对象
同一个SqlSession对象,在参数和SQL语句户安全一样的情况下,只执行一次SQL语句(如果缓存没有过期),当我们在执行查询操作时,查询的结果会同时存入SqlSession提供的一块区域。该区域时一个Map结构,当我们再次查询同样的数据时,Mybatis会先去SqlSession中找,有的话直接拿来用。SqlSession消失后,一级缓存也就消失了。
@Test
public void testFindHeroById(){
SqlSession session = MyBatisUtil.getSession();
HeroDao dao = session.getMapper(HeroDao.class);
// 第一次查询
Hero hero = dao.findHeroById(1);
List<Skill> skills = hero.getSkills();
// 第二次查询
Hero hero1 = dao.findHeroById(1);
System.out.println(hero==hero1); //true
}
从输出的日志中可以看出:
第一次查询时发送了sql语句后返回了结果;
第二次查询没有发送SQL语句,直接从内存中获取了结果。
而且两次获得到的结果一致,并且通过判断得出两个对象是同一个对象。
2.2 不同的SqlSession对象
在代码中,重新构建一个新的SqlSession对象进行相同的查询,可以看到两次查询都分别从数据库中取出了数据,虽然结果相同,但是是两个不同的对象。
@Test
public void testFindHeroById() throws IOException {
SqlSession session = MyBatisUtil.getSession();
// 第一次查询
HeroDao dao = session.getMapper(HeroDao.class);
Hero hero = dao.findHeroById(1);
List<Skill> skills = hero.getSkills();
// 第二次查询,重新构造一个session对象
// 加载核心配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
// 构建一个SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 创建Session 对象,操作数据库
SqlSession session1 = factory.openSession();
HeroDao dao1 = session1.getMapper(HeroDao.class);
Hero hero1 = dao1.findHeroById(1);
System.out.println(hero==hero1); //true
}
从结果可以看出,两次查询,发送了两次SQL语句。得到的结果也是不同的。
2.3 清空缓存
如果不想使用一级缓存,可以在每次查询之后,清空缓存,具体做法如下:
》》 在Mapper映射文件中的查询语句中添加flushCache="true",代码如下:
<select id="findHeroById" resultMap="heroInfo" flushCache="true">
select h.*,s.* from hero h join skill s on h.id=s.hero_id
</select>
》》 测试代码:
public void testFindHeroById() throws IOException {
SqlSession session = MyBatisUtil.getSession();
// 第一次查询
HeroDao dao = session.getMapper(HeroDao.class);
Hero hero = dao.findHeroById(1);
List<Skill> skills = hero.getSkills();
HeroDao dao1 = session.getMapper(HeroDao.class);
Hero hero1 = dao1.findHeroById(1);
System.out.println(hero==hero1); //true
}
》》运行效果

从运行结果来看,发送了两次sql语句。除了上述添加flushCache="true"以外,也可以手动调用sqlsession对象的clearCache()方法来清空缓存。

注意:
看到这可能会有个疑问,如何两次查询一个数据时,中间对这个数据进行了修改,那么第二次查询的 结果会不会修改呢?答案是会的。因为在mybatis中,除了使用clearCache()方法时会清空缓存,对数 据进行 增,删,改,commit() , close()时也会对一级缓存进行清空。
总结 :
- 在同一个SqlSession中,Mybatis会把执行的方法和参数通过算法生成缓存的键值,将键值和结果存 放在一个Map中,如果后续的键值一样,则直接从Map中获取数据
- 不同的SqlSession之间的缓存是相互隔离的;
- 用一个SqlSession,可以通过配置使得在查询前清空缓存
- 任何的update, insert, delete语句都会清空缓存
三、二级缓存
二级缓存的原理和一级缓存原理一样,第一次查询,会将数据放入缓存中,然后第二次查询则会直接去缓存中取。但是一级缓存是基于sqlSession的,而二级缓存是基于mapper文件的namespace的,也就是说多个sqlSession可以共享一个mapper中的二级缓存区域,并且如果两个mapper的namespace 相同,即使是两个mapper,那么这两个mapper中执行sql查询到的数据也将存在相同的二级缓存区域中。

和一级缓存默认开启不一样,二级缓存需要我们手动开启
》》首先在全局配置文件mybatis-config.xml中的settings标签中加入如下代码:
<settings>
<!--全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。 -->
<setting name="cacheEnabled" value="true"/>
</settings>
此处需要注意的问题:需要被二级缓存的数据,其对应的实体类要实现java.io.serializable接口。
》》其次在映射文件中开启缓存
<!--配置二级缓存 -->
<cache></cache>
》》 要进行二级缓存的Pojo类必须实现java.io.serializable接口。
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Hero implements Serializable {
private Integer id;
private String hname;
private String job;
private Integer level;
private String sex;
// 一对多,添加集合属性,用来存储关联查询出来的多个技能数据
List<Skill> skills;
}
》》 测试代码:
@Test
public void test2cache() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
HeroDao dao = session.getMapper(HeroDao.class);
Hero hero = dao.findHeroById(1);
System.out.println(hero);
// 清除一级缓存
session.close();
System.out.println("-------------------------");
SqlSession session1 = factory.openSession();
HeroDao dao1 = session1.getMapper(HeroDao.class);
Hero hero1 = dao1.findHeroById(1);
session1.close();
System.out.println(hero1);
}
运行效果:

经过上面的测试,我们发现执行了两次查询,并且在执行第一次查询后,我们关闭了一级缓存,再去执行第二次查询时,我们发现并没有对数据库发出 sql 语句,所以此时的数据就只能是来自于我们所说的二级缓存。
587

被折叠的 条评论
为什么被折叠?



