MyBatis缓存之一级缓存(SqlSession级别)二级缓存(Mapper级别)原理

        MyBatis的缓存分为一级缓存和二级缓存(全局缓存) ,缓存示意图如下图所示。默认情况下,一级缓存是开启的,且不能被关闭。

MyBatis缓存之一级缓存(SqlSession级别)二级缓存(Mapper级别)原理_第1张图片

  MyBatis的一级缓存(SqlSession级别)

在同一个SqlSession中执行相同的SQL语句时,将查询结果集缓存,第二次查询时直接取缓存,不在操作数据库。注意:一级缓存最多缓存1024条SQL语句。

原理: 当客户端第一次发出SQL查询语句时,MyBatis执行SQL的查询并将查询结果写入SqlSession的一级缓存中,当第二次相同的SQL的查询语句过来时,则直接从缓存中获取数据,具体过程如上图所示。缓存使用的数据结构是Map,其中key是 MapperId+Offset+Limit+SQL+所有的入参。

同一个SqlSession操作相同的SQL语句,如中间有修改操作时,需不要手动flush?

不需要!!!

当同一个SqlSession多次发出相同的SQL语句查询时,MyBatis会直接从缓存中取数据。如果前后两次中间出现过commit操作(修改、添加、删除),则认为数据发生了变化,MyBatis会把SqlSession中的一级缓存区域全部清空,则下一次的SQL语句查询时,需从数据库中查询数据并将查询的结果写入缓存。

1、MyBatis的一级缓存是SqlSession级别的,但是它并不定义在SqlSessio接口的实现类DefaultSqlSession中,而是定义在DefaultSqlSession的成员变量Executor中,Executor是在openSession的时候被实例化出来的,它的默认实现为SimpleExecutor

2、MyBatis中的一级缓存,与有没有配置无关,只要SqlSession存在,MyBastis一级缓存就存在,localCache的类型是PerpetualCache,它其实很简单,一个id属性+一个HashMap属性而已,id是一个名为"localCache"的字符串,HashMap用于存储数据,Key为CacheKey,Value为查询结果

3、MyBatis的一级缓存查询的时候默认都是会先尝试从一级缓存中获取数据的,但是我们看第6行的代码做了一个判断,ms.isFlushCacheRequired(),即想每次查询都走DB也行,将标签所在的Mapper的Namespace+标签中定义的sql语句

即只要两次查询满足以上三个条件且没有定义flushCache="true",那么第二次查询会直接从MyBatis一级缓存PerpetualCache中返回数据,而不会走DB。


/**
 * @author Clinton Begin
 */
public final class MappedStatement {

  private String resource;
  private Configuration configuration;
  private String id;
  private Integer fetchSize;
  private Integer timeout;
  private StatementType statementType;
  private ResultSetType resultSetType;
  private SqlSource sqlSource;
  private Cache cache;
  private ParameterMap parameterMap;
  private List resultMaps;
  private boolean flushCacheRequired;
  private boolean useCache;
  private boolean resultOrdered;
  private SqlCommandType sqlCommandType;
  private KeyGenerator keyGenerator;
  private String[] keyProperties;
  private String[] keyColumns;
  private boolean hasNestedResultMaps;
  private String databaseId;
  private Log statementLog;
  private LanguageDriver lang;
  private String[] resultSets;

  private MappedStatement() {
    // constructor disabled
  }

  public static class Builder {
    private MappedStatement mappedStatement = new MappedStatement();

    public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
      mappedStatement.configuration = configuration;
      mappedStatement.id = id;
      mappedStatement.sqlSource = sqlSource;
      mappedStatement.statementType = StatementType.PREPARED;
      mappedStatement.parameterMap = new ParameterMap.Builder(configuration, "defaultParameterMap", null, new ArrayList()).build();
      mappedStatement.resultMaps = new ArrayList();
      mappedStatement.timeout = configuration.getDefaultStatementTimeout();
      mappedStatement.sqlCommandType = sqlCommandType;
      mappedStatement.keyGenerator = configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
      String logId = id;
      if (configuration.getLogPrefix() != null) logId = configuration.getLogPrefix() + id;
      mappedStatement.statementLog = LogFactory.getLog(logId);
      mappedStatement.lang = configuration.getDefaultScriptingLanuageInstance();
    }

