- 缓存机制
- 1、一级缓存
- 1.1、一级缓存演示
- 1.2、一级缓存失效的四种情况
- 2、二级缓存
- 2.1、缓存相关设置
- 3、缓存原理图示
- 4、第三方缓存整合原理 & ehcache
MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存。
- 默认情况下,只有一级缓存(SqlSession 级别的缓存,也称为本地缓存)开启;
- 二级缓存需要手动开启和配置,他是基于 namespace 级别的缓存;
- 为了提高扩展性。 MyBatis 定义了缓存接口 Cache。我们可以通过实现 Cache 接口来自定义二级缓存。
一级缓存(local cache),即本地缓存, 作用域默认为 sqlSession。当 Session flush 或 close 后, 该 Session 中的所有 Cache 将被清空。
本地缓存不能被关闭,但可以调用 clearCache() 来清空本地缓存,或者改变缓存的作用域。
在 mybatis3.1 之后, 可以配置本地缓存的作用域。在 mybatis.xml 中配置:
@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='jerry3@qq.com', gender='0', dept=null} Employee{id=1, lastName='Admin', email='jerry3@qq.com', gender='0', dept=null} true
在查询结果中可以看到,sql 语句只传递了一次。empById01 对象和 empById02 对象的地址相同,是同一个对象。
同一次会话期间只要查询过的数据都会保存在当前 SqlSession 的一个 Map 中。
1.2、一级缓存失效的四种情况一级缓存失效的四种情况:
- 不同的 SqlSession 对应不同的一级缓存;
- 同一个 SqlSession 但是查询条件不同;
- 同一个 SqlSession 两次查询期间执行了任何一次增删改 *** 作;
- 同一个 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 对应一个二级缓存:
工作机制:
-
一个会话,查询一条数据;这个数据就会被放在当前会话的一级缓存中;
-
如果当前会话关闭,一级缓存的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存;
-
sqlSession === EmployeeMapper ==> Employee
DepartmentMapper ==> Department
不同 namespace 查出的数据会放在自己对应的缓存中(不同的 map)
使用:
- 开启全局二级缓存的配置:
; - 去 xxxmapper.xml 中配置使用二级缓存:
- 我们的 POJO 需要实现序列化接口
mybatis-config.xml:
EmployeeMapper.xml:
缓存相关属性:
FIFO – 先进先出:按对象进入缓存的顺序来移除它们
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象
默认的是 LRU
false:读写缓存; 会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。
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='jerry3@qq.com', 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='jerry3@qq.com', gender='0', dept=null}2.1、缓存相关设置
-
全局 setting 的 cacheEnable
配置二级缓存的开关。一级缓存一直是打开的。
-
select 标签的 useCache 属性
配置这个 select 是否使用二级缓存。一级缓存一直是使用的。
-
sql 标签的 flushCache 属性
增删改默认 flushCache=true。 sql 执行以后,会同时清空一级和二级缓存。查询默认 flushCache=false。
-
sqlSession.clearCache()
只是用来清除一级缓存。
-
localCacheScope
当在某一个作用域(一级缓存 Session/二级缓存 Namespaces)进行了 C/U/D *** 作后,默认该作用域下所有 select 中的缓存将被 clear。
3、缓存原理图示
4、第三方缓存整合原理 & ehcache
下载:https://github.com/mybatis/ehcache-cache/releases
EhCache 是一个纯 Java 的进程内缓存框架,具有快速、精干等特点,是 Hibernate 中默认的 CacheProvider :MyBatis 定义了Cache 接口方便我们进行自定义扩展 。
步骤:
-
导入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
-
编写 ehcache.xml 配置文件;
-
配置 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='jerry3@qq.com', 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='jerry3@qq.com', gender='0', dept=null}
引用缓存 DepartmentMapper.xml:
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)