SSM——8.Mybatis的缓存

这篇文章我们将主要讲一下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缓存查询的顺序


1.为什么要有Mybatis缓存

在前面的文章中说过,Mybatis是基于jdbc而来的,是对jdbc的一种优化。因为jdbc存在一些问题,所以我们需要使用Mybatis。那么jdbc存在哪些问题?

jdbc存在的问题:

  • 数据库连接的频繁创建、释放浪费资源进而影响系统性能。
  • sql代码写在 java文件当中,如果在开发过程中我们改动某个sql,就需要去修改java代码,改完之后还需要重新编译。
  • 对结果集的解析也是硬编码,sql变化会导致解析结果的代码也跟着变化,系统不易维护

对于第二个问题,Mybatis是通过xml文件也就是配置文件的方式来解决的。之前,我们的sql是写在servlet类中的,是一个.java文件,每次修改后需要重新编译、解析,而Mybatis的sql语句是写在xml文件也就是配置文件中的,修改后不需要重新编译

对于第三个问题,Mybatis是通过Javabean对象来解决的。Mybatis是通过Javabean对象来完成对结果集的解析的,是Javabean对象的一种交互,就不是硬解析了。

对于第一个问题,Mybatis是通过缓存来解决的,这就是这篇文章主要要讲的内容。

2,什么是缓存

2.1 什么是缓存

  • 缓存:就是存储在内存当中的数据
  • 将用户常使用的数据存放在缓存(内存)当中,用户查询数据就不用从磁盘(关系型数据库文件)当中查询,从缓存当中查询,从而提高查询效率,解决了高并发系统的性能问题

我们可以看一下下面这张图片:

 2.2 为什么使用缓存

为了减少和数据库的交互次数,减少系统开销,提高系统效率

2.3 什么样的数据能使用缓存

经常查询并且不经常改变的数据

多说一句题外话:数据库的数据是存储在硬盘中的

3.Mybatis缓存

3.1 概述

    mybatis包含了一个非常强大的查询缓存特性,他可以非常方便的定制和配置缓存。缓存可以极大的提高查询的效率

mybatis系统当中默认定义了两级缓存:一级缓存和二级缓存

默认情况之下,只有一级缓存开启(sqlSession级别的缓存),二级缓存需要手动开启配置,需要局域namespace级别的缓存。

3.2 SQLSession工厂的说明

 现在,对上述图片做一个解析。

第25行,@Before是一个注解,是前置通知,表示下面内容是在Test之前运行的,@After和这个一个道理。

第28行,加载主配置文件,即sqlMapConfig.xml文件,这个文件里有driver,有url,有用户名,有密码,以及我们所关联的xml配置文件。加载成什么?加载成字节流文件,看第21行的定义。

第30行,将第28行所加载的字节流文件交给SqlSessionFactory工厂进行解析,仔细观察这个类的创建。SqlSessionFactory是怎么解析的?是通过硬匹配进行解析的,这个与SqlSessionFactory源码有关,这里不过多赘述,有兴趣的可以点进去看源码。

第32行,我们将最终解析出来的内容生成一个session对象

第34行,我们通过session对象来调用相关的接口来生成代理对象mapper。

其中,和数据库进行交互的还是我们的SqlSession对象,SqlSession中是有许多方法的。

题外话:这一块特别有意思,我前面也写过对这部分的一些理解,现在理解加深了,就又写了点,这块要多想,同时还要结合前面的那张图。

3.3 一级缓存

一级缓存也叫本地缓存。

  • 与数据库同一次会话期间查询到的数据会放入的本地缓存当中。
  • 如果以后需要获取相同的数据直接去缓存当中拿,没必要再去查询数据库

 3.4 测试一级缓存

我们书写以下代码(当然,相关的内容你得有):

@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行

然后,我们运行,看下结果:

 解释true的原因:

当我们第一次执行sql语句时,SqlSession会从数据库中拿出数据,放入local catch中(就是本地缓存),然后当我们第二次执行此sql语句时,SqlSession直接从local catch中拿数据,所以,两次执行sql的地址是一样的,所以为true,这就是Mybatis的一级缓存。

3.5 一级缓存失效的情况

