Mybatis中的缓存管理

查询缓存工作原理:

查询语句 <---> 缓存 <----> DB

缓存内容:

Mybatis中存放的是查询的内容;

缓存对比:

类别 作用范围 生命周期
一级缓存 namespace 和sqlsession的生存周期相同
二级缓存 namespace 和整个应用的生命周期相同

一级缓存:默认是开启的,无开关关闭;

二级缓存:默认是开启的,需要手动关闭,二级缓存可以外置第三方的产品。

配置缓存:

默认配置:

  • 一级缓存默认是开启的,无需配置,也不能关闭;

  • 二级缓存也是默认开启状态的,如果想要关闭的话在“全局配置文件”中配置;全局关闭之后后面的配置也就不起作用了;

<configuration>
<!--二级缓存的全局开关-->
<settings>
<setting name="cacheEnabled" value="false"/>
</settings>
</configuration>

局部关闭设置:

<!--在查询语句里添加设置:useCache="false"-->
<select id="selectStudentById2" resultType="Student" useCache="false">
select id,name,age,score from student where id=#{xx}
</select>

开启内置二级缓存:

  1. 在mapper文件中添加并配置标签:

    <cache size="512" eviction="LRU" flushInterval="100000"/>
    • flushInterval:刷新间隔;单位是ms;

    • eviction:收回缓存的策略;LRU为默认值,移除最长时间不被使用的对象;

    • size:可以存储集合或者对象的引用数目;

    • readOnly:设置只读,默认值为false;

    为什么配置在mapper文件中:由于Mybatis的二级缓存是和命名空间绑定的(作用范围),所以二级缓存需要配置在Mapper.xml 映射文件中或者在接口文件中(注解形式配置);

  2. 操作的实体类实现Serializable接口。

使用二级缓存:

证明二级缓存Demo

public void Test01() throws IOException {
SqlSession session = MyBatisUtil.getSqlSession();
studentDao = session.getMapper(IStudentDao.class);
//第一次查询
Student student = studentDao.selectStudentById(2);
System.out.println("第一次查询:"+student);
//关闭一级缓存
session.close(); SqlSession session2 = MyBatisUtil.getSqlSession();
studentDao = session2.getMapper(IStudentDao.class);
//第二次查询
Student student2 = studentDao.selectStudentById(2);
System.out.println("第二次查询:"+student2);
//关闭一级缓存
session2.close(); SqlSession session3 = MyBatisUtil.getSqlSession();
studentDao = session3.getMapper(IStudentDao.class);
//第三次查询
Student student3 = studentDao.selectStudentById(2);
System.out.println("第三次查询:"+student3);
//关闭一级缓存
session3.close();
}

输出日志文件:

[DEBUG] Cache Hit Ratio [com.abc.dao.IStudentDao]: 0.0
[DEBUG] ==> Preparing: select id,name,age,score from student where id=?
[DEBUG] ==> Parameters: 2(Integer)
[DEBUG] <== Total: 1
第一次查询:Student{id=2, name='李四', age=18, score=90.0}
[DEBUG] Cache Hit Ratio [com.abc.dao.IStudentDao]: 0.5
第二次查询:Student{id=2, name='李四', age=18, score=90.0}
[DEBUG] Cache Hit Ratio [com.abc.dao.IStudentDao]: 0.6666666666666666
第三次查询:Student{id=2, name='李四', age=18, score=90.0}

可以看到第一次查询访问DB,关闭了sqlsession之后一级缓存已经无效,但是第二次和第三次查询都成功命中了缓存;

过程:在一级缓存的sqlsession关闭之后,sqlsession就会把数据保存到二级缓存中,在这之后二级缓存就有了缓存数据;

刷新缓存Demo:

public void Test02() throws IOException {
SqlSession session = MyBatisUtil.getSqlSession();
studentDao = session.getMapper(IStudentDao.class);
//第一次查询
Student student = studentDao.selectStudentById(2);
System.out.println("第一次查询:"+student);
//关闭一级缓存
session.close(); SqlSession session2 = MyBatisUtil.getSqlSession();
studentDao = session2.getMapper(IStudentDao.class);
//执行增删改操作
studentDao.insertStudent(new Student("小吕",21,96)); //第二次查询
Student student2 = studentDao.selectStudentById(2);
System.out.println("第二次查询:"+student2);
//关闭一级缓存
session2.close();
}

日志文件:

[DEBUG] ==>  Preparing: select id,name,age,score from student where id=?
[DEBUG] ==> Parameters: 2(Integer)
[DEBUG] <== Total: 1
第一次查询:Student{id=2, name='李四', age=18, score=90.0}
[DEBUG] ==> Preparing: insert into student (name,age,score) value (?,?,?)
[DEBUG] ==> Parameters: 小吕(String), 21(Integer), 96.0(Double)
[DEBUG] <== Updates: 1
[DEBUG] Cache Hit Ratio [com.abc.dao.IStudentDao]: 0.5
[DEBUG] ==> Preparing: select id,name,age,score from student where id=?
[DEBUG] ==> Parameters: 2(Integer)
[DEBUG] <== Total: 1
第二次查询:Student{id=2, name='李四', age=18, score=90.0}

