MyBatis——缓存机制

MyBatis——缓存机制,第1张

MyBatis——缓存机制 缓存机制

文章目录
  • 缓存机制
    • 1、一级缓存
      • 1.1、一级缓存演示
      • 1.2、一级缓存失效的四种情况
    • 2、二级缓存
      • 2.1、缓存相关设置
    • 3、缓存原理图示
    • 4、第三方缓存整合原理 & ehcache

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

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

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

一级缓存(local cache),即本地缓存, 作用域默认为 sqlSession。当 Session flush 或 close 后, 该 Session 中的所有 Cache 将被清空。

本地缓存不能被关闭,但可以调用 clearCache() 来清空本地缓存,或者改变缓存的作用域。

在 mybatis3.1 之后, 可以配置本地缓存的作用域。在 mybatis.xml 中配置:

localCacheScopeMyBatis利用本地缓存机制防止循环引用和加速重复嵌套查询。默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。 1.1、一级缓存演示
@Test
public void testFirstLevelCache() throws IOException {
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    SqlSession sqlSession = sqlSessionFactory.openSession(true);

    try{
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        Employee empById01 = mapper.getEmpById(1);

        System.out.println(empById01);


        //xxx

        Employee empById02 = mapper.getEmpById(1);

        System.out.println(empById02);

        System.out.println(empById01 == empById02); //true
    }finally {
        sqlSession.close();
    }
}
DEBUG 12-29 21:07:48,871 ==>  Preparing: select id,last_name lastName,email,gender from tbl_employee where id = ?  (baseJdbcLogger.java:137) 
DEBUG 12-29 21:07:48,924 ==> Parameters: 1(Integer)  (baseJdbcLogger.java:137) 
DEBUG 12-29 21:07:48,954 <==      Total: 1  (baseJdbcLogger.java:137) 
Employee{id=1, lastName='Admin', email='[email protected]', gender='0', dept=null}
Employee{id=1, lastName='Admin', email='[email protected]', gender='0', dept=null}
true

在查询结果中可以看到,sql 语句只传递了一次。empById01 对象和 empById02 对象的地址相同,是同一个对象。

同一次会话期间只要查询过的数据都会保存在当前 SqlSession 的一个 Map 中。

1.2、一级缓存失效的四种情况

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

  1. 不同的 SqlSession 对应不同的一级缓存;
  2. 同一个 SqlSession 但是查询条件不同;
  3. 同一个 SqlSession 两次查询期间执行了任何一次增删改 *** 作;
  4. 同一个 SqlSession 两次查询期间手动清空了缓存。
@Test
public void testFirstLevelCache() throws IOException {
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    SqlSession sqlSession = sqlSessionFactory.openSession(true);

    try{
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        Employee empById01 = mapper.getEmpById(1);

        System.out.println(empById01);
        

        System.out.println("---------------1、sqlSession不同---------------");
        //1、sqlSession不同
        SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
        EmployeeMapper mapper1 = sqlSession1.getMapper(EmployeeMapper.class);
        Employee empById02 = mapper1.getEmpById(1);

        System.out.println(empById02);
        

        System.out.println(empById01 == empById02); //false

        sqlSession1.close();

        System.out.println("---------------2、sqlSession相同,查询条件不同---------------");
        //2、sqlSession相同,查询条件不同
        Employee empById03 = mapper.getEmpById(3);
        System.out.println(empById03);
        

        System.out.println(empById01 == empById03); //true


        System.out.println("---------------3、sqlSession相同,两次查询之间执行了增删改 *** 作---------------");
        //3、sqlSession相同,两次查询之间执行了增删改 *** 作
        mapper.addEmp(new Employee(null, "testCache", "cache", "1"));
        
        System.out.println("数据添加成功!");

        Employee empById04 = mapper.getEmpById(1);
        System.out.println(empById04);
        

        System.out.println(empById01 == empById04); //false


        System.out.println("---------------4、sqlSession相同,手动清除了一级缓存---------------");
        //4、sqlSession相同,手动清除了一级缓存
        sqlSession.clearCache();

        Employee empById05 = mapper.getEmpById(1);
        System.out.println(empById05);
        

        System.out.println(empById01 == empById05); //false
    }finally {
        sqlSession.close();
    }
}

2、二级缓存

二级缓存(second level cache),全局作用域缓存。二级缓存默认不开启,需要手动配置。MyBatis 提供二级缓存的接口以及实现,缓存实现要求 POJO 实现 Serializable 接口。二级缓存在 SqlSession 关闭或提交之后才会生效。

