详解Mybatis的缓存

网友投稿 283 2022-11-06


详解Mybatis的缓存

Mybatis的缓存

mybatis是一个查询数据库的封装框架,主要是封装提供灵活的增删改sql,开发中,service层能够通过mybatis组件查询和修改数据库中表的数据;作为查询工具,mybatis有使用缓存,这里讲一下mybatis的缓存相关源码。

缓存

在计算机里面,任何信息都有源头,缓存一般指源头信息读取后,放在内存或者其他读取较快的地方,下次读取相同信息不去源头查询而是直接从内存(或者能快速存取的硬件)读取。这样可以减少硬件使用,提高读取速度。

mybatis也是这样,查询数据库的数据之后,mybatis可以把查询结果缓存到内存,下次查询如果查询语句相同,并且查询相关的表的数据没被修改过,就可以直接返回缓存中的结果,而不用去查询数据库的语句,有效节省了时间。

简单看一下mybatis一级缓存和二级缓存相关源码,学习使用

一级缓存

通过查看源码可知,一级缓存是绑定sqSsession中的,所以每次查询sqlSession不同就失效,相同的sqlSession可以使用一级缓存。

mybatis默认sqlsession:org.apache.ibatis.session.defaults.DefaultSqlSession

构造方法中传入executor(查询执行对象)

public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {

this.configuration = configuration;

this.executor = executor;

this.dirty = false;

this.autoCommit = autoCommit;

}

executor中携带一级缓存成员:

protected BaseExecutor(Configuration configuration, Transaction transaction) {

this.transaction = transaction;

this.deferredLoads = new ConcurrentLinkedQueue<>();

this.localCache = new PerpetualCache("LocalCache"); //默认一级缓存

this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");

this.closed = false;

this.configuration = configuration;

this.wrapper = this;

}

查询使用一级缓存逻辑

org.apache.ibatis.executor.BaseExecutor.query()

public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {

ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());

Lihttp://st list;

try {

queryStack++;

//localCache 一级缓存

list = resultHandler == null ? (List) localCache.getObject(key) : null;

//先从一级缓存中获取,key是通过sql语句生成

if (list != null) {

handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);

} else {

// 如果缓存中没有 才从数据库查询

list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

}

} finally {

queryStack--;

}

return list;

}

//从数据库读取数据

private List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {

List list;

localCache.putObject(key, EXECUTION_PLACEHOLDER);

try {

list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);

} finally {

localCache.removeObject(key);//将一级缓存清除

}

localCache.putObject(key, list);//返回查询结果之前,先放入一级缓存 刷新

if (ms.getStatementType() == StatementType.CALLABLE) {

localOutputParameterCache.putObject(key, parameter);

}

return list;

}

二级缓存

二级缓存mapper中的,默认是开启的,但需要在映射文件mapper.xml中添加标签

配置false可以关闭二级缓存

二级缓存的解析

org.apache.ibatis.builder.xml.XMLMapperBuilder

private void configurationElement(XNode context) {

try {

//...

cacheElement(context.evalNode("cache")); //解析cache标签

} catch (Exception e) {

throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);

}

}

private void cacheElement(XNode context) {

if (context != null) { // if hava cache tag 如果有cache标签才执行下面的逻辑

String type = context.getStringAttribute("type", "PERPETUAL");

Class extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);

String eviction = context.getStringAttribute("eviction", "LRU");

Class extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);

Long flushInterval = context.getLongAttribute("flushInterval");

Integer size = context.getIntAttribute("size");

boolean readWrite = !context.getBooleanAttribute("readOnly", false);

boolean blocking = context.getBooleanAttribute("blocking", false);

Properties props = context.getChildrenAsProperties();

builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);//建立二级缓存

}

}

org.apache.ibatis.builder.MapperBuilderAssistant.useNewCache():

public Cache useNewCache(Class extends Cache> typeClass,

Class extends Cache> evictionClass,

Long flushInterval,

Integer size,

boolean readWrite,

boolean blocking,

Properties props) {

Cache cache = new CacheBuilder(currentNamespace)

.implementation(valueOrDefault(typeClass, PerpetualCache.class))

.addDecorator(valueOrDefault(evictionClass, LruCache.class))

.clearInterval(flushInterval)

.size(size)

.readWrite(readWrite)

.blocking(blocking)

.properties(props)

.build();

configuration.addCache(cache);//二级缓存赋值,如果cache标签为空,不会执行此方法,currentCache为空

currentCache = cache;

return cache;

}

在映射文件mapper中如果没有cache标签,不会执行上面的useNewCache方法,cache为null,就不会使用二级缓存(相当于失效)。

查询使用二级缓存逻辑

org.apache.ibatis.executor.CachingExecutor :

@Override

public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)

throws SQLException {

Cache cache = ms.getCache();

if (cache != null) {//如果二级缓存对象不为空 尝试在二级缓存中获取(没有cache标签此对象就是空)

flushCacheIfRequired(ms);

if (ms.isUseCache() && resultHandler == null) {

ensureNoOutParams(ms, boundSql);

@SuppressWarnings("unchecked")

List list = (List) tcm.getObject(cache, key); //从二级缓存中获取数据

if (list == null) {

list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); //如果为空,使用delegate查询(BaseExecutor)

tcm.putObject(cache, key, list); // 查询结果保存到二级缓存

uRGFTzte }

return list;

}

}

return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

}

二级缓存和一级缓存不用想,数据库的数据被修改是要清空缓存的,不然数据有误,至于怎么清空,是另一套逻辑了,mapper中的cache标签可以配置一些参数,比如缓存定期清空。

一级二级缓存先后顺序

mybatis默认是先查询二级缓存,没有,再查看一级缓存,都为空,最后查询数据库

以上就是详解Mybatis的缓存的详细内容,更多关于Mybatis的缓存的资料请关注我们其它相关文章!


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:MAC 安装python3
下一篇:/tmp/supervisor.sock no such file 报错
相关文章

 发表评论

暂时没有评论,来抢沙发吧~