【Mybatis】一级缓存和二级缓存

(一)为什么需要缓存

缓存一般用在可高速读写的存储器上,为提高速度,可以暂时把常用的数据缓存在内存中,这样读取速度就会远快于读取磁盘的速度

(二)一级缓存(在SqlSession上缓存)

【1】示例代码

public class TestMybatis {

    private InputStream inputStream;
    private SqlSessionFactory sqlSessionFactory;
    private SqlSession session;
    private ICategoryDao categoryDao;

    @Before
    public void init() throws Exception{
//        1-读取配置文件(拿到资源)
        inputStream= Resources.getResourceAsStream("mybatis_Config.xml");
//        2-创建工厂(把资源给构建者)
        sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
//        3-使用工厂生产SQLSession对象
        session=sqlSessionFactory.openSession(true);//true以后就不需要自己提交了
//        4-使用SQLSession创建Dao接口的代理对象(使用反射获取字节码class)
        categoryDao=session.getMapper(ICategoryDao.class);
//        5-使用代理对象执行方法,结果放在List中,并且循环遍历
    }

    @After
    public void destory() throws Exception{
//        6-释放资源
        session.close();
        inputStream.close();
    }

    @Test
    public void testFirstLevelCache(){
//        下面这个语句可以清空缓存,然后重新开启
//        session.clearCache();
        categoryDao=session.getMapper(ICategoryDao.class);
        Category c1=categoryDao.selectOne(1);
        System.out.println(c1);
        Category c2=categoryDao.selectOne(1);
        System.out.println(c2);

//      只发起了一次查询,结果表明两次取出的是同一个值
        System.out.println(c1==c2);
    }
}

在这里插入图片描述

可以看到,当使用categoryDao=session.getMapper(ICategoryDao.class);后,数据就缓存放在SQLSession中了,后面进行了两个同条件的检索c1和c2,但是它俩都是从SQLSession里取的数据,sql语句只在第一次检索的时候执行了,第二次检索的时候没有执行,而是直接到缓存里取数据,最后的输出值为true。

但是SQLSession层面的缓存要求必须是同一个SQLSession对象,如果是不同的对象,就无法获取到缓存数据。要想实现不同的SQLSession对象也能获取同样的缓存数据,就要继续往上层去缓存,也就是把数据缓存在SqlSessionFactory上。

【2】增删改操作会刷新一级缓存

而每次增删改操作都有可能会改变原来的数据,所以必须刷新清空一级缓存。
在这里插入图片描述

【3】一级缓存流程总结

(1)一级缓存简介

一级缓存作用域是sqlsession级别的,同一个sqlsession中执行相同的sql查询(相同的sql和参数),第一次会去查询数据库并写到缓存中,第二次从一级缓存中取。

一级缓存是基于 PerpetualCache 的 HashMap 本地缓存,默认打开一级缓存。

(2)何时清空一级缓存

如果中间sqlSession去执行commit操作(执行插入、更新、删除),则会清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

一级缓存时执行commit,close,增删改等操作,就会清空当前的一级缓存;当对SqlSession执行更新操作(update、delete、insert)后并执行commit时,不仅清空其自身的一级缓存(执行更新操作的效果),也清空二级缓存(执行commit()的效果)。

(3)一级缓存无过期时间,只有生命周期

MyBatis在开启一个数据库会话时,会创建一个新的SqlSession对象,SqlSession对象中会有一个Executor对象,Executor对象中持有一个PerpetualCache对象,见下面代码。当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。

(三)一级缓存底层原理

在这里插入图片描述

(四)二级缓存(在SqlSessionFactory上缓存)

【1】二级缓存需要手动配置开启

二级缓存是全局的,也就是说;多个请求可以共用一个缓存,二级缓存需要手动开启,有2种方式配置二级缓存:
(1)缓存会先放在一级缓存中,当sqlSession会话提交或者关闭时才会将一级缓存刷新到二级缓存中;
(2)开启二级缓存后,用户查询时,会先去二级缓存中找,找不到在去一级缓存中找;

(1)第一种配置方式
单个mapper配置,主需要在需要开启二级缓存的mapper.xml文件中加入以下配置即可开启

 <!-- 开启单个mapper的二级缓存,也叫全局缓存-->
  <cache />

注意一定要加到xxMapper.xml的文件内,千万不要加到mybatis 的主配置文件里面了,会报错的

(2)第二种配置方式
所有的mapper都开启二级缓存,在mybatis.xml中加入以下配置即可,只需要在配置文件中加上如下配置:

<!--打开延迟加载-->
    <settings>
        <!-- 打开延迟加载的开关 -->
        <setting name="lazyLoadingEnabled" value="true" />
        <!-- 将积极加载改为消息加载即按需加载 -->
        <setting name="aggressiveLazyLoading" value="false"/>
        <!-- 打开二级缓存 -->
        <setting name="cacheEnabled" value="true"/>
    </settings>

【2】示例代码

不同的SQLSession对象在获取同一条记录的时候,都是只在第一次检索的时候才会执行sql语句,后面的检索都不再执行sql,而是直接到缓存里找。正是因为SQLSession是由SqlSessionFactory生成的,而数据也缓存在SqlSessionFactory中,所以不同的SQLSession对象能够获取到相同的缓存数据。

实例代码

public static void main(String[] args) throws IOException {

    // 加载mybatis配置文件
    Reader reader = Resources.getResourceAsReader("config/configuration.xml");
    //创建数据工厂
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

    SqlSessionFactory sqlSessionFactory = builder.build(reader);

    // 第一个会话
    SqlSession sqlSession = sqlSessionFactory.openSession(true);


    // 获取会话一的mapper接口对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    // 第一次查询
    User user = mapper.selectByPrimaryKey("3rfrf34r34");
    

    //释放第一个会话
    sqlSession.clearCache();
    sqlSession.close();

    // 第二个会话
    SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
    // 获取会话二的mapper接口对象
    UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);

    // 第二次查询
    User user1 = mapper2.selectByPrimaryKey("3rfrf34r34");
    // 释放第二个会话
    sqlSession2.clearCache();
    sqlSession2.close();
}

打印结果
在这里插入图片描述

【3】二级缓存流程总结

(1)二级缓存简介

它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。

二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。

(2)二级缓存何时存入

在关闭sqlsession后(close),才会把该sqlsession一级缓存中的数据添加到namespace的二级缓存中。

开启了二级缓存后,还需要将要缓存的pojo实现Serializable接口,为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不一定只存在内存中,有可能存在硬盘中。

(3)二级缓存有过期时间,但没有后台线程进行检测

需要注意的是,并不是key-value的过期时间,而是这个cache的过期时间,是flushInterval,意味着整个清空缓存cache,所以不需要后台线程去定时检测。

每当存取数据的时候,都有检测一下cache的生命时间,默认是1小时,如果这个cache存活了一个小时,那么将整个清空一下。

(4)一级缓存和二级缓存的执行顺序

当 Mybatis 调用 Dao 层查询数据库时,先查询二级缓存,二级缓存中无对应数据,再去查询一级缓存,一级缓存中也没有,最后去数据库查找。

(五)二级缓存底层原理

在这里插入图片描述

(六)注意事项

另外,缓存还有以下几种情况需要注意
(1)映射语句文件中的所有 select 语句的结果将会被缓存。
(2)映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
(3)缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
(4)缓存不会定时进行刷新(也就是说,没有刷新间隔)。
(5)缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
(6)缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值