缓存在互联网系统中是很是重要的, 其主要做用是将数据保存到内存中, 当用户查询数据 时, 优先从缓存容器中获取数据,而不是频繁地从数据库中查询数据,从而提升查询性能。目 前流行的缓存服务器有MongoDB 、Redis 、Ehcache 等,不一样的缓存服务器有不一样的应用场景, 不存在孰优孰劣。 MyBatis 提供一级缓存和二级缓存的机制。一级缓存是SqlSession 级别的缓存, 在操做数据 库时, 每一个SqlSession 类的实体对象中都有一个HashMap 数据结构能够用来缓存数据,不一样的 SqlSession 类的实例对象缓存的HashMap 数据结构互不影响。二级缓存是Mapper 级别的缓存, 数据库
My Batis 的缓存模式如图所示:缓存
MyBatis 的一级缓存是SqI Session 级别的缓存, 在操做数据库时须要构造Sq!Session 对象。 每一个SqISession 对象中都有一个HashMap 对象用于缓存数据,不一样的SqlSession 之间的缓存数 据区域互不影响。 MyBatis的一级缓存做用域是SqlSession 范围的,在参数和SQL 彻底同样的状况下,使用 同一个SqlSession 对象调用同一个Mapper 方法,每每只执行一次SQL ,由于MyBatis 会将数据 放在缓存中,下次查询的时候,若是没有声明须要刷新缓存而且缓存没有超时, SqlSession 都 只会取出当前缓存的数据,而不会再次发送SQL 到数据库中。须要注意的是,若是SqlSession 执行了DML 操做( insert 、update 和delete ),并提交到数据库, MyBatis 会清空SqlSession 中 的一级缓存,这样作的目的是为了保证缓存中存储的是最新的信息, 以免出现脏读现象。 MyBatis 的缓存机制是基于id 进行缓存的, MyBatis 使用HashMap 缓存数据时,使用对象的id 做为key,而对象则做为value 保存。服务器
上述代码中,经过@Resource 注解注入SqlSessionFactoryBean 对象, SqlSessionFactoryBean 对象在applicationContext.xml 配置文件中己配置, 具体代码以下:session
经过SqlSessionFactory 工厂获取SqlSession 对象, 经过SqlSession 对象的getMapper()方法 获取AyUserDao 接口对象, 并执行AyUserDao 接口对象的findByld()方法。AyUserDao 和 AyUserMapper.xml 的代码以下所示。数据结构
执行测试用例testsessionCach e(),控制台打印相关的信息,具体如图9-2 所示。mybatis
由图9 -2 中控制台打印的信息能够看出,第一次查询和第二次查询,查询SQL 的日志只输 出一遍, 这就说明了第二次查询的数据不是从数据库查询出来的,是从一级缓存中获取的。app
MyBatis 在开启一个Session 会话时, 会建立一个新的SqISession 对象, 每一个SqlSession 对 象会建立一个新的Executor 对象, Executor 对象中持有一个新的PerpetualCache 对象: 当会话结 束时, SqISession 对象及其内部的Executor 对象还有PerpetualCache 对象也会一并释放掉,即: (1 )若是SqlSession 调用了close()方法,会释放掉一级缓存PerpetualCache 对象, 一级缓 存不可用。 (2 )若是SqlSession 调用了clearCache(), 会清空PerpetualCache 对象中的数据,可是该对 象仍可以使用。 ( 3 ) SqlSession 中执行任何一个DDL 操做( update 、delete 、insert) ,都会清空 PerpetualCache 对象的数据,可是该对象能够继续使用。性能
二级缓存是Mapper 级别的缓存。使用二级缓存时,多个SqlSession 使用同一个Mapper ( namespace )的SQL 语句操做数据库,获得的数据会存在二级缓存区域,二级缓存一样是使用 HashMap 进行数据存储。二级缓存比一级缓存做用域范围更大,多个SqISession 能够共用二级 缓存,二级缓存是跨SqISession 的。当某个SqISession 类的实例对象执行了增、删、改等操做 时, Mapper 实例会清空二级缓存。MyBatis 默认没有开启二级缓存,须要在配置中开启二级缓 存。开启二级缓存的步骤以下。测试
首先,在applicationContext.xml 配置文件中添加以下配置:spa
上面配置信息中最重要的就是指定MyBatis 配置文件的位置:
其次,在src\main\resources 目录下添加配置文件mybatis-config . xml , 具体代码以下:
最后,因为二级缓存是Mapper 级别的,还要在须要开启二级缓存的具体mapper.xml 文件 中开启二级缓存,方法很简单,只须要在mappe川ml 文件中添加一个cache 标签既可,具体代 码以下所示:
cache 标签有不少属性,经常使用的属性如表9- 1 所示:
AyUserDao 和AyUserMapper.xml 代码以下:
当开启MyBatis 二级缓存后,执行测试用例testSessionCache(), 控制台打印相关的信息, 具体如图9-4 所示。
由图9-4 可知, 第一次查询数据时, 获取链接、编译SQL、加载了数据库中的数据。而第 二次查询数据以前,进行了update 操做,至关于进行commit 操做,也就是说会清空一级缓存来 保证数据的最新状态。可是开启了二级缓存,在第二次查询时,会从二级缓存中获取数据。 这里须要注意的是,若是在select 标签中设置“ userCache = false” 能够禁用当前select 语句 的二级缓存,具体代码以下:
这里简单总结一下二级缓存的特色:
还须要注意的是,使用二级缓存须要特别谨慎,有时候不一样的namespace 下的SQL 配置可 能缓存了相同的数据。例如AyUserMapper.xml 中有不少查询缓存了用户数据,其余的 XXXMapper且nl 中有针对用户表进行单表操做, 也缓存了用户数据,若是在AyUserMapper.xml 中作了刷新缓存的操做,在XXXMapper.xml 中的缓存数据仍然有效, 这样在查询数据时可能会 出现脏数据。因此使用MyBatis 的二级缓存时,要根据具体的业务状况,谨慎使用。
###cache-ref共享缓存
MyBatis 并非整个Application 只有一个Cache 缓存对象,它将缓存划分的更细, 也就是 Mapper 级别的,即每个Mapper 均可以拥有一个Cache 对象,具体以下:
( 1 ) 为每个Mapper 分配一个Cache 缓存对象(使用<cache>节点配置) 。 ( 2 )多个Mapper 共用一个Cache 缓存对象(使用<cache-ref>节点配置) 。
若是想让多个Mapper 共用一个Cache ,可使用<cache-ref namespace="与节点,来指定这 个Mapper 共享哪个Mapper 的Cache 缓存。具体如图9-5 所示。
如图9- 6 所示, 一个SqlSession 对象中建立一个本地缓存( local cache ),对于每次查询, 都会根据查询条件去一级缓存中查找,若是缓存中存在数据,就直接从缓存中取出,而后返回 给用户:不然,从数据库读取数据,将查询结果存入缓存并返回给用户。
SqlSession 将它的工做交给了Executor 执行器这个角色来完成,负责完成对数据库的各类 操做。当建立一个SqlSession 对象时, MyBatis 会为这个SqlSession 对象建立一个新的Executor 执行器,而缓存信息就被维护在这个Executor 执行器中, MyBatis 将缓存和对缓存相关的操做 封装成了Cache 接口。
如图9 - 7 所示, MyBatis 的二级缓存机制的关键是使用Executor 对象。当开启SqlSession 会 话时, 一个Session 对象使用一个Executor 对象来完成会话操做。若是用户配置了 "cacheEnabled=true”, 那么MyBatis 在为SqlSession 对象建立Executor 对象时,会对Executor 对 象加上一个装饰者: CachingExecutor ,这时SqISession 使用CachingExecutor 对象来完成操做请 求。CachingExecutor 对于查询请求,会先判断该查询请求在二级缓存中是否有缓存结果,若是 有查询结果, 则直接返回缓存结果:若是缓存中没有, 再交给真正的Executor 对象来完成查询 操做,以后CachingExecutor 会将真正Executor 返回的查询结果放置到缓存中,而后再返回给 用户。