    public Builder resource(String resource) {
      mappedStatement.resource = resource;
      return this;
    }

    public String id() {
      return mappedStatement.id;
    }

    public Builder parameterMap(ParameterMap parameterMap) {
      mappedStatement.parameterMap = parameterMap;
      return this;
    }

    public Builder resultMaps(List resultMaps) {
      mappedStatement.resultMaps = resultMaps;
      for (ResultMap resultMap : resultMaps) {
        mappedStatement.hasNestedResultMaps = mappedStatement.hasNestedResultMaps || resultMap.hasNestedResultMaps();
      }
      return this;
    }

    public Builder fetchSize(Integer fetchSize) {
      mappedStatement.fetchSize = fetchSize;
      return this;
    }

    public Builder timeout(Integer timeout) {
      mappedStatement.timeout = timeout;
      return this;
    }

    public Builder statementType(StatementType statementType) {
      mappedStatement.statementType = statementType;
      return this;
    }

    public Builder resultSetType(ResultSetType resultSetType) {
      mappedStatement.resultSetType = resultSetType;
      return this;
    }

    public Builder cache(Cache cache) {
      mappedStatement.cache = cache;
      return this;
    }

    public Builder flushCacheRequired(boolean flushCacheRequired) {
      mappedStatement.flushCacheRequired = flushCacheRequired;
      return this;
    }

    public Builder useCache(boolean useCache) {
      mappedStatement.useCache = useCache;
      return this;
    }

    public Builder resultOrdered(boolean resultOrdered) {
      mappedStatement.resultOrdered = resultOrdered;
      return this;
    }

    public Builder keyGenerator(KeyGenerator keyGenerator) {
      mappedStatement.keyGenerator = keyGenerator;
      return this;
    }

    public Builder keyProperty(String keyProperty) {
      mappedStatement.keyProperties = delimitedStringtoArray(keyProperty);
      return this;
    }

    public Builder keyColumn(String keyColumn) {
      mappedStatement.keyColumns = delimitedStringtoArray(keyColumn);
      return this;
    }

    public Builder databaseId(String databaseId) {
      mappedStatement.databaseId = databaseId;
      return this;
    }

    public Builder lang(LanguageDriver driver) {
      mappedStatement.lang = driver;
      return this;
    }

    public Builder resulSets(String resultSet) {
      mappedStatement.resultSets = delimitedStringtoArray(resultSet);
      return this;
    }
    
    public MappedStatement build() {
      assert mappedStatement.configuration != null;
      assert mappedStatement.id != null;
      assert mappedStatement.sqlSource != null;
      assert mappedStatement.lang != null;
      mappedStatement.resultMaps = Collections.unmodifiableList(mappedStatement.resultMaps);
      return mappedStatement;
    }
  }


  public boolean hasNestedResultMaps() {
    return hasNestedResultMaps;
  }



  
  public BoundSql getBoundSql(Object parameterObject) {
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    List parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.size() <= 0) {
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }

    // check for nested result maps in parameter mappings (issue #30)
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
      String rmId = pm.getResultMapId();
      if (rmId != null) {
        ResultMap rm = configuration.getResultMap(rmId);
        if (rm != null) {
          hasNestedResultMaps |= rm.hasNestedResultMaps();
        }
      }
    }

    return boundSql;
  }

  private static String[] delimitedStringtoArray(String in) {
    if (in == null || in.trim().length() == 0) {
      return null;
    } else {
      String[] answer = in.split(",");
      return answer;
    }
  }

}

一二级缓存之间的关系 

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  executorType = executorType == null ? defaultExecutorType : executorType;
  executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  Executor executor;
  if (ExecutorType.BATCH == executorType) {
    executor = new BatchExecutor(this, transaction);
  } else if (ExecutorType.REUSE == executorType) {
    executor = new ReuseExecutor(this, transaction);
  } else {
    executor = new SimpleExecutor(this, transaction);
  }
  // 如果开启二级缓存会使用CachingExecutor对原Executor进行包装  
  if (cacheEnabled) {
    executor = new CachingExecutor(executor);
  }
  // Mybatis插件功能对Executor的增强  
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}

MyBatis缓存

你可能感兴趣的:(杂记和踩坑,MyBatis,MyBatis,MyBatis缓存机制)