MyBatis缓存机制详解(三)

一、MyBatis缓存机制

  对于一个持久层框架,缓存是很重要的。缓存的作用就是提升数据的查询效率,与数据库的访问速度等问题,是一种快速响应的技术。
  缓存的原理就是将数据副本存入速度更快的存储设备,将数据放到与使用者更近的位置。
在这里插入图片描述

MyBatis系统中默认定义了两级缓存:
(1)一般情况下只有一级缓存(SqlSession级别的缓存,也称为本地缓存)是默认开启的。与数据库同一次会话期间查询到的数据会放在本地缓存中

(2)二级缓存需要手动开启和配置,他是基于namespace级别的缓存,也称为全局缓存。
(3)为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存。

1.1、一级缓存

知识:一级缓存(本地缓存)与数据库同一次会话期间查询到的数据会放在本地缓存中。以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库。

测试:当我们查询不同的数据的时候:
在这里插入图片描述
当我们查询相同数据的时候:
在这里插入图片描述
结果:
在这里插入图片描述

注意
(1)一级缓存是SqlSession级别的缓存,一直是开启的,不能被关闭。
(2)每个SqlSession对象都有一级缓存,且每个对象之间相互独立,互不干扰的。假如创建了两个SqlSession对象,且两个对象执行的sql和查询的数据是一样的,这种情况就不会调用一级缓存,还是与数据库发生了交互,因为不是同一个SqlSession对象。
(3)SqlSession相同情况下,先执行查询,再执行增加、删除、或修改操作,最后再执行同一个查询,该情况也不会调用一级缓存,因为数据中途发生了变化。
(4)SqlSession相同,如果我们调用sqlSession.clearCache();手动清除了缓存,那么此时互将没有缓存被调用。

如图:
在这里插入图片描述

1.2、二级缓存

  二级缓存也叫全局缓存,基于namespace级别的缓存,一个namespace对应一个二级缓存。

工作机制
  一个会话,查询一条数据,这个数据就会被放在当前会话一级缓存中;如果会话关闭,一级缓存中的数据会被保存到二级缓存中。此时,新的会话查询信息,就可以参照二级缓存中的内容。对于不同的namespacce查询的数据会放在自己对应的缓存中(map中),是相互独立的

使用:
  (1)开启全局二级缓存配置
  (2)去mapper.xml中配置使用二级缓存
  (3)POJO实现序列化接口

1、开启全局二级缓存配置:
在这里插入图片描述
2、去mapper.xml中配置使用二级缓存:

<cache>
-------eviction :缓存的回收策略,默认是LRU
------------1) LRU :最近最少使用的,移除最长时间不被使用对象
------------2) FIFO :先进先出,按照缓存进入的顺序来移除它们
------------3) SOFT :软引用,移动基于垃圾回收器状态和软引用规则对象
------------4) WEAK:弱引用,更积极的移除基于垃圾回收器状态和弱引用规则对象
-------flushInterval :缓存刷新间隔。默认是不清空。
------------比如flushInterval = “6000”
-------readOnly :是否只读
------------true :只读。直接让用户去读数据,特点不安全,速度快。
------------false :非只读。myBatis认为可能会修改数据,使用序列化与反序列化先克隆一份数据,然后给用户克隆的数据。特点,安全,速度慢。
-------size :缓存大小。比如:1024
-------type :指定自定义的全类名。myBatis提供cache接口可重写。

在这里插入图片描述
(3)POJO实现序列化接口:
在这里插入图片描述

测试:
在这里插入图片描述
结果:
在这里插入图片描述
问题:
如果第一次会话查询完成之后,不执行sqlSession.close()方法,或者放到代码最后再关闭第一次会话,请问会调用二级缓存么。
答案: 不会。

结论
1、查出的数据默认先放在一级缓存中,只有执行了会话提交(factory.openSession(true))或关闭(sqlSession.close())操作,在二级缓存开启的情况下,一级缓存中的数据才会转到二级缓存中。
2、二级缓存中的数据只与命名空间对象有关,与sqlSession对象无关。

1.3、缓存有关的设置和属性

1、二级缓存MyBatis全局配置文件设置

cacheEnabled默认时true
----------cacheEnabled = true
----------cacheEnabled = false 关闭的是二级缓存

2、Mapper文件中每个select标签都有useCache = “true”
----------useCache = “true” 默认是true
----------useCache = ”false“ 关闭的是二级缓存
在这里插入图片描述

