1. 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其生命周期为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。
2. 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。
3. 对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。
1.缓存应用场景
查询频率比较高,而且数据信息不经常变动。客户档案定义 ,托运部定义。
2.缓失效的问题
缓存是有过期事件的 。
滑动过期:从最后一次使用开始往后推算一段时间
绝对过期:一段时间内一定会失效
3.缓存是一把双刃剑
哲学:缓存,快,高速,
容易炸,使用不当,起不到加速查询的作用。
Redis ----------->D
4.缓存的存储结构
session 的底层map集合 key value
5.第三方缓存插件
EhCache
查询缓存的使用,主要是为了提高查询访问速度。将用户对同一数据的重复查询过程简化,不再每次均从数据库查询获取结果数据,从而提高访问速度。
MyBatis查询缓存机制。根据缓存区的作用域与生命周期,可划分为两种:一级缓存和二级缓存。
MyBatis查询缓存的作用域是根据映射文件的namespace划分的,相同的namespace的mapper查询数据放在同一个缓存区域。不同namespace下的数据互不干扰。无论是一级缓存还是二级缓存,都是按照namespace进行分别存放的。
但是一级、二级缓存的不同之处在于,SqlSession一旦关闭,则SqlSession中的数据将不存在,即一级缓存就不复存在。而二级缓存的生命周期与整个应用同步,与SqlSession是否关闭无关。换句话说,一级缓存是在同一线程(同一SqlSession)间共享数据,而二级缓存是在不同线程(不同的SqlSession)间共享数据。
缓存的底层实现是一个Map,Map的value是查询结果
Map的key,即查询依据,使用的ORM架构不同,查询依据是不同的
MyBatis的查询依据是:Sql的id+SQL语句
Hibernate的查询依据是:查询结果对象的id
测试:
@Test
public void show(){
List<Result> list=null;
SqlSession session= UtilMybatis.getsession();
try {
list= session.getMapper(ResultDao.class).findAllResult();
} catch (Exception e) {
e.printStackTrace();
}
for(Result result:list){
System.out.println(result);
}
System.out.println("==========一级缓存===========");
try {
list= session.getMapper(ResultDao.class).findAllResult();
} catch (Exception e) {
e.printStackTrace();
}finally {
UtilMybatis.closesession(session);
}
for(Result result:list){
System.out.println(result);
}
}
结果:sql只执行一次后将id与sql 存储在一级缓存内(使用同一个session)
增删改会清空一级缓存:注意:必须使用insert标签,不能使用select,否则实验做不成功
测试:
public void show() throws Exception {
List<Result> list=null;
SqlSession session= UtilMybatis.getsession();
try {
list= session.getMapper(ResultDao.class).findAllResult();
} catch (Exception e) {
e.printStackTrace();
}
for(Result result:list){
System.out.println(result);
}
System.out.println("==========增加===========");
Result result1=new Result();
result1.setAge(18);
result1.setScore(80);
result1.setSubjectName("shuxue");
Integer count=session.getMapper(ResultDao.class).adderesult(result1);
System.out.println(count);
System.out.println("==========一级缓存===========");
try {
list= session.getMapper(ResultDao.class).findAllResult();
} catch (Exception e) {
e.printStackTrace();
}finally {
UtilMybatis.closesession(session);
}
for(Result result:list){
System.out.println(result);
}
}
结果:session用同一个,在添加后在查询因(增加清空一级缓存)故在进行db查询
由于MyBatis从缓存中读取数据的依据与SQL的id相关,而非查询出的对象。所以,使用二级缓存的目的,不是在多个查询间共享查询结果(所有查询中只要查询结果中存在该对象,就直接从缓存中读取,这是对查询结果的共享,Hibernate中的缓存就是为了在多个查询间共享查询结果,但MyBatis不是),而是为了防止同一查询(相同的Sql id,相同的sql语句)的反复执行。
https://my.oschina.net/KingPan/blog/280167
http://www.mamicode.com/info-detail-890951.html
http://blog.csdn.net/isea533/article/details/44566257
http://blog.csdn.net/u010676959/article/details/43953087
http://blog.csdn.net/xiadi934/article/details/50786293
如何开启二级缓存,二级缓存在Mybatis默认是开启(全局配置的某个属性值为true)的,但是开始和能直接使用时两码事。
1.你cacheEnabled=true,默认值为true
<setting name="cacheEnabled" value="true"></setting>
2.你得在Mapper文件中,<cache/>
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,
而且返回的对象被认为是只读的
3.Entity Implements Serializable(实体类序列化)
缓存中对象可用的收回策略
LRU – 最近最少使用的:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
默认的是 LRU。
flushInterval(刷新间隔)可以被设置为任意的正整数,而且它们代表一个合理的毫秒
形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的
可用内存资源数目。默认值是 1024。
readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回
缓 存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
可读写的缓存 会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false。
1.增删改也会清空二级缓存
2.对于二级缓存的清空实质上是对value清空为null,key依然存在,并非将Entry<k,v>删除
3.从DB中进行select查询的条件是:
1.缓存中根本不存在这个key
2.存在key对应的Entry,但是value为null
二级缓存的关闭
全局关闭:
局部关闭
二级缓存的使用原则
1.多个namespace不要操作同一张表
2.查询大于修改的情况下使用
在MyBatis中通过ehcache配置二级缓存
1.jar包
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.4</version>
</dependency>
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.1.0</version>
</dependency>
2.在小配置中添加一个缓存类
在小配置中输入EhcacheCache 并选中 双击shift
点击EhcacheCache 出现
鼠标右击出现
粘贴在
粘贴前:
粘贴后:
3.在src下植入ehcache.xml文件
<ehcache>
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/>
<cache name="sampleCache1"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
/>
<cache name="sampleCache2"
maxElementsInMemory="1000"
eternal="true"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="false"
/> -->
</ehcache>
测试: public void show() throws Exception {
List<Result> list=null;
SqlSession session= UtilMybatis.getsession();
try {
list= session.getMapper(ResultDao.class).findAllResult();
} catch (Exception e) {
e.printStackTrace();
}finally {
UtilMybatis.closesession(session); //session关闭
}
for(Result result:list){
System.out.println(result);
}
System.out.println("========二级缓存=============");
SqlSession session1= UtilMybatis.getsession();
try {
list= session1.getMapper(ResultDao.class).findAllResult();
} catch (Exception e) {
e.printStackTrace();
}finally {
UtilMybatis.closesession(session1);
}
for(Result result:list){
System.out.println(result);
}
}
结果:sql语句执行一次