关于一级缓存:
- MyBatis在开启一个数据库会话时会新建一个SqlSession对象,SqlSession对象中会有一个新的Executor对象,Executor中会持有一个新的PerpetualCache对象目的;当会话结束时,SqlSession对象、其内部的Executor对象和PerpetualCache对象也被释放。
- 如果SqlSession调用了close()方法,会释放一级缓存PerpetualCache对象,一级缓存不可用。
- 如果SqlSession调用clearCache(),PerpetualCache对象中的数据会被清空,但该对象仍然可以使用。
- SqlSession执行任何更新操作(update(),delete(),insert()),PerpetualCache对象的数据都会被清空,但对象可以继续使用。
如果启用二级缓存参见Configuration#newExecutor,CachingExecutor
publicExecutornewExecutor(Transactiontransaction,ExecutorTypeexecutorType){
执行器类型=执行器类型==空?默认执行器类型:执行器类型;
执行器类型=执行器类型==空?执行者类型。简单:执行者类型;
执行者执行者;
if(ExecutorType.BATCH==executorType){
executor=newBatchExecutor(this,transaction);
}elseif(ExecutorType.REUSE==executorType){
executor=newReuseExecutor(this,transaction);
}别的{executor=newSimpleExecutor(this,transaction);
}
如果(缓存启用){
执行器=新缓存执行器(执行器);
}
executor=(Executor)interceptorChain.pluginAll(executor);
回归执行人;
}
这是一个装饰器模型看下CachingExecutor#query
@Override
publicListquery(MappedStatementms,ObjectparameterObject,RowBoundsrowBounds,ResultHandlerresultHandler,CacheKeykey,BoundSqlboundSql)
抛出SQLException{
缓存cache=ms.getCache();
如果(缓存!=null){
flushCacheIfRequired(毫秒);
如果(ms.isUseCache()&&resultHandler==null){
ensureNoOutParams(ms,parameterObject,boundSql);
@SuppressWarnings("未检查")
Listlist=(List)tcm.getObject(cache,key);
如果(列表==空){
list=delegate.query(ms,parameterObject,rowBounds,resultHandler,key,boundSql);
tcm.putObject(缓存,键,列表);//问题#578和#116}
返回列表;
}
}
返回委托。query(ms,parameterObject,rowBounds,resultHandler,key,boundSql);
}
二级缓存总结
- MyBatis的二级缓存,相对于一级缓存,实现了SqlSession之间缓存数据的共享,同时粒度更细,可以达到命名空间级别。通过不同的组合,Cache的可控性也更强。
- MyBatis在查询多表的时候极有可能出现脏数据,存在设计缺陷。安全使用二级缓存的条件比较苛刻。
- 在分布式环境下,由于MyBatisCache默认实现是基于本地的,因此在分布式环境下难免会读取到脏数据。MyBatis的Cache接口需要使用集中式缓存来实现,需要一定的开发。成本,直接使用Redis、Memcached等分布式缓存可能成本更低,安全性更高。,rollback,getTransaction,close,isClosed)
- ParameterHandler(getParameterObject,setParameters)
- ResultSetHandler(handleResultSets,handleOutputParameters)
- StatementHandler(prepare,parameterize,batch,update,query)
先看mybatis拦截器接口定义
publicinterfaceInterceptor{
对象拦截(Invocationinvocation)throwsThrowable;
对象插件(对象目标);
voidsetProperties(属性属性);
}
再看官方例子