Mybatis 执行查询的整个流程

原创
2016/02/25 17:05
阅读数 1.7K


先来一张图,看看整个流程。。。。。


1、使用Spring-Mybatis , SqlSessionTemplate 进行数据库操作

RowBounds row = new RowBounds(1,10);// 分页参数
List<Map<String, Object>> list = commonRepository.selectList("GradeMapper.getGrade", params,row);


sqlSessionTemplate.selectList(statement, parameter,rowBounds); // 调用代理


2、public class DefaultSqlSession implements SqlSession ,通过代理可以调用默认使用DefaultSqlSession进行数据库操作,真正的操作又是委托给 Executor

@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
  try {
    MappedStatement ms = configuration.getMappedStatement(statement);
    return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
  } catch (Exception e) {
     throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}


3、public class CachingExecutor implements Executor , 查询首先要做的就是判断缓存中是否存在,如果有就直接返回,否则就从数据中查询,这里的缓存是二级缓存

@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
  Cache cache = ms.getCache();
  if (cache != null) {
    flushCacheIfRequired(ms);
    if (ms.isUseCache() && resultHandler == null) {
      ensureNoOutParams(ms, parameterObject, boundSql);
      List<E> list = (List<E>) tcm.getObject(cache, key);
      if (list == null) {
        list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        tcm.putObject(cache, key, list); // issue #578 and #116
     }
    return list;
   }
  }
  return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}


4、public abstract class BaseExecutor implements Executor  , 这里的缓存是一级缓存

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


6、public class SimpleExecutor extends BaseExecutor 如果缓存中没有数据只能从数据库中获取,数据库获取数据需要如下几步

@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();
     // 获取系统设置信息,这个类可以说涵盖整个系统需要的设置项,下面是加载插件  
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    stmt = prepareStatement(handler, ms.getStatementLog()); // 首先创建Statement,其次赋值Params
    return handler.<E>query(stmt, resultHandler); // 执行数据操作
  } finally {
    closeStatement(stmt); // 关闭数据库连接
  }
}


7、stmt = prepareStatement(handler, ms.getStatementLog());   创建Statement,其次赋值Params

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  Connection connection = getConnection(statementLog);// 获取数据库连接
  stmt = handler.prepare(connection); // 创建Statement
  handler.parameterize(stmt); // 赋值Params
  return stmt;
}

1)prepare 设置他的超时以及批量数,以及选择 Statement 的形式

statement = instantiateStatement(connection);
setStatementTimeout(statement);
setFetchSize(statement);
return statement;

2) 选择 Statement 形式,可滚动的结果集,mappedStatement.getResultSetType() 就是我们配置<select resultSetType> 属性

@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
  String sql = boundSql.getSql();
  if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) { // 设置获取主键属性的值
    String[] keyColumnNames = mappedStatement.getKeyColumns();
    if (keyColumnNames == null) {
        return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
      } else {
        return connection.prepareStatement(sql, keyColumnNames);
      }
  } else if (mappedStatement.getResultSetType() != null) { // 如果设置了滚动类型
    return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
  } else { // 默认构建
    return connection.prepareStatement(sql);
  }
}

3)handler.parameterize(stmt) 设置参数,参数设置的重点在于参数类型的获取,也就是 JdbcType 的作用

@Override
public void setParameters(PreparedStatement ps) {
  ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
  List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
  if (parameterMappings != null) {
    for (int i = 0; i < parameterMappings.size(); i++) {
      ParameterMapping parameterMapping = parameterMappings.get(i);
      if (parameterMapping.getMode() != ParameterMode.OUT) {
        Object value;
        String propertyName = parameterMapping.getProperty();
        if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            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);
        }
        TypeHandler typeHandler = parameterMapping.getTypeHandler();
        // 获取jdbcType,一般XML中不需要配置这个参数,系统可以自动匹配
        JdbcType jdbcType = parameterMapping.getJdbcType(); 
        if (value == null && jdbcType == null) {
          jdbcType = configuration.getJdbcTypeForNull();
        }
        try {
          // 这里如果jdbcType=null,那么获取的就是UnknownTypeHandler
          typeHandler.setParameter(ps, i + 1, value, jdbcType);
        } catch (TypeException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
        } catch (SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
        }
      }
    }
  }
}


8、public class PreparedStatementHandler extends BaseStatementHandler 执行 Statement execute 

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  ps.execute(); // 执行sql语句
  return resultSetHandler.<E> handleResultSets(ps); // 处理结果集
}


9、public class DefaultResultSetHandler implements ResultSetHandler  处理结果集

1)判断是否有嵌套的 ResultMap ,这个也是在 <select resultMap>设置的

private void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    if (resultMap.hasNestedResultMaps()) { // 如果有嵌套
        ensureNoRowBounds();
        checkResultHandler();
        handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else { // 如果没有嵌套
        handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
}

2)假设没有嵌套的ResultMap ,那么进行结果集遍历,也就是Mybatis的分页

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
  DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
  //首先跳过不需要的记录数
  skipRows(rsw.getResultSet(), rowBounds);
  // 滚动结果集,直到 limit
  while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) { 
    ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
    //获取行中的数据
    Object rowValue = getRowValue(rsw, discriminatedResultMap);
    // 解析值并存储
    storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet()); 
  }
}

3)skipRows(rsw.getResultSet(), rowBounds) 首先跳过不需要的记录数

private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
    if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) {
      rs.absolute(rowBounds.getOffset());
    }
  } else { 
  // 没有设置滚动类型时默认就是 TYPE_FORWARD_ONLY ,这里就算是这个值,也可以使用 absolute ,为什么不用呢?
    for (int i = 0; i < rowBounds.getOffset(); i++) {
      rs.next();
    }
  }
}

4)shouldProcessMoreRows(resultContext, rowBounds) 是否已经到了Limit

private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) throws SQLException {
    return !context.isStopped() && context.getResultCount() < rowBounds.getLimit();
}

5)getRowValue(rsw, discriminatedResultMap) 从结果集的每一行中获取数据,同样这里也需要类型转换,ResultMap 中可以配置 JavaType JdbcType 对获取数据类型是有点作用的。

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
  final ResultLoaderMap lazyLoader = new ResultLoaderMap();
  Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null);
  // 获取类型转换器和以前一样
  if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) { 
    final MetaObject metaObject = configuration.newMetaObject(resultObject);
    boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty();
    if (shouldApplyAutomaticMappings(resultMap, false)) {
      foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
    }
    foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
    foundValues = lazyLoader.size() > 0 || foundValues;
    resultObject = foundValues ? resultObject : null;
    return resultObject;
  }
  return resultObject;
}

6)storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet()) 存储数据

private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
  if (parentMapping != null) {
    linkToParents(rs, parentMapping, rowValue);
  } else {
    callResultHandler(resultHandler, resultContext, rowValue);
  }


展开阅读全文
打赏
1
10 收藏
分享
加载中
更多评论
打赏
0 评论
10 收藏
1
分享
返回顶部
顶部