1. 程式人生 > >六、MyBatis-快取機制

六、MyBatis-快取機制

MyBatis 包含一個非常強大的查詢快取特性,它可以非常方便地配置和定製。快取可以極大的提升查詢效率。
MyBatis系統中預設定義了兩級快取, 一級 快取和 二級快取。
– 1、預設情況下,只有一級快取(SqlSession級別的快取,也稱為本地快取)開啟,一級快取預設實現類org.apache.ibatis.cache.impl.PerpetualCache。
– 2、二級快取需要手動開啟和配置,他是基於namespace級別的快取。
– 3、為了提高擴充套件性。MyBatis定義了快取介面Cache,我們可以通過實現Cache介面來自定義二級快取。

一級快取

一級快取是Local Cache,即本地快取,預設作用域為SqlSesson級別,mybatis3.1之後, 可以配置本地快取的作用域(SESSION|STATEMENT),在全域性配置檔案中可以設定localCacheScope。Mybatis預設的一級快取預設是通過org.apache.ibatis.cache.impl.PerpetualCache來實現的,PerpetualCache內裡有一個HashMap,用來儲存本地快取。

Map的CacheKey由方法的ID,RowBounds,SQL語句,引數,環境因素影響;Map的Value存放具體的結果物件。

格式:影響因子的HashCode1:影響因子的HashCode2:方法ID:offset:limit:SQL語句:引數:環境ID

-1623117942:1735139101:com.kancy.mapper.PersonMapper.selectPersonById:0:2147483647:select * from t_person where id = ? and last_name = ? and sex = ?:1:emma:0:test

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()); List
<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry(); // mimic DefaultParameterHandler logic for (ParameterMapping parameterMapping : parameterMappings) { if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } cacheKey.update(value); } } if (configuration.getEnvironment() != null) { // issue #176 cacheKey.update(configuration.getEnvironment().getId()); } return cacheKey; }

 

從查詢的原始碼可以看出,mybatis在查詢前,先看是否需要清除快取,再通過CacheKey從快取中查詢是否有對應的key,有則返回物件,無則從資料庫中查詢,再看一級快取的作用域範圍,是STATEMENT則清除快取,否則將結果集放到快取中。

public <E> List<E> 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());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }

 

一級快取演示& & 失效情況

同一次會話期間只要查詢過的資料都會儲存在當前SqlSession的一個Map中
一級快取失效的四種情況
– 1、不同的SqlSession對應不同的一級快取
– 2、同一個SqlSession但是查詢條件不同
– 3、同一個SqlSession兩次查詢期間執行了任何一次增刪改操作
– 4、同一個SqlSession兩次查詢期間手動清空了快取

 

注意:

– sql標籤的flushCache屬性查詢預設flushCache=false;增刪改insert|update|delete會修改資料,預設flushCache=true,會同時清空一級和二級快取。
– sqlSession.clearCache():只是用來清除一級快取,不會清除二級快取。
– 當在某一個作用域 (一級快取Session/二級快取Namespaces) 進行了 C/U/D 操作後,預設該作用域下所有有select中的快取將被clear。

 

二級快取