1. 介绍

缓存就是内存中的数据,常常来自对数据库查询结果的保存,使用缓存可以避免与数据库频繁的交互,提高响应速度。mybaits的缓存分为一级缓存和二级缓存。
(1)一级缓存:是sqlsession级别的缓存,在操作数据库时需要构造sqlsession对象,在对象中有一个数据结构 (HashMap)来存储缓存数据。不同的sqlsession之间的缓存域(HashMap)是互不干扰的。
(2)二级缓存:是mapper级别的缓存,多个sqlsession去操作同一个Mapper的sql语句,多个sqlsqssion可以共 用二级缓存,二级缓存是跨sqlsession的。
6. Mybatis缓存 - 图1

2. 一级缓存

2.1 测试一级缓存

  1. @Test
  2. public void firstLevelCache() {
  3. InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
  4. SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
  5. SqlSession sqlSession = build.openSession(true);
  6. UserDao userMapper = sqlSession.getMapper(UserDao.class);
  7. // 第一步:首先查询一级缓存,有则直接返回,没有则去数据库查询,同时将查询出来的结果放入一级 // 缓存
  8. User user1 = userMapper.findUserById(1);
  9. // 中间进行了增删改的操作并进行事务提交后,就会刷新一级缓存;或者可以用clearCache()手动刷新 // 缓存,避免出现脏读
  10. // 第二步:首先查询一级缓存,有则直接返回,没有则去数据库查询,同时将查询出来的结果放入一级 // 缓存
  11. User user2 = userMapper.findUserById(1);
  12. // 如果没有刷新缓存则为true,刷新了缓存为false
  13. system.out.println(user1 == user2)
  14. }

2.2 一级缓存原理与源码分析

  1. 问题分析
    1. 一级缓存到底是什么?(HashMap)
    2. 一级缓存什么时候被创建?(在Executor类的query方法中,如果cacheKey值在缓存中不存在则创建)
    3. 一级缓存的工作流程是怎样的?(查询时判断cacheKey存不存在,存在直接返回缓存数据,不存在则向数据库查询,得到的结果再存进一级缓存)
  2. 源码分析过程
    1. Sqlsession.clearCache():一级缓存是在sqlsession中的,所以就从sqlsession的类开始研究,找遍sqlsession的方法,只有一个clearCache()是和缓存相关的,所以就从clearCache方法入手。
    2. DefaultSqlsession.clearCache():选择SqlSession的实现类DefaultSqlsession,发现clearCache()方法的实现执行了executor.clearLocalCache()。
    3. Executor.clearLocalCache():点进该方法发现是一个接口,所以选择它的实现类BaseExecutor。
    4. BaseExecutor.clearLocalCache() :该方法调用的是localCache.clear(),继续看该方法。
    5. PrepetualCache.clear():该方法调用的是cache.clear(),点进cache发现是一个Map,所以cache.clear()实际上就是Map.clear()。由此可知,一级缓存的数据结构就是HashMap。

6. Mybatis缓存 - 图2

  1. Executor.createCacheKey():在Executor中找创建一级缓存的方法。
  2. BaseExecutor.createCacheKey():createCacheKey方法的实现,生成cacheKey,需要的参数分别为statementId,params(参数),boundSql(sql语句),rowBounds(分页)。
  3. 然后查找Executor中调用createCacheKey方法的地方,发现在query方法中,调用createCacheKey(ms, parameter, rowBounds, boundSql)创建了cacheKey,然后通过localCache.getObject(key)来判断该key存不存在,如果存在则直接返回缓存中的值,如果不存在,调用方法queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql),在该方法中,先向数据库查询,得到的结果存入一级缓存中localCache.putObject(key, list),此时,是一级缓存创建的时候。

    3. 二级缓存

    3.1 二级缓存介绍

  1. 二级缓存的原理跟一级缓存一样,第一次查询会把数据放入缓存中,第二次查询就会去缓存中取,但是一级缓存是基于sqlSesion,二级缓存是基于mapper文件的namespace的,使用同一个mapper的多个sqlSession共用一个二级缓存区域,如果两个mapper的namespace相同,即使是不同的mapper文件也共享二级缓存

6. Mybatis缓存 - 图3

3.2 如何使用二级缓存

  1. 开启二级缓存

和一级缓存的默认开启不同,二级缓存需要手动开启,首先在全局配置文件sqlMapperConfig.xml中加入如下代码:

  1. <!--开启二级缓存-->
  2. <settings>
  3. <setting name="cacheEnabled" value="true" />
  4. </settings>

其次在userMapper.xml文件开启缓存

  1. <!--开启二级缓存-->
  2. <cache></cache>
  1. 测试二级缓存的方法跟测试一级缓存差不多,只是把一个sqlSession变为多个sqlSession。二级缓存存储的不是对象本身,而是对象的数据,所以两次查询的对象地址是不一样的。

3.3 使用redis来实现二级缓存

  1. mybatis默认的二级缓存的实现类是PerpetualCache,这个实现类可以通过Mapper.xml中的标签来配置,例如,默认就是PerpetualCache,也可以去实现Cache接口来自定义缓存实现类。
  2. 在PerpetualCache类中可以看到mybatis默认的二级缓存底层数据结构也是HashMap。
  3. mybatis自带的二级缓存只能用于单服务器,如果要实现分布式缓存,就要整合其它的缓存实现,比如redis。
  4. mybatis整合redis

    1. pom文件

      1. <dependency>
      2. <groupId>org.mybatis.caches</groupId>
      3. <artifactId>mybatis-redis</artifactId>
      4. <version>1.0.0-beta2</version>
      5. </dependency>
    2. 配置文件 ```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">

  1. 3. redis.properties
  2. ```properties
  3. redis.host=127.0.0.1
  4. redis.port=6379
  5. redis.connectionTimeout=5000
  6. redis.password=
  7. redis.database=0