前言

  使用缓存可以使应用更快的获取数据,避免频繁的数据库交互,尤其是在查询越多、缓存命中率越高的情况下,使用缓存的作用就越明显。MyBatis作为持久化框架,提供了非常强大的查询缓存特性,可以非常方便地配置和定制使用。一般提到MyBatis缓存的时候,都是指二级缓存,一级缓存默认会启用,并且不能控制,因此很少会提到。不过,知道一级缓存的存在可以避免产生一些难以发现的错误。

一级缓存
SqlSession sqlSession = sessionFactory.openSession();
try{
TUserMapper tUserMapper = sqlSession.getMapper(TUserMapper.class);
TUser result1 = tUserMapper.selectByPrimaryKey(11L);
System.out.println("第一次查询:" + result1.getuName());
result1.setuName("cache");
TUser result2 = tUserMapper.selectByPrimaryKey(new Long(11));
System.out.println("第二次查询:" + result2.getuName());
tUserMapper.deleteByPrimaryKey(8L);
TUser result3 = tUserMapper.selectByPrimaryKey(new Long(11));
System.out.println("第三次查询:" + result3.getuName());
}finally {
sqlSession.close();
}

查询结果:

第一次查询:mh
第二次查询:cache
第三次查询:mh

说明:
  第一次执行selectByPrimaryKey方法查询时,真正执行了数据库查询,得到了result1的结果。第二次查询获取result2时,使用了MyBatis的一级缓存,没有查询数据库,直接从缓存中获取了结果。所以result1和result2是一个对象。
  MyBatis的一级缓存存在于SqlSession的生命周期中,在同一个SqlSession中查询时,MyBatis会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存入一个Map对象中。如果同一个SqlSession中执行的方法和参数完全一致,那么通过算法会生成相同的键值,当Map缓存对象中已经存在该键值时,则会返回缓存中的对象。如果我们不想让selectByPrimaryKey使用一级缓存,可以对方法做如下修改:

<select id="selectByPrimaryKey" flushCache="true" resultMap="BaseResultMap">
select id, u_name, u_password
from t_user
where
id = #{id,jdbcType=BIGINT}
</select>

  设置了flushCache属性为true。这种方式表示在查询前清空一级缓存,会影响其他的查询,所以避免这么使用。
  另外,第三次查询时又真正执行了数据库查询,是因为前面执行了delete操作。任何的Insert、update、delete操作都会清空一级缓存。

二级缓存
  MyBatis的二级缓存非常强大,它不同于一级缓存只存在于SqlSession的生命周期中,而是可以理解为存在于SqlSessionFactory的生命周期中。

配置二级缓存
  MyBatis的二级缓存是默认开启的。也可以在mybatis-config.xml中显式声明,配置如下:

<settings>
<setting name="cacheEnabled" value="true"/>
</settings>

  如果将cacheEnabled设为false,即使有后面的二级缓存配置,也不会生效。
  MyBatis的二级缓存是和命名空间绑定的,即二级缓存需要配置在Mapper.xml映射文件中,或者配置在Mapper.java接口中。在映射文件中,命名空间就是XML根节点mapper的namespace属性。在接口中,命名空间就是接口的全限定名称。

Mapper.xml中配置二级缓存
  在保证二级缓存的全局配置开启的情况下,给TUserMapper.xml开启二级缓存只需要在其中添加<cache/>元素即可,添加后的TUserMapper.xml如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"&gt;
<mapper namespace="cn.jujianfei.demo.dao.TUserMapper">
<cache/>
..........
</mapper>

在Mapper接口中配置二级缓存
  当使用注解配合接口开发时,开启二级缓存就需要在接口上配置了。具体如下:

@CacheNamespace
public interface TUserMapper {
//接口方法
}

使用二级缓存
//TUserMapper.xml
<cache readOnly="false"></cache>

  cache的属性readOnly设置为flase表示此二级缓存为可读写缓存,可以使用SerializedCache序列化缓存。这个缓存类要求所有被序列化的对象必须实现Serializable(java.io.Serializable)接口,所以还需要修改TUser对象,代码如下:

public class TUser implements Serializable {
......
}

测试类
SqlSession sqlSession = sessionFactory.openSession();
try{
TUserMapper tUserMapper = sqlSession.getMapper(TUserMapper.class);
TUser result1 = tUserMapper.selectByPrimaryKey(11L);
System.out.println("第一次查询:" + result1.getuName());
result1.setuName("cache");
TUser result2 = tUserMapper.selectByPrimaryKey(new Long(11));
System.out.println("第二次查询:" + result2.getuName());
Assert.assertEquals(result1,result2);
}finally {
sqlSession.close();
}

