缓存
一、一级缓存
-
一级缓存的级别:SqlSession
默认开启的。通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问
-
测试
@Test public void test1() { SqlSession sqlSession = SqlSessionUtil.getSqlSession(); CacheMapper mapper1 = sqlSession.getMapper(CacheMapper.class); Emp emp1 = mapper1.getEmpByCache(1); System.out.println("emp1 = " + emp1); CacheMapper mapper2 = sqlSession.getMapper(CacheMapper.class); Emp emp2 = mapper2.getEmpByCache(1); System.out.println("emp2 = " + emp2); }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UIAepz9v-1653983200092)(F:\MarkDown学习\图片素材-1\MyBatis\缓存\1.一级缓存实验结果.jpg)]
-
一级缓存失效的四种情况
① 不同的SqlSession对应不同的一级缓存
② 同一个SqlSession,但查询条件不同
③ 同一个SqlSession,两次查询期间执行了任何一次增删改操作
④ 同一个SqlSession,两次查询期间手动清空了缓存:sqlSession.clearCache();
二、二级缓存
-
级别
需手动开启。二级缓存是SqlSessionFactory级别,通过用一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后,再执行相同的查询语句,结果就会从缓存中换取。
-
开启条件
① 在核心配置文件中,设置全局配置属性cacheEnabled=true,默认为true,不需要设置
② 在映射文件中设置标签< cache/>
③ 二级缓存在SqlSession关闭或提交之后有效
// 关闭 sqlSession.close(); // 提交 sqlSession.commit();
④ 查询的数据所转换的实体类类型必须实现序列化接口
public class Emp implements Serializable { // 省略属性、get/set方法等 }
-
测试
@Test public void test2() { try { InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is); // 使用同一个SqlSessionFactory对象创建sqlSession1 SqlSession sqlSession1 = factory.openSession(true); CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class); Emp emp1 = mapper1.getEmpByCache(1); System.out.println("emp1 = " + emp1); sqlSession1.close(); // 使用同一个SqlSessionFactory对象创建sqlSession2 SqlSession sqlSession2 = factory.openSession(true); CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class); Emp emp2 = mapper2.getEmpByCache(1); System.out.println("emp2 = " + emp2); } catch (IOException e) { e.printStackTrace(); } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-khiBM9VP-1653983200093)(F:\MarkDown学习\图片素材-1\MyBatis\缓存\2.二级缓存实验结果.jpg)]
-
使二缓存失效的情况
两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
三、二级缓存的cache标签内的相关属性的设置
-
eviction:缓存回收策略
LRU , Least Recently Used ,最近最少使用的:移除最长时间不被使用的对象(默认)
FIFO,First in First out ,先进先出:按对象进入缓存的顺序移除它们
SOFT,软引用:移除基于垃圾回收器状态和软引用规则的对象
WEAK,弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象
-
flushInterval:刷新间隔,单位毫秒
默认不设置,即没有刷新间隔,缓存仅仅调用语句时刷新
-
size:引用数目,正整数
代表缓存最多可以存储多少个对象,太大容易导致内存溢出
-
readOnly:只读(默认false)
true,只读缓存,会给所有调用者返回缓存对象的相同实例,性能高
false,读写缓存,会返回缓存对象的拷贝(通过序列化),安全
四、MyBatis缓存查询的顺序
- 先查询二级缓存,因为二级缓存中有多个SqlSession,可能会有其它程序查出来的数据,可以拿来直接使用
- 如果二级缓存没有命中,再查询一级缓存
- 如果一级缓存没有命中,则查询数据库
- 注:SqlSession关闭后,一级缓存中的数据会写入二级缓存
五、整合第三方的缓存EHCache
-
为什么要引入第三方缓存
因为MyBatis本质是持久层框架,做缓存业务不是很专业,故提供了缓存接口,可以由其它缓存技术作为其二级缓存。但是第三方缓存不能做MyBatis的一级缓存
-
引入依赖
<!-- MyBatis EHCache整合包 --> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.2.1</version> </dependency> <!-- slf4j 日志门面的一个具体实现 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency>
-
对应的两个配置文件
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:\ehcache"/> <defaultCache maxElementsInMemory="1000" maxElementsOnDisk="10000000" eternal="false" overflowToDisk="true" timeToIdleSeconds="120" timeToLiveSeconds="120" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> </defaultCache> </ehcache>
logback.xml
<?xml version="1.0" encoding="UTF-8"?> <configuration debug="true"> <!-- 指定日志输出的位置 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <!-- 日志输出的格式 --> <!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 --> <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern> </encoder> </appender> <!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR --> <!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 --> <root level="DEBUG"> <!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender --> <appender-ref ref="STDOUT" /> </root> <!-- 根据特殊需求指定局部日志级别 --> <logger name="com.atguigu.crowd.mapper" level="DEBUG"/> </configuration>
-
日志各框架说明
-
EHCache配置文件说明
属性名 是否必须 作用 maxElementsInMemory 是 在内存中缓存的element的最大数目 maxElementsOnDisk 是 在磁盘上缓存的element的最大数目,若是0表示无穷大 eternal 是 设定缓存的elements是否永远不过期。 如果为true,则缓存的数据始终有效, 如果为false那么还要根据timeToIdleSeconds、timeToLiveSeconds判断 overflowToDisk 是 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上 timeToIdleSeconds 否 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时, 这些数据便会删除,默认值是0,也就是可闲置时间无穷大 timeToLiveSeconds 否 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大 diskSpoolBufferSizeMB 否 DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区 diskPersistent 否 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。 diskExpiryThreadIntervalSeconds 否 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s, 相应的线程会进行一次EhCache中数据的清理工作 memoryStoreEvictionPolicy 否 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。 默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)