MyBatis一级缓存原理解析

MyBatis是一个简单,小巧但功能很是强大的ORM开源框架,它的功能强大也体如今它的缓存机制上。MyBatis提供了一级缓存、二级缓存 这两个缓存机制,可以很好地处理和维护缓存,以提升系统的性能。本文将介绍MyBatis的一级缓存,并深刻源码解析MyBatis一级缓存的实现原理。java

什么是一级缓存?sql

若是想学习Java工程化、高性能及分布式、深刻浅出。微服务、Spring,MyBatis,Netty源码分析的朋友能够加个人Java高级交流:787707172,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给你们。数据库

每当咱们使用MyBatis开启一次和数据库的会话,MyBatis会建立出一个SqlSession对象表示一次数据库会话。缓存

在对数据库的一次会话中,咱们有可能会反复地执行彻底相同的查询语句,若是不采起一些措施的话,每一次查询都会查询一次数据库,而咱们在极短的时间内作了彻底相同的查询,那么它们的结果极有可能彻底相同,因为查询一次数据库的代价很大,这有可能形成很大的资源浪费。架构

为了解决这一问题,减小资源的浪费,MyBatis会在表示会话的SqlSession对象中创建一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候,若是判断先前有个彻底同样的查询,会直接从缓存中直接将结果取出,返回给用户,不须要再进行一次数据库查询了。框架

以下图所示,MyBatis会在一次会话的表示(一个SqlSession对象)中建立一个本地缓存(local cache),对于每一次查询,都会尝试根据查询的条件去本地缓存中查找是否在缓存中,若是在缓存中,就直接从缓存中取出,而后返回给用户;不然,从数据库读取数据,将查询结果存入缓存并返回给用户。分布式

MyBatis一级缓存原理解析

 

对于会话(Session)级别的数据缓存,咱们称之为一级数据缓存,简称一级缓存。微服务

一级缓存的实现原理源码分析

若是想学习Java工程化、高性能及分布式、深刻浅出。微服务、Spring,MyBatis,Netty源码分析的朋友能够加个人Java高级交流:787707172,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给你们。性能

因为MyBatis使用SqlSession对象表示一次数据库的会话,那么,对于会话级别的一级缓存也应该是在SqlSession中控制的。

实际上, MyBatis只是一个MyBatis对外的接口,SqlSession将它的工做交给了Executor执行器这个角色来完成,负责完成对数据库的各类操做。当建立了一个SqlSession对象时,MyBatis会为这个SqlSession对象建立一个新的Executor执行器,而缓存信息就被维护在这个Executor执行器中,MyBatis将缓存和对缓存相关的操做封装成了Cache接口中。SqlSession、Executor、Cache之间的关系以下列类图所示:

MyBatis一级缓存原理解析

 

如上述的类图所示,Executor接口的实现类BaseExecutor中拥有一个Cache接口的实现类PerpetualCache,则对于BaseExecutor对象而言,它将使用PerpetualCache对象维护缓存。

因为Session级别的一级缓存实际上就是使用PerpetualCache维护的,那么PerpetualCache是怎样实现的呢?

PerpetualCache实现原理其实很简单,其内部就是经过一个简单的HashMap<k,v> 来实现的,没有其余的任何限制。以下是PerpetualCache的实现代码:

public class PerpetualCache implements Cache {
 
 private String id;
 
 private Map<Object, Object> cache = new HashMap<Object, Object>();
 
 public PerpetualCache(String id) {
 this.id = id;
 }
 
 public String getId() {
 return id;
 }
 
 public int getSize() {
 return cache.size();
 }
 
 public void putObject(Object key, Object value) {
 cache.put(key, value);
 }
 
 public Object getObject(Object key) {
 return cache.get(key);
 }
 
 public Object removeObject(Object key) {
 return cache.remove(key);
 }
 
 public void clear() {
 cache.clear();
 }
 
 public ReadWriteLock getReadWriteLock() {
 return null;
 }
 
 public boolean equals(Object o) {
 if (getId() == null) throw new CacheException("Cache instances require an ID.");
 if (this == o) return true;
 if (!(o instanceof Cache)) return false;
 
 Cache otherCache = (Cache) o;
 return getId().equals(otherCache.getId());
 }
 
 public int hashCode() {
 if (getId() == null) throw new CacheException("Cache instances require an ID.");
 return getId().hashCode();
 }
 
}

一级缓存的生命周期有多长?

a. MyBatis在开启一个数据库会话时,会 建立一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象,Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。

b. 若是SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用;

c. 若是SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,可是该对象仍可以使用;

d.SqlSession中执行了任何一个update操做(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,可是该对象能够继续使用;

MyBatis一级缓存原理解析

 

CacheKey的定义

咱们知道,Cache最核心的实现其实就是一个Map,将本次查询使用的特征值做为key,将查询结果做为value存储到Map中。

如今最核心的问题出现了:怎样来肯定一次查询的特征值?

换句话说就是:怎样判断某两次查询是彻底相同的查询?

也能够这样说:如何肯定Cache中的key值?

MyBatis认为,对于两次查询,若是如下条件都彻底同样,那么就认为它们是彻底相同的两次查询:

  1. 传入的 statementId
  2. 查询时要求的结果集中的结果范围 (结果的范围经过rowBounds.offset和rowBounds.limit表示);
  3. 此次查询所产生的最终要传递给JDBC java.sql.Preparedstatement的Sql语句字符串(boundSql.getSql() )
  4. 传递给java.sql.Statement要设置的参数值

综上所述,CacheKey由如下条件决定:statementId + rowBounds + 传递给JDBC的SQL + 传递给JDBC的参数值

欢迎工做一到八年的Java工程师朋友们加入Java高级交流:787707172

本群提供免费的学习指导 架构资料 以及免费的解答

不懂得问题均可以在本群提出来 以后还会有直播平台和讲师直接交流噢