MyBatis一级缓存原了解析
来源:匠丶     阅读:585
织梦二开
发布于 2018-12-05 22:58
查看主页

Batis是一个简单,小巧但功能非常强大的ORM开源框架,它的功能强大也表现在它的缓存机制上。MyBatis提供了一级缓存、二级缓存 这两个缓存机制,能够很好地解决和维护缓存,以提高系统的性能。本文将详情MyBatis的一级缓存,并深入源码解析MyBatis一级缓存的实现原理。

什么是一级缓存?

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

在对数据库的一次会话中,我们有可能会反复地执行完全相同的查询语句,假如不采取少量措施的话,每一次查询都会查询一次数据库,而我们在极短的时间内做了完全相同的查询,那么它们的结果极有可能完全相同,因为查询一次数据库的代价很大,这有可能造成很大的资源白费。

为理解决这一问题,减少资源的白费,MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候,假如判断先前有个完全一样的查询,会直接从缓存中直接将结果取出,返回给客户,不需要再进行一次数据库查询了。

如下图所示,MyBatis会在一次会话的表示----一个SqlSession对象中创立一个本地缓存(local cache),对于每一次查询,都会尝试根据查询的条件去本地缓存中查找能否在缓存中,假如在缓存中,就直接从缓存中取出,而后返回给客户;否则,从数据库读取数据,将查询结果存入缓存并返回给客户。



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

一级缓存的实现原理

因为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认为,对于两次查询,假如以下条件都完全一样,那么就认为它们是完全相同的两次查询:

  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的参数值

免责声明:本文为用户发表,不代表网站立场,仅供参考,不构成引导等用途。 系统环境 服务器应用
相关推荐
高并发场景下的限流和降级
Node.js开发者必需理解的4个JS要点
Tomcat中三个端口的作用
Node.js开发者必需理解的4个JS要点
H3C 企业防火墙实操系列(3、NAT策略配置)
首页
搜索
订单
购物车
我的