Mybatis源码解析-查询流程解析

125 阅读3分钟

接上文在拿到SqlSession对象后,就可以使用此类提供的接口对数据库操作了。

image.png 从上图可以很直观的看出,SqlSession类提供的一些对数据库的crud操作,以及提交回滚事务。

@Test
void shouldSelectAllAuthors() {
  try (SqlSession session = sqlMapper.openSession(TransactionIsolationLevel.SERIALIZABLE)) {
    List<Author> authors = session.selectList("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAllAuthors");
    assertEquals(2, authors.size());
  }
}

通过Mybatis的源码测试类shouldSelectAllAuthors()进行调试。

//来到此行代码
List<Author> authors = session.selectList("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAllAuthors");
  • selectList
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
  try {
    //获取MappedStatement
    MappedStatement ms = configuration.getMappedStatement(statement);
    dirty |= ms.isDirtySelect();
    return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}
  • 获取MappedStatement

在Configuration类中

protected final Map<String, MappedStatement> mappedStatements = new StrictMap( "Mapped Statements collection") .conflictMessageProducer((savedValue, targetValue) -> ". please check " + savedValue.getResource() + " and " + targetValue.getResource()); 对象获取到 MappedStatement

image.png

image.png

上图是我们调用接口的Mapper类和xml配置sql文件,mappedStatements对象的是一个map类型,key是AhthorMapper类 selectAllAuthors方法全限定名:org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAllAuthors value是一个MappedStatement对象,也就是一个select标签

image.png 接着往下走到excutor.query()。excutor是创建SqlSession流程的时候构建的CachingExcutor对象(详细可以看上文)。

  • query()
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler)
    throws SQLException {
  //获取sql
  BoundSql boundSql = ms.getBoundSql(parameterObject);
  //创建CacheKey
  CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
  return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

这里我们重点关注下mybatis二级缓存key是怎么生成的。

@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
  if (closed) {
    throw new ExecutorException("Executor was closed.");
  }
  CacheKey cacheKey = new CacheKey();
  //0org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAllAuthors
  cacheKey.update(ms.getId());
  //0
  cacheKey.update(rowBounds.getOffset());
  //2147483647 Integer.MAX_VALUE
  cacheKey.update(rowBounds.getLimit());
  //select * from author
  cacheKey.update(boundSql.getSql());
  //以下是参数
  List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
  TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
  // mimic DefaultParameterHandler logic
  MetaObject metaObject = null;
  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 {
        if (metaObject == null) {
          metaObject = configuration.newMetaObject(parameterObject);
        }
        value = metaObject.getValue(propertyName);
      }
      cacheKey.update(value);
    }
  }
  if (configuration.getEnvironment() != null) {
    // issue #176
   //数据源
    cacheKey.update(configuration.getEnvironment().getId());
  }
  return cacheKey;
}

可已看出key是由方法全限定类名,分页偏移,sql,参数,数据构成。也就是说满足这些条件相同的sql查询才会走缓存。 最终的key值: 1456362818:1472358054:org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAllAuthors:0:2147483647:select * from author:development

继续往下走到


@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler,
   CacheKey key, BoundSql boundSql) throws SQLException {
 //cache
 Cache cache = ms.getCache();
 if (cache != null) {
   //清空缓存
   flushCacheIfRequired(ms);
   if (ms.isUseCache() && resultHandler == null) {
     ensureNoOutParams(ms, boundSql);
     @SuppressWarnings("unchecked")
       //获取缓存
     List<E> list = (List<E>) tcm.getObject(cache, key);
     if (list == null) {
       //缓存为空查询完后再放入缓存中
       list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
       tcm.putObject(cache, key, list); // issue #578 and #116
     }
     return list;
   }
 }
 return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

@Override
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.");
 }
 //flushCacheRequired==true 也回清除缓存
 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 {
     //不走缓存,查询db获取数据
     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;
}

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;
}

关于缓存的一些操作,有缓存数据则拿到缓存数据,没有的话查询db拿数据

下面来看doQuery()方法,获取StatementHandler和Statement,然后执行查询。

@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
    BoundSql boundSql) throws SQLException {
  Statement stmt = null;
  try {
    Configuration configuration = ms.getConfiguration();
    //获取RoutingStatementHandler
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler,
        boundSql);
    //获取Statement
    stmt = prepareStatement(handler, ms.getStatementLog());
    //执行查询
    return handler.query(stmt, ```
Statement
```);
  } finally {
    closeStatement(stmt);
  }
}
    

执行 handler.query()

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
 PreparedStatement ps = (PreparedStatement) statement;
 //jdbc查询操作
 ps.execute();
 //处理结果集
 return resultSetHandler.handleResultSets(ps);
}