【SSM详细教程】-10-Mybatis缓存机制

 精品专题:

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 语句,所以此时的数据就只能是来自于我们所说的二级缓存。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

听潮阁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>