MyBatis-3(四)缓存机制

MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。

• MyBatis系统中默认定义了两级缓存:
•  一级 缓存和 二级缓存。
       – 1、默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。
       – 2、二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
       – 3、为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

1、一级缓存

1.1 概述

       一级缓存也称为本地缓存:sqlSession级别的缓存,与数据库同一次会话期间(同一个SqlSession)查询到的数据会放在本地缓存中。以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库。当 SqlSession flush 或 close 后, 该SqlSession 中的所有 Cache 将被清空。

       一级缓存是一直开启的(无法关闭);但可以调用 clearCache()来清空本地缓存, 或者改变缓存的作用域。

        一级缓存的实现原理就是:SqlSession级别的一个Map,把查询到的数据放到Map中,下次的查询就先从Map里面找,没有找到才去查询数据库。

       在mybatis3.1之后, 可以配置本地缓存的作用域,在 全局配置文件mybatis-config.xml 中配置:

设置名描述有效值默认值
localCacheScope       MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。SESSION | STATEMENTSESSION

1.2 演示一级缓存

1.2.1 映射文件sql

<select id="selectById" resultType="com.mybatis.pojo.Employee">
		select id,last_name,age,addr from t_employee where id = #{id}
	</select>

1.2.2 mapper接口

public Employee selectById(String id);

1.2.3 测试查看打印日志

 @Test
    public void test2(){
        SqlSession sqlSession = null;
        try {
            sqlSession = sessionFactory.openSession();
            EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
            Employee employee = employeeMapper.selectById("1");//第一次查询,肯定走数据库
            System.out.println(employee);
            Employee employee2 = employeeMapper.selectById("1");//第二次查询不会发送sql,走的缓存
            System.out.println(employee2);
        }finally {
            sqlSession.close();
        }
    }
DEBUG 02-20 15:16:04,617 ==>  Preparing: select id,last_name,age,addr from t_employee where id = ?   (BaseJdbcLogger.java:145) 
DEBUG 02-20 15:16:04,737 ==> Parameters: 1(String)  (BaseJdbcLogger.java:145) 
DEBUG 02-20 15:16:04,764 <==      Total: 1  (BaseJdbcLogger.java:145) 
Employee{id='1', lastName='tom', age=22, addr='changshashi'}
Employee{id='1', lastName='tom', age=22, addr='changshashi'}

可以看到,第二查询的时候,并没有发送sql,而是从缓存中取出的数据。

1.3 一级缓存失效的四种情况

• 同一次会话期间只要查询过的数据都会保存在当前SqlSession的一个Map中。
       • key:hashCode+查询的SqlId+编写的sql查询语句+参数


一级缓存失效的四种情况
      – 1、sqlSession不同:不同的SqlSession对应不同的一级缓存;
      – 2、sqlSession相同:查询条件不同(当前一级缓存中还没有这个数据)
      – 3、sqlSession相同:两次查询期间执行了任何一次增删改操作(因为这次增删改可能对当前数据有影响)
      – 4、sqlSession相同:两次查询期间手动清空了缓存(openSession.clearCache())

 

2、二级缓存

2.1 概述

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

工作机制:

      1、一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中;

      2、如果会话关闭;一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存中的内容;

      3、sqlSession===EmployeeMapper==>Employee
                                   DepartmentMapper===>Department
                       不同namespace查出的数据会放在自己对应的缓存中(map)

 

2.2 开启二级缓存

二级缓存默认不开启,需要手动配置

1、在全部配置文件中,开启全局二级缓存配置:<setting name="cacheEnabled" value="true"/>

设置名描述有效值默认值
cacheEnabled全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。true | falsetrue

虽然他的默认值是true,即开启二级缓存的,但是我们建议还是在全局配置文件中显式的配置,以免版本更迭,下个版本出来的时候,默认值变成了fasle。 

2、到相关的mapper.xml文件中配置使用二级缓存,使用标签<cache></cache>

<cache>标签有下面几个属性:

•  eviction=“ FIFO” :缓存回收策略,有如下四个值:
       • LRU – 最近最少使用的:移除最长时间不被使用的对象。默认的是 LRU。
       • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
       • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
       • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

•  flushInterval :刷新间隔,单位毫秒,• 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
•  size :引用数目,正整数 • 代表缓存最多可以存储多少个对象,太大容易导致内存溢出
•  readOnly :只读,true/false
       • true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
       • false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。

•  type:自定义缓存时,实现缓存的Java类全名。

3、我们对应的pojo需要实现序列化接口

2.3、演示二级缓存

2.3.1 在全局配置文件mybatis-config.properties中显式的开启二级缓存

		<setting name="cacheEnabled" value="true"/>

2.3.2 在EmployeeMapper.xml中配置<cache>

    <!--开启缓存,使用默认配置-->
	<cache></cache>

2.3.3 Employee类实现Serializable接口

public class Employee implements Serializable {
}

