Mybatis有兩級緩存,一級緩存是在同一個SqlSession中有效,兩次徹底相同的查詢,第二次就是從本地緩存中獲得上一次的查詢結果java
Mybatis是默認開啓一級緩存的,沒法關閉。好比在一個會話中連續兩次執行同一個方法, 獲得的是同一個對象sql
一級緩存在一下六種狀況會失效:
1.不一樣的會話
2.同一個會話,兩次相同查詢中間有增刪改操做,由於都會調用clearLocalCache()方法清除緩存
3.同一個會話,查詢(條件)參數不一樣
4.同一個會話,調用清楚會話內容操做 SqlSession.flushCache()
5.同一個會話,SQL相同與條件(參數)相同,可是方法名不一樣
6.同一個會話,分頁信息(偏移量和須要返回的記錄數)不一樣數據庫
舉例:緩存
public void testCache1() { SqlSession session = getSqlSession(); CustMapper mapper = session.getMapper(CustMapper.class); //SQL:select cust_id,cust_name from cust where cust_id=#{custId}; Cust cust1 = mapper.getCustById(1); //SQL:select cust_id,cust_name from cust where cust_id=#{custId}; //雖然getCustById和getCustByIdNew使用的相同條件和相同SQL,但方法不一樣不能使用緩存 Cust cust2 = mapper.getCustByIdNew(1); //若是在這裏執行一個增刪改操做,那麼本地緩存中的 數據將會被清空,下面的getCustByIdNew(1)會再次從數據庫中查詢數據 //mapper.addCust(new Cust("TonTan")) //由於和Cust cust2 = mapper.getCustByIdNew(1);方法名(全類名+方法名)、條件、SQL、默認分頁信息都同樣因此可使用緩存 Cust cust3 = mapper.getCustByIdNew(1); }
附上一些源碼分析:session
一級緩存是使用了名爲PerpetualCache的類,變量名爲localCache,其內部就是一個Map<Object,Object>。app
public class PerpetualCache implements Cache { private final String id; private Map<Object, Object> cache = new HashMap<Object, Object>(); }
Mybatis每次執行查詢的時候會執行localCache.get(key),這個Key是CacheKey類,他的hashCode值是由以下幾個因素決定,只要這幾個因素徹底一致就能夠從localCache中找到以前的查詢結果。生成key的方法以下:源碼分析
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) { if (closed) { throw new ExecutorException("Executor was closed."); } CacheKey cacheKey = new CacheKey(); cacheKey.update(ms.getId()); cacheKey.update(rowBounds.getOffset()); cacheKey.update(rowBounds.getLimit()); cacheKey.update(boundSql.getSql()); //其餘部分沒有複製出來 }
ms.getId是方法的全類名+方法名
rowBounds.getOffset()分頁查詢時的偏移量
rowBounds.getLimit()分頁查詢時的查詢記錄數
boundSql.getSql()sql自己code
何時將查詢結果放入緩存的喃?對象
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> 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; }
localCache.putObject(key, list);就是它了。。。。。。。rem
那爲何調用了增刪改緩存就失效了喃,由於他們都底層都是調用update方法,而它調用了clearLocalCache(),因此。。你懂的。
最後提一個全局配置參數:localCacheScope,它能夠設置爲SESSION或者STATEMENT,默認爲SESSION,可是這樣的話,可能取到髒數據,假如設置爲STATEMENT的話就能夠避免這種狀況,至關於禁止了一級緩存。