二级缓存:(全局缓存):基于 namespace 级别的缓存,一个 namespace 对应一个二级缓存:

工作机制:

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

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

  3. sqlSession === EmployeeMapper ==> Employee

    ​ DepartmentMapper ==> Department

    不同 namespace 查出的数据会放在自己对应的缓存中(不同的 map)

使用:

  1. 开启全局二级缓存的配置:
  2. 去 xxxmapper.xml 中配置使用二级缓存:
  3. 我们的 POJO 需要实现序列化接口

mybatis-config.xml:


    

    
    
    
    
    

EmployeeMapper.xml:



    
    

缓存相关属性:

属性描述eviction:缓存回收策略LRU – 最近最少使用的:移除最长时间不被使用的对象
FIFO – 先进先出:按对象进入缓存的顺序来移除它们
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象
默认的是 LRUflushInterval:刷新间隔,单位毫秒默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新size:引用数目,正整数代表缓存最多可以存储多少个对象,太大容易导致内存溢出readOnly:只读true:只读缓存;会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这提供了很重要的性能优势。
false:读写缓存; 会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。type:指定自定义缓存类名,实现 cache 接口即可自定义缓存

Employee.java:

public class Employee implements Serializable {

测试:

	
@Test
public void testSecondLevelTest() throws IOException {
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    SqlSession sqlSession01 = sqlSessionFactory.openSession(true);
    SqlSession sqlSession02 = sqlSessionFactory.openSession(true);

    try {
        //1.
        EmployeeMapper mapper01 = sqlSession01.getMapper(EmployeeMapper.class);

        EmployeeMapper mapper02 = sqlSession02.getMapper(EmployeeMapper.class);

        Employee empById01 = mapper01.getEmpById(1);
        System.out.println(empById01);
        sqlSession01.close();

        //第二次查询是从二级缓存中拿到的数据,并没有发送新的sql
        Employee empById02 = mapper02.getEmpById(1);
        System.out.println(empById02);

        sqlSession01.close();
        sqlSession02.close();
    }finally {

    }
}
DEBUG 12-30 10:17:48,578 Cache Hit Ratio [mybatis.dao.EmployeeMapper]: 0.0  (LoggingCache.java:60) 
DEBUG 12-30 10:17:48,585 ==>  Preparing: select id,last_name lastName,email,gender from tbl_employee where id = ?  (baseJdbcLogger.java:137) 
DEBUG 12-30 10:17:48,614 ==> Parameters: 1(Integer)  (baseJdbcLogger.java:137) 
DEBUG 12-30 10:17:48,632 <==      Total: 1  (baseJdbcLogger.java:137) 
Employee{id=1, lastName='Admin', email='[email protected]', gender='0', dept=null}
WARN  12-30 10:17:48,650 As you are using functionality that deserializes object streams, it is recommended to define the JEP-290 serial filter. Please refer to https://docs.oracle.com/pls/topic/lookup?ctx=javase15&id=GUID-8296D8E8-2B93-4B9A-856E-0A65AF9B8C66  (SerialFilterChecker.java:45) 
DEBUG 12-30 10:17:48,653 Cache Hit Ratio [mybatis.dao.EmployeeMapper]: 0.5  (LoggingCache.java:60) 
Employee{id=1, lastName='Admin', email='[email protected]', gender='0', dept=null}
2.1、缓存相关设置
  1. 全局 setting 的 cacheEnable

    配置二级缓存的开关。一级缓存一直是打开的。

  2. select 标签的 useCache 属性

    配置这个 select 是否使用二级缓存。一级缓存一直是使用的。

  3. sql 标签的 flushCache 属性

    增删改默认 flushCache=true。 sql 执行以后,会同时清空一级和二级缓存。查询默认 flushCache=false。

  4. sqlSession.clearCache()

    只是用来清除一级缓存。

  5. localCacheScope

    当在某一个作用域(一级缓存 Session/二级缓存 Namespaces)进行了 C/U/D *** 作后,默认该作用域下所有 select 中的缓存将被 clear。


3、缓存原理图示


4、第三方缓存整合原理 & ehcache

下载:https://github.com/mybatis/ehcache-cache/releases

EhCache 是一个纯 Java 的进程内缓存框架,具有快速、精干等特点,是 Hibernate 中默认的 CacheProvider :MyBatis 定义了Cache 接口方便我们进行自定义扩展 。

步骤:

  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 配置文件;