2.3.4 测试

@Test
    public void testSecondCache(){
        //第一次会话
        SqlSession sqlSession1 = sessionFactory.openSession();
        EmployeeMapper employeeMapper = sqlSession1.getMapper(EmployeeMapper.class);
        Employee employee = employeeMapper.selectById("1");//第一次查询,肯定走数据库
        System.out.println(employee);
        sqlSession1.close();//只有关闭会话,才会把数据存入到二级缓存中

        //第二次会话
        SqlSession sqlSession2 = sessionFactory.openSession();
        EmployeeMapper employeeMapper2 = sqlSession2.getMapper(EmployeeMapper.class);
        Employee employee2 = employeeMapper2.selectById("1");//第一次查询,肯定走数据库
        System.out.println(employee2);
        sqlSession2.close();
    }

打印结果:

DEBUG 02-20 22:32:06,870 Cache Hit Ratio [com.mybatis.mapper.EmployeeMapper]: 0.0  (LoggingCache.java:62) 
DEBUG 02-20 22:32:07,245 ==>  Preparing: select id,last_name,age,addr from t_employee where id = ?   (BaseJdbcLogger.java:145) 
DEBUG 02-20 22:32:07,309 ==> Parameters: 1(String)  (BaseJdbcLogger.java:145) 
DEBUG 02-20 22:32:07,337 <==      Total: 1  (BaseJdbcLogger.java:145) 
Employee{id='1', lastName='tom', age=22, addr='changshashi'}
DEBUG 02-20 22:32:07,431 Cache Hit Ratio [com.mybatis.mapper.EmployeeMapper]: 0.5  (LoggingCache.java:62) 
Employee{id='1', lastName='tom', age=22, addr='changshashi'}

可以从打印日志看出:每次都会Cache hit Ratio:计算缓存的命中率,第一次是0.0 ,也就是进行了一次查询,没有命中缓存。第二次的Cache Hit Ratio的值是0.5,也就是两次查询,第二次命中了缓存,命中率是0.5的意思。

二级缓存是namespace级别的缓存,也就是两次会话的结果都会缓存到EmployeeMapper的缓存中,第二次查询就能从缓存中获取数据了。

注意:查出的数据都会被默认先放在一级缓存中。只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中

这句话的意思是:如果我们把sqlSession1.close() 代码放到第二次方法后面,那么第二次查询也不会走缓存。因为我们的sqlSession1还没有关闭,数据只存在一级缓存,但是第二次查询又跟第一次不是同一个会话,所以是不会查询缓存的。

 

3、和缓存有关的设置/属性

1、cacheEnabled=true(默认值,开启二级缓存):false:关闭缓存(二级缓存关闭)(这个值不影响一级缓存,一级缓存一直可用的)。

2、每个select标签都有属性 useCache="true"(默认值true),false:不使用缓存(二级缓存不使用,一级缓存依然使用)。

3、每个增删改标签都有属性 flushCache="true"(默认值true):增删改执行完成后就会清除缓存(一级二级都会清除);每个查询标签也有属性 flushCache="false"(默认值false),查询操作不会清除缓存,如果查询标签设置:flushCache="true",每次查询之后都会清空缓存;缓存是没有被使用的;

4、sqlSession.clearCache();  只是清楚当前session的一级缓存;二级缓存不受影响。

5、在全局配置文件的<settings>标签内可以设置 localCacheScope(本地缓存作用域,影响的是一级缓存),默认值是session(缓存数据保存到会话中),另一个值是STATEMENT(相当于禁用一级缓存)。

 

4、缓存原理图

 

5、MyBatis整合第三方缓存EhCache

EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。

为了整合第三方缓存,MyBatis定义了Cache接口方便我们进行自定义扩展。

5.1 整合步骤

1、导入第三方缓存包以及相关依赖包,还有整合相关的支持包,整合EhCache需要的包如下:

ehcache-core-2.6.8.jar、mybatis-ehcache-1.0.3.jar、slf4j-api-1.6.1.jar、slf4j-log4j12-1.6.2.jar

2、编写ehcache.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
 <!-- 磁盘保存路径 -->
 <diskStore path="D:\44\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.xml文件中配置cache标签,cache标签指定type属性,指向EhCache的实现

<mapper namespace="com.mybatis.mapper.EmployeeMapper">
	<!--开启缓存,使用EhCache的缓存实现-->
	<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
</mapper>

参照缓存:若想在命名空间中共享相同的缓存配置和实例。可以使用 cache-ref 元素来引用另外一个缓存。

<mapper namespace="com.mybatis.mapper.DepartmentMapper">
    <!--参照EmployeeMapper的缓存方案-->
    <cache-ref namespace="com.mybatis.mapper.EmployeeMapper"/>
</mapper>

4、缓存原理:

注意:当执行一条查询SQL时,流程为:1 从二级缓存中进行查询;2 从一级缓存中查询;3 查询数据库。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值