MyBatis是一个简单,小巧但功能很是强大的ORM开源框架,它的功能强大也体如今它的缓存机制上。MyBatis提供了一级缓存、二级缓存 这两个缓存机制,可以很好地处理和维护缓存,以提升系统的性能。本文将介绍MyBatis的一级缓存,并深刻源码解析MyBatis一级缓存的实现原理。java
什么是一级缓存?sql
若是想学习Java工程化、高性能及分布式、深刻浅出。微服务、Spring,MyBatis,Netty源码分析的朋友能够加个人Java高级交流:787707172,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给你们。数据库
每当咱们使用MyBatis开启一次和数据库的会话,MyBatis会建立出一个SqlSession对象表示一次数据库会话。缓存
在对数据库的一次会话中,咱们有可能会反复地执行彻底相同的查询语句,若是不采起一些措施的话,每一次查询都会查询一次数据库,而咱们在极短的时间内作了彻底相同的查询,那么它们的结果极有可能彻底相同,因为查询一次数据库的代价很大,这有可能形成很大的资源浪费。架构
为了解决这一问题,减小资源的浪费,MyBatis会在表示会话的SqlSession对象中创建一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候,若是判断先前有个彻底同样的查询,会直接从缓存中直接将结果取出,返回给用户,不须要再进行一次数据库查询了。框架
以下图所示,MyBatis会在一次会话的表示(一个SqlSession对象)中建立一个本地缓存(local cache),对于每一次查询,都会尝试根据查询的条件去本地缓存中查找是否在缓存中,若是在缓存中,就直接从缓存中取出,而后返回给用户;不然,从数据库读取数据,将查询结果存入缓存并返回给用户。分布式
对于会话(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之间的关系以下列类图所示:
如上述的类图所示,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对象的数据,可是该对象能够继续使用;
CacheKey的定义
咱们知道,Cache最核心的实现其实就是一个Map,将本次查询使用的特征值做为key,将查询结果做为value存储到Map中。
如今最核心的问题出现了:怎样来肯定一次查询的特征值?
换句话说就是:怎样判断某两次查询是彻底相同的查询?
也能够这样说:如何肯定Cache中的key值?
MyBatis认为,对于两次查询,若是如下条件都彻底同样,那么就认为它们是彻底相同的两次查询:
综上所述,CacheKey由如下条件决定:statementId + rowBounds + 传递给JDBC的SQL + 传递给JDBC的参数值
欢迎工做一到八年的Java工程师朋友们加入Java高级交流:787707172
本群提供免费的学习指导 架构资料 以及免费的解答
不懂得问题均可以在本群提出来 以后还会有直播平台和讲师直接交流噢