3、Mapper文件中每个insert、del、update标签上都有flushCache = “true” 默认是true,每次执行完增、删、改后都会清空一级缓存,和二级缓存。其实在select标签中也有这个属性,只不过默认是false

4、如果代码中使用了sqlSession.clearCache();,该方法只会清除当前sqlSession一级缓存

5、localCacheScope:本地缓存作用域(一级缓存),在MyBatis3.3之后存在,当前会话的所有数据保留在当前会话中。
-----localCacheScope = STATEMENT,可以禁用一级缓存

1.4、MaBatis整合第三方缓存EhCache框架

  MyBatis为我们提供了Cache接口,也提供了一些实现类,进入Cache接口源码,可以看到缓存对于MyBatis来说就是一个Map,比较简陋。但是大家都知道MyBatis是一个专注于持久层框架,与数据库打交道MyBatis是很专业的,但是对于缓存它就略显不足,即便如此MyBatis却提供了Cache接口的标准规范,谁做缓存专业,谁就来实现它,提供了很好的扩展性。hibenate框架也是用EhCache作为缓存提供者。本节我们将介绍了一下MyBatis与Ehcache框架的整合。

  EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认CacheProvider。Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特点。

EhCache详情介绍,请参考:https://www.jianshu.com/p/154c82073b07

EhCache测试:
(1)引入需要的jar包:
在这里插入图片描述
(2)在项目中导入ehcache.xml文件。

ehcache.xml文件介绍
ehcache.xml文件是用来定义Ehcache的配置信息的,更准确的来说它是>定义CacheManager的配置信息的。ehcache.xml文件的根元素必须是ehcache,一个ehcache元素就相当于一个CacheManager,我们在ehcache元素上指定的属性以及在ehcache元素下定义的子元素都是针对于当前CacheManager的,比如我们在ehcache元素下定义了一个cache元素,那就代表我们所定义的CacheManager中有这么一个cache存在。
详细参考:https://www.cnblogs.com/crazylqy/p/4238148.html
在这里插入图片描述
其中,Ehcache.xml:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:noNamespaceSchemaLocation="ehcache.xsd">
 <!-- 磁盘保存路径 -->
 <diskStore path="D:\temp_Ehcache" />
 
 <defaultCache 
   maxElementsInMemory="10000" 
   maxElementsOnDisk="10000000"
   eternal="false" 
   overflowToDisk="true"
   timeToIdleSeconds="120"
   timeToLiveSeconds="120" 
   diskExpiryThreadIntervalSeconds="120"
   memoryStoreEvictionPolicy="LRU">
 </defaultCache>
</ehcache>
 
<!-- 
属性说明:
l diskStore:指定数据在磁盘中的存储位置。(内存里面满了以后会保存到磁盘)
l defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略
 
以下属性是必须的:
l maxElementsInMemory - 在内存中缓存的element的最大数目 
l maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大
l eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
l overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
 
以下属性是可选的:
l timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
l timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
 diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
l diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
l diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
l memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
 -->

(3)在mapper文件中引入Ehcache:
在这里插入图片描述

(4)在二级缓存属性开启的情况下进行测试:
在这里插入图片描述
测试类:

    @Test
    public void queryEmpById() throws IOException {
        InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder build = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = build.build(in);
        SqlSession sqlSession = factory.openSession(true);
        IEmpDao empDao = sqlSession.getMapper(IEmpDao.class);

        //创建对象1
        Emp emp_1 = empDao.queryEmpById(102);
        System.out.println(emp_1);

        sqlSession.clearCache();

        //创建对象2
        Emp emp_2 = empDao.queryEmpById(102);
        System.out.println(emp_2);

        //对象1和对象2是同一个对象么?
        System.out.println(emp_1 == emp_2);

        sqlSession.close();
        in.close();
    }

查看测试结果:
在这里插入图片描述

(5)如果是deptMapper.xml也想要使用Ehcache,那么此时在这样设置即可:
在这里插入图片描述

Ehcache执行过程总结
当执行一条查询SQL的时候,会先去二级缓存中进行查询,二级缓存不存在数据,再去查一级缓存,当一级缓存也没有数据的时候,再去查数据库。

发布了22 篇原创文章 · 获赞 16 · 访问量 2901

猜你喜欢

转载自blog.csdn.net/qq_25083447/article/details/104480972