以下四种情况,一级缓存会失效:

  • sqlSession不同
  • sqlSession相同,查询条件不同
  • sqlSession相同,两次查询之间执行了增删改操作!
  • sqlSession相同,手动清除一级缓存

3.5.1 SqlSession不同

很好理解,SQLSession都不是同一个了嘛,最后的地址怎么可能是同一个

3.5.2 sqlSession相同,查询条件不同

sqlSession相同,查询条件不同,会导致一级缓存失效,但是,多次查询不同的情况,不会导致缓存失效

很简单,因为你拿的数据都不一样,执行的sql都不一样,怎么可能相同嘛 

3.5.3 sqlSession相同,两次查询之间执行了增删改操作!

 

 因为你数据库改变了,缓存刷新了(我自己瞎猜的),所以就是false咯

3.5.4 sqlSession相同,手动清除一级缓存

 废话,你清除了,它当然就没有了,很简单的啦。

3.6 二级缓存

Mybatis的二级缓存非常强大,它不同于一级缓存只存在于sqlSession的生命周期中,而是可以理解为存在于SQLSessionFactory的生命周期中。

二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactroy创建SqlSession查询结果会被缓存;此后若再次执行相同的查询语句,结果会从一个缓存中获取。

3.7 测试二级缓存

二级缓存的开启有以下四个条件:

①:在核心配置文件中,设置全局属性caheEnable="true"。

②:在映射件中置

③:查询数据所转换的实体类类型必须实现序列化接口

④:二级缓存必须在SqlSession关闭或提交之后有效

下面,我们依次执行这四个条件。

3.7.1 在核心配置文件中,设置全局属性caheEnable="true"。

在SqlMapConfig.xml配置文件中开启二级缓存

<!‐‐ 开启二级缓存 ‐‐>
<settings>
    <!--开启二级缓存-->
    <setting name="cacheEnabled" value="true"/>
</settings>

3.7.2 在UserDao.xml配置文件声明使用二级缓存

<!--使用二级缓存-->
<cache/>

3.7.3 查询数据所转换的实体类类型必须实现序列化接口

public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    // get set方法 .....
 }

3.7.4 二级缓存必须在SqlSession关闭或提交之后有效

@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();
}

3.7.5 测试结果

 重要结论:打印发现2个对象的地址值不一样,但是确实只发送了一次SQL语句的查询,二级缓存中存储的是数据,不是对象。

3.8 Catch参数的具体细节

eviction(收回策略)

(session1在catch中注入一条缓存,session2在catch中注入一条缓存,大家都注入,满了怎么办?所以要回收,从catch中回收缓存的策略就叫(缓存)收回策略)

  • LRU(最近最少使用的):移除最长时间不被使用的对象,这是默认值。
  • FIFO(先进先出):按对象进入缓存的顺序来移除它们。
  • SOFT(软引用):移除基于垃圾回收器状态和软引用规则的对象。
  • WEAK(弱引用):更积极地移除基于垃圾收集器状态和弱引用规则的对象。

flushinterval(刷新间隔) 可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。

(数据库中的数据发生变化后,缓存如果不发生变化,那会出问题的,所以缓存也需要发生变化,要保持和数据库中数据的一致性,怎么办?刷新扫描呗)

  • 默认情况不设置,即没有刷新间隔,缓存仅仅在调用语句时刷新。

size(引用数目)

  • 可以被设置为任意正整数,要记住缓存的对象数目和运行环境的可用内存资源数目。默认值是1024 。

readOnly(只读) 属性可以被设置为 true / false。

  • true:只读缓存:会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改, 这提供了很重要的性能优势。
  • false读写缓存: 通过序列化返回缓存对象的拷贝,这种方式会慢一些,但是安全,因此默认是 false。

配置结果:

<cache eviction="FIFO"
flushInterval="6000"
size="512"
readOnly="true"/>

4. Mybatis缓存查询的顺序

  • 先查询二级缓存,因为二级缓存中可能会有其他程序查询出来的数据,可以直接拿来使用
  • 如果二级缓存命中,再查询一级缓存
  • 如果一级缓存也没有命中,则查询数据库
  • SqlSession关闭之后,一级缓存的数据会写入二级缓存

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

L纸鸢

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

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

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

打赏作者

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

抵扣说明:

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

余额充值