Mybatis的一級緩存,指的是SqlSession級別的緩存,默認開啓;Mybatis的二級緩存,指的是SqlSessionFactory級別的緩存,須要配置。緩存是針對select來講的。java
<configuration> <settings> <setting name="localCacheScope" value="SESSION|STATEMENT" /> </settings> </configuration>
localCacheScope用於配置一級緩存的範圍,默認值是SESSION,表示SqlSession範圍;sql
若是配置爲STATEMENT,則表示SqlSession範圍內的一個查詢範圍,但它並非一個Statement實例範圍。數據庫
STATEMENT舉例:查詢Student對象發送一次sql查詢,緊接着再發一次sql查詢關聯的Teacher對象,這個完整過程稱之爲一個查詢。apache
一級緩存默認開啓,且沒有全局關閉的配置開關。緩存
<select ... flushCache="false" useCache="true|false"/>
flushCache:同時影響了一級、二級緩存,flushCache=true,會致使清空本條sql(當前MappedStatement)的一級、二級緩存,注意是當前的,不影響其餘的MappedStatement。網絡
useCache:配置本條MappedStatement是否使用二級緩存,useCache=true,從二級緩存中獲取,沒有獲取到,才從數據庫中獲取。數據結構
org.apache.ibatis.executor.CachingExecutor#query()方法源碼:mybatis
Cache cache = ms.getCache(); if (cache != null) { // flushCache做用於二級緩存 flushCacheIfRequired(ms); // useCache做用於二級緩存 if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, parameterObject, boundSql); @SuppressWarnings("unchecked") List<E> list = (List<E>) tcm.getObject(cache, key); if (list == null) { list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
org.apache.ibatis.executor.BaseExecutor#query()方法源碼:app
if (queryStack == 0 && ms.isFlushCacheRequired()) { // flushCache做用於一級緩存 clearLocalCache(); }
在執行update、insert、delete、flushCache="true"、commit、rollback、LocalCacheScope.STATEMENT等狀況下,一級緩存就都會被清空。ide
緩存其實基本數據結構就是一個HashMap,緩存中是否存在緩存數據,依賴key的生成策略。
org.apache.ibatis.executor.BaseExecutor.createCacheKey()源碼。
@Override public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) { CacheKey cacheKey = new CacheKey(); cacheKey.update(ms.getId()); cacheKey.update(Integer.valueOf(rowBounds.getOffset())); cacheKey.update(Integer.valueOf(rowBounds.getLimit())); cacheKey.update(boundSql.getSql()); for (int i = 0; i < parameterMappings.size(); i++) { //... cacheKey.update(value); } } if (configuration.getEnvironment() != null) { // issue #176 cacheKey.update(configuration.getEnvironment().getId()); } return cacheKey; }
id:com.mybatis3.mappers.TeacherMapper.findTeacherById
key的生成策略:id + offset + limit + sql + param value + environment id,這些值都相同,生成的key就相同。
<configuration> <settings> <setting name="cacheEnabled" value="true|false" /> </settings> </configuration>
cacheEnabled=true表示二級緩存可用,可是要開啓話,須要在Mapper.xml內配置。
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
if (cacheEnabled) { executor = new CachingExecutor(executor); }
二級緩存經過CachingExecutor來實現,原理是緩存裏存在,就返回,沒有就調用Executor delegate到數據庫中查詢。
org.apache.ibatis.executor.CachingExecutor.query()源碼。
@Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { Cache cache = ms.getCache(); if (cache != null) { flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, parameterObject, boundSql); @SuppressWarnings("unchecked") List<E> list = (List<E>) tcm.getObject(cache, key); if (list == null) { list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
FIFO:First In First Out先進先出隊列。
flushInterval="60000",間隔60秒清空緩存,這個間隔60秒,是被動觸發的,而不是定時器輪詢的。
size=512,表示隊列最大512個長度,大於則移除隊列最前面的元素,這裏的長度指的是CacheKey的個數。
CacheKey的生成策略,和一級緩存相同,id + offset + limit + sql + param value + environment id。
readOnly="true",表示任何獲取對象的操做,都將返回同一實例對象。若是readOnly="false",則每次返回該對象的拷貝對象,簡單說就是序列化複製一份返回。
二級緩存有一個很是重要的空間劃分策略:
namespace="com.mybatis3.mappers.TeacherMapper"
namespace="com.mybatis3.mappers.StudentMapper"
即,按照namespace劃分,同一個namespace,同一個Cache空間,不一樣的namespace,不一樣的Cache空間。
每當執行insert、update、delete,flushCache=true時,二級緩存都會被清空。
版權提示:文章出自開源中國社區,若對文章感興趣,可關注個人開源中國社區博客(http://my.oschina.net/zudajun)。(通過網絡爬蟲或轉載的文章,常常丟失流程圖、時序圖,格式錯亂等,仍是看原版的比較好)