关于mybatis里面的一级缓存:
mybatis里面的一级缓存和二级缓存实际上和hibernate里面的差别不大。
一级缓存其实通俗地来讲就是,在sqlsession里面创建一个本地缓存,然后第二次进行相同的查询时候,就不会到数据库里面进行查找。
关于一级缓存我们不得不提及的内容就是这个类了:
PerpetualCache
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.apache.ibatis.cache.impl; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReadWriteLock; import org.apache.ibatis.cache.Cache; import org.apache.ibatis.cache.CacheException; public class PerpetualCache implements Cache { private final String id; private Map<Object, Object> cache = new HashMap(); public PerpetualCache(String id) { this.id = id; } public String getId() { return this.id; } public int getSize() { return this.cache.size(); } public void putObject(Object key, Object value) { this.cache.put(key, value); } public Object getObject(Object key) { return this.cache.get(key); } public Object removeObject(Object key) { return this.cache.remove(key); } public void clear() { this.cache.clear(); } public ReadWriteLock getReadWriteLock() { return null; } public boolean equals(Object o) { if (this.getId() == null) { throw new CacheException("Cache instances require an ID."); } else if (this == o) { return true; } else if (!(o instanceof Cache)) { return false; } else { Cache otherCache = (Cache)o; return this.getId().equals(otherCache.getId()); } } public int hashCode() { if (this.getId() == null) { throw new CacheException("Cache instances require an ID."); } else { return this.getId().hashCode(); } } } 复制代码
是的,这个类里面主要的核心就是一个hashmap。
举个例子来说,当我们查询一遍数据库内容的时候,它会存储相应的内容到cache
这个哈希表中,截图如下所示:
假若再要深入探索:
可以发现相应的一个内容:
BaseExecutor.queryFromDatabase()函数,这个函数是一个在debug中非常实用的一个函数。
假若当我们需要查看mybatis的sql语句时候,而mybatis有没有采用打印sql的方式在控制台输出,那么我们就可以使用断点停留的方式来查看sql内容了。
现在我们来进行一次小实验:
假设我们进行一次sql的查询,就会发现以下内容:
首先是第一次查询:
会往perpetualCache里面存放相应的数据缓存信息:
在一次次的翻看源码的时候,渐渐发现mybatis里面有个非常有趣的东西,叫做
mappedStatements,这个类里面主要用于处理xml映射的内容,里面的id正好是我们xml里面定义的内容:
通过对于源码的再次深入,我们会发现里面的BaseExecutor.createCacheKey
类,在这个类里面有创建缓存的核心代码部分内容:
在经过多次的断点查询之后,终于终于在BaseExecutor里面的public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException 这个函数中看到了关键点:
第一次查询的时候会将查询的内容存入localCache里面
第二次查询的时候会直接从里面取出
一级缓存使用sqlsession作为单位划分的。
每次查询会先去缓存中找,如果找不到,再去数据库查询,然后把结果写到缓存中。Mybatis的内部缓存使用一个HashMap,key为hashcode+statementId+sql语句。Value为查询出来的结果集映射成的java对象。
SqlSession执行insert、update、delete等操作commit后会清空该SQLSession缓存。
关于二级缓存
在二级缓存里面,我们需要开启以下的配置内容:
在setting里面开启二级缓存的作用域是针对于全局性的。
如果只是想要单独对于某一个sql的二级缓存查询开启,我们可以在相应的select标签里面设置:
同时还要添加一个cache配置:
查询的model需要进行序列化操作:
当我们开启了二级缓存以后,通过断点的方式进入到源码里面查看就会发现相应的内容:
之前没有开启二级缓存的时候,这里的cache是个null值,但是由于xml里的配置开启了二级缓存,所以这个时候程序会用putobject来进行一个缓存存储。
第二次查询相同内容的时候,我们通过断点可以看到源码里面的内容:
由于第一次进行sql查询的时候,mybatis做了相应的操作设置:
this.tcm.putObject(cache, key, list); 复制代码
Mybatis的key为:
Value则是查询出来的内容和信息,因此这里面的list不为null,那么也就不会进入对于数据库的查询了。开启了二级缓存之后,就不需要进行对数据库的查询了。