第一次查询之后关闭sqlsession并进行了插入操作,然后再次查询时候依然访问DB,因为缓存被刷新了;

刷新缓存过程:

Mybatis缓存底部实现:

一级缓存和二级缓存的底层都是通过map来实现的

  • 对应key : HashCode + statementId + SQL语句(对象的哈希值+mapper文件中执行SQL语句的标签的id+SQL语句)
  • 对应value : 查询结果本身

缓存刷新

  • 增删改操作会刷新一级缓存和二级缓存:刷新缓存实际上是将缓存中所有的Entry对象的value置为null。并未删除整个Entry对象,key仍保留;所以当发生刷新之后再次查询会显示本次命中缓存,但是却到DB中查询;

  • 增删改默认影响二级缓存的,但也可以让其不影响:只需要在对应增删改的statement中添加flushCache="false"

    <insert id="insertStudent" flushCache="false">
    insert into student (name,age,score) value (#{name},#{age},#{score})
    </insert>

所以综上,有两种情况是直接到DB中执行真正的查询:

  1. Map中根本不存在要查找的Entry,即没有要查找的key
  2. Map中存在要查找的key,但是它的value值为null

配置EHcache

EHcache是外置的二级缓存,Mybatis官方也提供了EHcache的缓存:https://github.com/mybatis/ehcache-cache>

配置:

  1. 首先导入两个jar:EHcache核心jar包,Mybatis和EHcache整合jar
<!-- https://mvnrepository.com/artifact/org.ehcache/ehcache -->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.8.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-ehcache -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.0</version>
</dependency>
  1. 在映射文件中添加:

    <cache type="org.mybatis.caches.ehcache.EhcacheCache">
    <property name="maxElementsInMemory" value="1000"/>
    </cache>

    其中中可以添加属性和它对应的值;

产生脏数据

脏数据产生:在关系型数据库中,经常使用多表联合查询,需要关联多个数据表才能得到查询结果,查询结果会被放在一个namespace的缓存中;涉及到其他表的增删改通常不在一个映射文件中,当其他的表的信息变更的时候,在当前的namespace缓存中查到的信息还是没有改变之前的数据。

避免脏数据的出现:这时需要用到参照缓存。当某几个表可以作为一个业务整体时,通常是让几个会关联的ER 表同时使用同一个二级缓存;当参照缓存更改之后就会重新到DB中查询;

<mapper namespace= "UserMapper" >
<cache-ref namespace= "RoleMapper" />
</mapper>

使用原则:

  1. 不能出现多个namespace操作一张表的情况。

    原因:二级缓存的作用范围只是单个namespace。

  2. 使用单表查询为主,尽量避免关联数据被改变;
    对于关联查询的数据表不要出现增删改等操作,容易出现脏数据。

  3. 查询操作较多,增删改较少的应用;

最新文章

  1. Delphi的文件操作
  2. DS_Store 是什么文件
  3. UIImage转换UIColor内存会莫名增大可以试试另一种方法
  4. UVa 10007 - Count the Trees(卡特兰数+阶乘+大数)
  5. resin安装和配置
  6. CString 的一些事
  7. java框架BeanUtils及路径问题练习
  8. MFC控件(7):Split Button
  9. 工具使用——MATLAB基本调试方法
  10. webpack+babel项目在IE下报Promise未定义错误引出的思考
  11. python设计模式第六天【原型模式】
  12. IOS 数据存储之 FMDB 详解
  13. 高性能Java RPC框架Dubbo与zookeeper的使用
  14. Everything实用技巧随手记
  15. POJ.1379.Run Away(模拟退火)
  16. SuperMap开发入门1——资源下载
  17. 【Android】3.6 地图基本控制方法
  18. ContentNegotiatingViewResolver多种输出格式实例: json/jsp/xml/xls/pdf
  19. Git学习笔记-----下载GitHub上某个分支的代码
  20. iOS Png Crush 错误

热门文章

  1. mysql主从配置步骤
  2. Unity整合TortoiseSVN
  3. 201871010123-吴丽丽 《面向对象程序设计(Java)》第十四周学习总结
  4. 201871010108-高文利《面向对象程序设计(java)》第十二周学习总结
  5. 怎么在虚拟机下的Linux系统安装数据库
  6. .NET开发中 springMVC+NHibernate注入失败的几个常见错误
  7. 记一次排错经历,requests和fake_useragent
  8. Bootstrap-table实现动态合并相同行
  9. Web协议详解与抓包实战:HTTP1协议-HTTP 响应行(3)
  10. 关于Windows自动化卸载软件的思路