详解Mybatis的缓存
Mybatis的缓存
mybatis是一个查询数据库的封装框架,主要是封装提供灵活的增删改sql,开发中,service层能够通过mybatis组件查询和修改数据库中表的数据;作为查询工具,mybatis有使用缓存,这里讲一下mybatis的缓存相关源码。
缓存
在计算机里面,任何信息都有源头,缓存一般指源头信息读取后,放在内存或者其他读取较快的地方,下次读取相同信息不去源头查询而是直接从内存(或者能快速存取的硬件)读取。这样可以减少硬件使用,提高读取速度。
mybatis也是这样,查询数据库的数据之后,mybatis可以把查询结果缓存到内存,下次查询如果查询语句相同,并且查询相关的表的数据没被修改过,就可以直接返回缓存中的结果,而不用去查询数据库的语句,有效节省了时间。
简单看一下mybatis一级缓存和二级缓存相关源码,学习使用
一级缓存
通过查看源码可知,一级缓存是绑定sqSsession中的,所以每次查询sqlSession不同就失效,相同的sqlSession可以使用一级缓存。
mybatis默认sqlsession:org.apache.ibatis.session.defaults.DefaultSqlSession
构造方法中传入executor(查询执行对象)
publicDefaultSqlSession(Configurationconfiguration,Executorexecutor,booleanautoCommit){ this.configuration=configuration; this.executor=executor; this.dirty=false; this.autoCommit=autoCommit; }
executor中携带一级缓存成员:
protectedBaseExecutor(Configurationconfiguration,Transactiontransaction){ this.transaction=transaction; this.deferredLoads=newConcurrentLinkedQueue<>(); this.localCache=newPerpetualCache("LocalCache");//默认一级缓存 this.localOutputParameterCache=newPerpetualCache("LocalOutputParameterCache"); this.closed=false; this.configuration=configuration; this.wrapper=this; }
查询使用一级缓存逻辑
org.apache.ibatis.executor.BaseExecutor.query()
publicList query(MappedStatementms,Objectparameter,RowBoundsrowBounds,ResultHandlerresultHandler,CacheKeykey,BoundSqlboundSql)throwsSQLException{ ErrorContext.instance().resource(ms.getResource()).activity("executingaquery").object(ms.getId()); List 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--; } returnlist; } //从数据库读取数据 private List queryFromDatabase(MappedStatementms,Objectparameter,RowBoundsrowBounds,ResultHandlerresultHandler,CacheKeykey,BoundSqlboundSql)throwsSQLException{ 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); } returnlist; }
二级缓存
二级缓存mapper中的,默认是开启的,但需要在映射文件mapper.xml中添加
配置false可以关闭二级缓存
二级缓存的解析
org.apache.ibatis.builder.xml.XMLMapperBuilder
privatevoidconfigurationElement(XNodecontext){ try{ //... cacheElement(context.evalNode("cache"));//解析cache标签 }catch(Exceptione){ thrownewBuilderException("ErrorparsingMapperXML.TheXMLlocationis'"+resource+"'.Cause:"+e,e); } } privatevoidcacheElement(XNodecontext){ if(context!=null){//ifhavacachetag如果有cache标签才执行下面的逻辑 Stringtype=context.getStringAttribute("type","PERPETUAL"); ClasstypeClass=typeAliasRegistry.resolveAlias(type); Stringeviction=context.getStringAttribute("eviction","LRU"); ClassevictionClass=typeAliasRegistry.resolveAlias(eviction); LongflushInterval=context.getLongAttribute("flushInterval"); Integersize=context.getIntAttribute("size"); booleanreadWrite=!context.getBooleanAttribute("readOnly",false); booleanblocking=context.getBooleanAttribute("blocking",false); Propertiesprops=context.getChildrenAsProperties(); builderAssistant.useNewCache(typeClass,evictionClass,flushInterval,size,readWrite,blocking,props);//建立二级缓存 } }
org.apache.ibatis.builder.MapperBuilderAssistant.useNewCache():
publicCacheuseNewCache(ClasstypeClass, ClassevictionClass, LongflushInterval, Integersize, booleanreadWrite, booleanblocking, Propertiesprops){ Cachecache=newCacheBuilder(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; returncache; }
在映射文件mapper中如果没有cache标签,不会执行上面的useNewCache方法,cache为null,就不会使用二级缓存(相当于失效)。
查询使用二级缓存逻辑
org.apache.ibatis.executor.CachingExecutor:
@Override publicList query(MappedStatementms,ObjectparameterObject,RowBoundsrowBounds,ResultHandlerresultHandler,CacheKeykey,BoundSqlboundSql) throwsSQLException{ Cachecache=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);//查询结果保存到二级缓存 } returnlist; } } returndelegate.query(ms,parameterObject,rowBounds,resultHandler,key,boundSql); }
二级缓存和一级缓存不用想,数据库的数据被修改是要清空缓存的,不然数据有误,至于怎么清空,是另一套逻辑了,mapper中的cache标签可以配置一些参数,比如缓存定期清空。
一级二级缓存先后顺序
mybatis默认是先查询二级缓存,没有,再查看一级缓存,都为空,最后查询数据库
以上就是详解Mybatis的缓存的详细内容,更多关于Mybatis的缓存的资料请关注毛票票其它相关文章!