sqlSession = sessionFactory.openSession();
try{
TUserMapper tUserMapper = sqlSession.getMapper(TUserMapper.class);
TUser result3 = tUserMapper.selectByPrimaryKey(new Long(11));
System.out.println("第三次查询:" + result3.getuName());
TUser result4 = tUserMapper.selectByPrimaryKey(new Long(11));
System.out.println("第四次查询:" + result4.getuName());
Assert.assertNotEquals(result4,result3);
}finally {
sqlSession.close();
}

打印结果:

第一次查询:mh
第二次查询:cache
第三次查询:cache
第四次查询:cache

  由于配置的是可读写的缓存,而MyBatis使用SerializedCache序列化缓存来实现可读写缓存类,并通过序列化和反序列化来保证通过缓存获取数据时,得到的是一个新的实例。如果配置为只读缓存,MyBatis就会使用Map来存储缓存值,这种情况下,从缓存中获取的对象就是同一个实例。
  第一次查询,是从数据库查询;第二次查询是从一级缓存查询,所以第一次和第二次查询是同一个实例。当调用close方法关闭SqlSession时,SqlSession才会保存查询数据到二级缓存中。在这之后二级缓存才有了数据。第三次查询就是从二级缓存中获取数据了。第四次查询也是从二级缓存中获取数据。result3和result4都是反序列化得到的结果,所以它们不是相同的实例。在这一部分,这两个实例是读写安全的,其属性不会互相影响。
  实际上,示例代码并不安全。result1.setuName("cache");这里修改result1的属性值后,按照常理应该更新数据,更新后会清空一、二级缓存,但是没有。导致后续查询结果都是cache。所有要想安全使用,需要避免毫无意义的修改。这样就可以避免人为产生的脏数据,避免缓存和数据库的数据不一致。
  MyBatis默认提供的缓存实现时基于Map实现的内存缓存,已经可以满足基本的应用。但是当需要缓存大量的数据时,不能仅仅通过提高内存来使用MyBatis的二级缓存,还可以选择一些类似EhCache的缓存框架或Redis缓存数据库等工具来保存MyBatis的二级缓存数据。

脏数据的产生和避免
  二级缓存虽然能提高应用效率,减轻数据库服务器的压力,但是如果使用不当,很容易产生脏数据,影响使用效果。

产生

  MyBatis的二级缓存是和命名空间绑定的,所以通常情况下每一个Mapper映射文件都拥有自己的二级缓存,不同Mapper的二级缓存互不影响。在常见的数据库操作中,多表联查非常常见,而多表联查肯定会将该查询放到某个命名空间下的映射文件中,这样一个多表的查询就会缓存在该命名空间的二级缓存中。涉及到这些表的增、删、改操作通常不在一个映射文件中,它们的命名空间不同,因此当有数据变化时,多表查询的缓存未必会被清空,这种情况下就会产生脏数据。

避免

  该如何避免脏数据的出现呢?这时就需要用到参照缓存了。当某几个表可以作为一个业务整体时,通常是让几个会关联的ER表同时使用同一个二级缓存,这样就能解决脏数据问题。配置如下:


<mapper namespace="cn.jujianfei.demo.dao.TUserMapper"><cache readOnly="false"></cache>......
</mapper><mapper namespace="cn.jujianfei.demo.dao.UserMapper"><cache-ref namespace="cn.jujianfei.demo.dao.TUserMapper"/>......
</mapper>

  虽然这样可以解决脏数据的问题,但是并不是所有的关联查询都可以这么解决,如果有几十个表甚至所有表都以不同的关联关系存在于各自的映射文件中时,使用参照缓存显然没有意义。

二级缓存适用的场景
以查询为主的应用中,只有尽可能少的增、删、改操作。
绝大多数以单表操作存在时,由于很少存在互相关联的情况,因此不会出现脏数据。
可以按业务划分对表进行分组时,如关联的表比较少,可以通过参照缓存进行配置。
除了推荐使用的情况,如果脏读对系统没有影响,也可以考虑使用。在无法保证数据不出现脏读的情况下,建议在业务层使用可控制的缓存代替二级缓存。

总结语

  更新操作会清空一、二级缓存。

转载于:https://blog.51cto.com/13792737/2151919

