MyBatis知识点复习-11一级缓存与二级缓存
下一篇:MyBatis知识点复习-12mybatis逆向工程
文章目录
本章我们将为大家讲解mybatis比较重要的一个知识点——缓存。mybatis的缓存分为一级缓存与二级缓存,其中一级缓存是默认开启的,二级缓存是需要手动开启的。从大的角度来说mybatis的缓存是查询缓存,就是说只有在做查询操作是才会有缓存产生。最后我们会讲一下ehcache缓存。
一.一级缓存与二级缓存概念
一级缓存指的就是sqlsession,sqlsession有一个数据区域,是一个map结构,里面放的是键值对,这个区域(或者说这个map)就是一级缓存区域。key是由sql语句等信息组成的一个唯一值,value就是我们查询出来的对象。
二级缓存是指在同一个namaspace下的mapper(也就是SqlsessionFactory),在二级缓存中也存在一个map结构,他就是一级缓存,这个一级缓存比较特殊,他是被多个SqlSession所共享的。同时这个一级缓存的key也是sql语句等信息组成的一个唯一值。
下面我们用一个图解让大家更好的理解:
二.一级缓存的具体原理与案例
下面我们来演示一下。
在Test方法执行下面代码:
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = mapper.findUserById(1);
User user2 = mapper.findUserById(1);
System.out.println(user1.hashCode());
System.out.println(user2.hashCode());
sqlSession.commit();//提交事务,否则会事务回滚
这个时候我们发现user1与user2的hashcode一样且只执行了一次sql语句。这是因为一级缓存是默认开启的,且一级缓存读取的是对象,而不是对象的数据来构造新的对象。
下面我们在user1与user2间插入一个插入操作。如下:
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = mapper.findUserById(1);
//=======插入代码========
User user = new User();
user.setUsername("张三");
//================
mapper.saveUser(user);
User user2 = mapper.findUserById(1);
System.out.println(user1.hashCode());
System.out.println(user2.hashCode());
sqlSession.commit();//提交事务,否则会事务回滚
我们发现user1与user2的hashcode并不一样,并且执行了三条sql语句的结果。这是由于执行了插入操作后会把一级缓存的数据清空。
三.二级缓存的具体原理与案例
上面的那个写入二级缓存我们需要注意一下,只有在sqlsession1关闭后他才会生效。
下面我们来演示一下。
首先我们要知道mybatis的二级缓存默认是关闭的我们需要去手动打开它。
那么第一步先在全局配置文件的<settings>下面加入下面内容:
<!--开启二级缓存-->
<setting name="cacheEnable" value="true"/>
接下来还需要在对应的mapper的配置文件中的<mapper>下加入标签对<cache>,这里我们使用UserMapper.xml
<!--配置缓存-->
<cache></cache>
其实cache标签还有一个type属性用来指定缓存的类型,但是不写会默认为是perpetualCache(永久缓存)
在Test方法执行下面代码:
SqlSession sqlSession1 = build.openSession();
SqlSession sqlSession2 = build.openSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User user1 = mapper1.findUserById(1);
sqlSession1.close();
User user2 = mapper2.findUserById(1);
System.out.println(user1.hashCode()+" "+user2.hashCode());
this.sqlSession.commit();//提交事务,否则会事务回滚
通过上图我们发现只执行了一次sql语句,但是user1与user2的hashcode却不一样,这是由于二级缓存不想一级缓存读取的是对象而是数据然后重新构造新的对象,这样做的是由于二级缓存是被共享的是存在数据安全的问题,因此我们不可以直接获取对象而是通过它的数据来构造出新的对象。
接下来我们在执行下面的代码:
SqlSession sqlSession1 = build.openSession();
SqlSession sqlSession2 = build.openSession();
SqlSession sqlSession3 = build.openSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
UserMapper mapper3 = sqlSession3.getMapper(UserMapper.class);
User user1 = mapper1.findUserById(1);
sqlSession1.close();
User user = new User();
user.setUsername("战神");
mapper3.saveUser(user);
User user2 = mapper2.findUserById(1);
System.out.println(user1.hashCode()+" "+user2.hashCode());
this.sqlSession.commit();//提交事务,否则会事务回滚
我们观察发现居然只有两条sql语句执行了,但是明明我们执行了saveUser这个操作后会清空二级缓存那么理应有三条呀,问题出现在哪了?
其实我们在执行完保存操作后应该把sqlSession的事务进行提交才能够生效。
加上后就对了。
上面我们说了在做提交操作时会导致二级缓存被清空,那么有什么办法可以阻止不被全清空吗?那是肯定的
使用了上面的配置后,我们再来执行看结果:
果然只有两次执行sql语句。
最后再补充一个如何对指定方法禁用二级缓存:
这个留给读者自己尝试。
四.ehcache缓存
首先我们在开发中一般都是遵循三层架构的思想:
表示层,业务逻辑层,数据表示层(持久层)。
而我们的mybatis框架就是属于持久层的框架。但是它本身不是专门为缓存设计的,因此对缓存的实现的不是十分好,这也导致他无法支持分布式。而Ehcache是一个分布式的缓存框架。
1.什么是分布式
我用王者荣耀来讲解一下:
系统为了提高性能,通常会对系统采用分布式部署(集群部署方式),那么每个服务器来运行同样的项目这就是分布式部署(集群部署)。
2.mybatis整合ehcache框架
前面我们说了在开启二级缓存后默认的是使用perpetualCache(永久缓存),这是mybatis自带的一个缓存。而Cache是一个接口perpetualCache是他的一个实现类
这是Cache的内容:
public interface Cache {
String getId();
void putObject(Object var1, Object var2);
Object getObject(Object var1);
Object removeObject(Object var1);
void clear();
int getSize();
ReadWriteLock getReadWriteLock();
}
下面我们开始整合ehcache:
第一步:导入jar包
链接:https://pan.baidu.com/s/1BSEa53HaEh_yt-qx1rPmAQ
提取码:qc0g
第二步:
导包后我们可以找到:
第三步:将我们前面的那个<cache>指定type:
\<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
第四步:
在我分享的文件包解压后可以找到下面的文件:
拷贝到src目录下面来更名为ehcache.xml:
如果出现了下面的问题可以参看此文:idea中ehcahe配置中 Cannot find the declaration of element ‘ehcache’.
现在我们已经配置完成了,但是这里我无法演示集群的效果,但是可以演示一下二级缓存配置是成功的。
再次在test方法里面测试下面代码:
SqlSession sqlSession1 = build.openSession();
SqlSession sqlSession2 = build.openSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User user1 = mapper1.findUserById(1);
sqlSession1.close();
User user2 = mapper2.findUserById(1);
System.out.println(user1.hashCode()+" "+user2.hashCode());
this.sqlSession.commit();//提交事务,否则会事务回滚
只执行了一次sql语句。正说明配置成功了。
3.ehcache.xml文件配置信息讲解
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
</ehcache>
maxElementsInMemory :
设置基于内存的缓存中可存放的对象最大数目
eternal:
设置对象是否为永久的,true表示永不过期,此时将忽略
timeToIdleSeconds 和 timeToLiveSeconds
两者默认值是false
timeToIdleSeconds:设置对象空闲最长时间,以秒为单位, 超过这个时间,对象过期。当对象过期时,Ehcache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态。
timeToLiveSeconds:设置对象生存最长时间,超过这个时间,对象过期。如果此值为0,表示对象可以无限期地存在于缓存中. 该属性值必须大于或等于 timeToIdleSeconds 属性值
overflowToDisk:
设置基于内在的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中
diskPersistent
当jvm结束时是否持久化对象 true false 默认是false
diskExpiryThreadIntervalSeconds:
指定专门用于清除过期对象的监听线程的轮询时间
memoryStoreEvictionPolicy:
当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(The Least Recently Used)(最近最少使用),可选的有LFU(Least Frequently Used )(最不常使用)和FIFO(先进先出)
在看到最后这个其实和计算机组成原理的cache的替换策略是类似的。
在使用二级缓存时我们要注意定时刷新,否则可能会导致读取的数据不是最新的而出错。设置方式是在<cache>下有一个flushInterval来设置单位为毫秒,我这里设置的是30分钟。
上一篇:MyBatis知识点复习-10懒加载讲解
下一篇:MyBatis知识点复习-12mybatis逆向工程