6. MyBatis缓存

1. 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其生命周期为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。

2. 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

3. 对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。

 

 

面试必考

1.缓存应用场景

查询频率比较高,而且数据信息不经常变动。客户档案定义 ,托运部定义。

2.缓失效的问题

缓存是有过期事件的 。

滑动过期:从最后一次使用开始往后推算一段时间

绝对过期:一段时间内一定会失效

3.缓存是一把双刃剑

哲学:缓存,快,高速,

容易炸,使用不当,起不到加速查询的作用。

Redis ----------->D

4.缓存的存储结构

session 的底层map集合 key value

5.第三方缓存插件

EhCache

查询缓存的使用,主要是为了提高查询访问速度。将用户对同一数据的重复查询过程简化,不再每次均从数据库查询获取结果数据,从而提高访问速度。

mybatis 缓存 随笔 第1张

6.1 缓存原理 mybatis 缓存 随笔 第2张

 

mybatis 缓存 随笔 第3张 6.2 缓存分类

MyBatis查询缓存机制。根据缓存区的作用域与生命周期,可划分为两种:一级缓存和二级缓存。

 

  MyBatis查询缓存的作用域是根据映射文件的namespace划分的,相同的namespace的mapper查询数据放在同一个缓存区域。不同namespace下的数据互不干扰。无论是一级缓存还是二级缓存,都是按照namespace进行分别存放的。

  但是一级、二级缓存的不同之处在于,SqlSession一旦关闭,则SqlSession中的数据将不存在,即一级缓存就不复存在。而二级缓存的生命周期与整个应用同步,与SqlSession是否关闭无关。换句话说,一级缓存是在同一线程(同一SqlSession)间共享数据,而二级缓存是在不同线程(不同的SqlSession)间共享数据。

 

 

6.3 一级缓存 6.3.1 一级缓存--从缓存中查找数据的依据:

缓存的底层实现是一个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)

mybatis 缓存 随笔 第4张

 

mybatis 缓存 随笔 第5张

6.3.2 增、删、改对一级缓存的影响

增删改会清空一级缓存:注意:必须使用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 缓存 随笔 第6张

 

mybatis 缓存 随笔 第7张

6.3.2 内置二级缓存(在同一mapper.xml文件中)

       由于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。

 

 

 

6.3.4. 增删改对二级缓存的影响

 1.增删改也会清空二级缓存

      2.对于二级缓存的清空实质上是对value清空为null,key依然存在,并非将Entry<k,v>删除

      3.从DB中进行select查询的条件是:

              1.缓存中根本不存在这个key

              2.存在key对应的Entry,但是value为null

 

二级缓存的关闭

 

全局关闭:

mybatis 缓存 随笔 第8张

 

mybatis 缓存 随笔 第9张

 

局部关闭

mybatis 缓存 随笔 第10张

 

mybatis 缓存 随笔 第11张

二级缓存的使用原则

1.多个namespace不要操作同一张表

2.查询大于修改的情况下使用

6.3.5 引入第三方插件Mybatis集成EhCache

在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

mybatis 缓存 随笔 第12张

 

mybatis 缓存 随笔 第13张

点击EhcacheCache 出现

mybatis 缓存 随笔 第14张

 

mybatis 缓存 随笔 第15张

鼠标右击出现

mybatis 缓存 随笔 第16张

 

mybatis 缓存 随笔 第17张

粘贴在

粘贴前:

mybatis 缓存 随笔 第18张

 

mybatis 缓存 随笔 第19张

粘贴后:

mybatis 缓存 随笔 第20张

 

mybatis 缓存 随笔 第21张

   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语句执行一次

mybatis 缓存 随笔 第22张

 

mybatis 缓存 随笔 第23张

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