【MyBatis】缓存配置相关推荐

  1. 第七章 MyBatis 缓存配置

    第七章 MyBatis 缓存配置 一. MyBatis的一级缓存 二. MyBatis的二级缓存 1. 配置二级缓存 1.1 Mapper映射文件配置二级缓存 1.2 Mapper接口配置二级缓存 2 ...

  2. Mybatis缓存配置

    pom文件配置: <dependency><groupId>org.mybatis</groupId><artifactId>mybatis</a ...

  3. SpringMVC+MyBatis+Redis开启二级缓存配置

    2019独角兽企业重金招聘Python工程师标准>>> spring-mybatis.xml配置文件<!-- spring和MyBatis完美整合,不需要mybatis的配置映 ...

  4. SpringMVC+Mybatis+MySQL配置Redis缓存

    SpringMVC+Mybatis+MySQL配置Redis缓存 1.准备环境: SpringMVC:spring-framework-4.3.5.RELEASE-dist Mybatis:3.4.2 ...

  5. redis springmvc mysql_SpringMVC + MyBatis + Mysql + Redis(作为二级缓存) 配置

    项目环境: 在SpringMVC + MyBatis + Mysql.Redis部署在Linux虚拟机. 1.整体思路 参考Ehcache实现MyBatis二级缓存代码(Maven引用对应jar查阅) ...

  6. SpringMVC + MyBatis + MySQL + Redis(作为二级缓存) 配置

    前言 Mybatis 有二级缓存,为什么还要用Redis? mybais一级缓存作用域是session,session commit之后缓存就失效了. mybais二级缓存作用域是sessionfac ...

  7. mysql redis缓存配置_SpringMVC + MyBatis + Mysql + Redis(作为二级缓存) 配置

    项目环境: 在SpringMVC + MyBatis + MySQL.Redis部署在Linux虚拟机. 1.整体思路 参考Ehcache实现MyBatis二级缓存代码(Maven引用对应jar查阅) ...

  8. SpringMVC +Spring + MyBatis + Mysql + Redis(作为二级缓存) 配置

    转载:http://blog.csdn.net/xiadi934/article/details/50786293 项目环境: 在SpringMVC +Spring + MyBatis + MySQL ...

  9. java | (三十一)MyBatis(1)配置、映射、缓存

    目录 配置MyBatis 基本配置 采用注解的方式映射sql 主要API生命周期 别名typeAliases environments配置 mappers SQL映射 select(带参数) sele ...

  10. MyBatis——XML配置解析

    目录 1. 环境配置(environments) 2. 属性(properties) 3. 类型别名(typeAliases) 方式一:通过typeAlias起别名 方式二:通过package起别名 ...

最新文章

  1. python 字典排序 最大键_Python中的列表、元祖、字典、集合操作大全
  2. MySQL中INDEX,PRIMARY,UNIQUE,FULLTEXT之间的区别?
  3. Android中对Log日志文件的分析
  4. occam‘s razor
  5. 字符串池化,减少了三分之一的内存占用
  6. eclipse 跑maven命令_eclipse中运行maven命令没有反应,console也不出现信息
  7. 获取焦点改变输入框背景色
  8. [转]Nginx的负载均衡方式
  9. linux构建基于mac的vlan,通过CLI配置交换机的基于MAC的VLAN组
  10. html5 canvas修改颜色,html5 canvas 笔记二(添加样式和颜色)
  11. Microsoft Visual Studio使用NodeJS
  12. JavaScript中var关键字的使用详解
  13. 为什么年龄大了近视还增加_都是做近视手术,为什么价格区别这么大?
  14. c语言有趣源代码,分享一段有趣的小代码
  15. 一个简单的姓名生成器
  16. 关于java语言的基本介绍
  17. PHP在线工具箱源码站长引流+在线工具箱源码+多款有趣的在线工具+一键安装
  18. 中山大学非全日制计算机考研,中山大学社会工作非全日制考研经验贴
  19. 洛谷 P1538 迎春舞会之数字舞蹈
  20. Keil的AC6与AC5中文手册

热门文章

  1. android数码管字体,matplotlib绘图时显示额外的“figure”浮窗
  2. Python实现LBP算法
  3. 【Excel从头开始】-3 从网页导出数据到Excel
  4. 北京化工大学研究生学堂在线矩阵论及其应用答案
  5. php 伪静态 获取当前页面路径_php url伪静态过程详解
  6. AR Camera开发记录(一) -- Rajawali的使用
  7. windows上vscode 安装Fortran-language-server
  8. [CM311-1A]-全网最全 Android 用户管理及用户应用权限
  9. Windows内核学习------双机调试的安装(物理机win10,虚拟机win7,虚拟机软件vmware)
  10. flash mx拖拽实例_Flash MX 2004动态串面板