1. 程式人生 > >六、MyBatis-緩存機制

六、MyBatis-緩存機制

cached asa color 全局 同時 null del tid property

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。

二級緩存

六、MyBatis-緩存機制