  3. 配置 cache 标签:

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

ehcache.xml:



 
 
 
 
 

 

EmployeeMapper.xml



    

测试:

DEBUG 12-30 11:04:22,803 Configuring ehcache from ehcache.xml found in the classpath: file:/G:/work/coding/java/MyBatis/MyBatis_05_cache/out/production/MyBatis_01_HelloWorld/ehcache.xml  (ConfigurationFactory.java:132) 
DEBUG 12-30 11:04:22,806 Configuring ehcache from URL: file:/G:/work/coding/java/MyBatis/MyBatis_05_cache/out/production/MyBatis_01_HelloWorld/ehcache.xml  (ConfigurationFactory.java:98) 
DEBUG 12-30 11:04:22,807 Configuring ehcache from InputStream  (ConfigurationFactory.java:150) 
DEBUG 12-30 11:04:22,830 Ignoring ehcache attribute xmlns:xsi  (BeanHandler.java:271) 
DEBUG 12-30 11:04:22,831 Ignoring ehcache attribute xsi:nonamespaceSchemaLocation  (BeanHandler.java:271) 
DEBUG 12-30 11:04:22,833 Disk Store Path: D:44ehcache  (DiskStoreConfiguration.java:141) 
DEBUG 12-30 11:04:22,845 Creating new CacheManager with default config  (CacheManager.java:1036) 
DEBUG 12-30 11:04:22,851 propertiesString is null.  (PropertyUtil.java:88) 
DEBUG 12-30 11:04:22,862 No CacheManagerEventListenerFactory class specified. Skipping...  (ConfigurationHelper.java:185) 
DEBUG 12-30 11:04:23,331 No BootstrapCacheLoaderFactory class specified. Skipping...  (Cache.java:955) 
DEBUG 12-30 11:04:23,334 CacheWriter factory not configured. Skipping...  (Cache.java:929) 
DEBUG 12-30 11:04:23,336 No CacheExceptionHandlerFactory class specified. Skipping...  (ConfigurationHelper.java:96) 
DEBUG 12-30 11:04:23,358 Initialized net.sf.ehcache.store.MemoryStore for mybatis.dao.EmployeeMapper  (MemoryStore.java:152) 
DEBUG 12-30 11:04:23,365 Using diskstore path D:44ehcache  (DiskStorePathManager.java:169) 
DEBUG 12-30 11:04:23,366 Holding exclusive lock on D:44ehcache.ehcache-diskstore.lock  (DiskStorePathManager.java:170) 
DEBUG 12-30 11:04:23,366 Failed to delete file mybatis%002edao%002e%0045mployee%004dapper.data  (DiskStorageFactory.java:860) 
DEBUG 12-30 11:04:23,366 Failed to delete file mybatis%002edao%002e%0045mployee%004dapper.index  (DiskStorageFactory.java:860) 
DEBUG 12-30 11:04:23,373 Matching data file missing (or empty) for index file. Deleting index file D:44ehcachemybatis%002edao%002e%0045mployee%004dapper.index  (DiskStorageFactory.java:168) 
DEBUG 12-30 11:04:23,373 Failed to delete file mybatis%002edao%002e%0045mployee%004dapper.index  (DiskStorageFactory.java:860) 
DEBUG 12-30 11:04:23,378 Initialised cache: mybatis.dao.EmployeeMapper  (Cache.java:1165) 
DEBUG 12-30 11:04:23,378 CacheDecoratorFactory not configured for defaultCache. Skipping for 'mybatis.dao.EmployeeMapper'.  (ConfigurationHelper.java:354) 
DEBUG 12-30 11:04:23,412 Cache Hit Ratio [mybatis.dao.EmployeeMapper]: 0.0  (LoggingCache.java:60) 
DEBUG 12-30 11:04:23,419 ==>  Preparing: select id,last_name lastName,email,gender from tbl_employee where id = ?  (baseJdbcLogger.java:137) 
DEBUG 12-30 11:04:23,447 ==> Parameters: 1(Integer)  (baseJdbcLogger.java:137) 
DEBUG 12-30 11:04:23,463 <==      Total: 1  (baseJdbcLogger.java:137) 
Employee{id=1, lastName='Admin', email='[email protected]', gender='0', dept=null}
DEBUG 12-30 11:04:23,477 put added 0 on heap  (Segment.java:425) 
DEBUG 12-30 11:04:23,480 Cache Hit Ratio [mybatis.dao.EmployeeMapper]: 0.5  (LoggingCache.java:60) 
Employee{id=1, lastName='Admin', email='[email protected]', gender='0', dept=null}

引用缓存 DepartmentMapper.xml:



    
    

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5685498.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存