Mybatis源码(三)mybatis的执行流程之查询

前面的博客已经介绍了mybatis的主配置文件的解析以及mapper文件的解析的全流程,我们已经得知mapper文件解析有两种情况,一种是只有#占位符的SQL语句,这种查询语句会将#转成?。还有一种就是含有$或者动态标签的SQL语句,这个时候不做任何处理。今天针对这两种情况,我们来分析一下源码,不过只分析select查询,至于还有insert delete update 会在下篇博客中详细说明。下面来书写我们的测试代码,具体的代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.DemoMapper">
  <select id="selectById" parameterType="int" resultType="map">
    select * from mybooks where id =#{id};
  </select>
  
  <select id="selectAll" parameterType="String" resultType="Map">
    select * from mybooks
    <where>
      1=1
      <if test="param1 != null and param1 != ''">
        and author = ${param1}
      </if>
      and title = #{param2}
    </where>
  </select>
</mapper>
public interface DemoMapper {
  List<Map<String, Object>> selectAll(String author, String title);
  List<Map<String, Object>> selectById(int id);
  default void test() {
    System.out.println(111);
  }
}
public class TestPerform {
  public static void main(String[] args) throws IOException {
    String resource = "mybatis.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    DemoMapper mapper = sqlSession.getMapper(DemoMapper.class);
    System.out.println(mapper.selectById(1));
    System.out.println(mapper.selectAll("Bob", "Ruby"));
    sqlSession.close();
  }
}

我们先来看代理类的创建过程。让我们跟随的代码执行的流程,具体代码如下:

public class DefaultSqlSession implements SqlSession {
  @Override
  public <T> T getMapper(Class<T> type) {
    return configuration.getMapper(type, this);
  }
}

public class Configuration {
	public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }
}

public class MapperRegistry {
	public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }
}

上面经过一系列调用然后从knownMappers中取出传入进行的接口类,获取到接口类的代理工厂类。然后会执行mapperProxyFactory.newInstance(sqlSession);,具体的代码如下:

public class MapperProxyFactory<T> {
	public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
}

这个时候会创建MapperProxy类,将sqlSessionmapperInterfacemethodCache参数传入进去。点进去发现就是进行了一些赋值。这个时候重点就是newInstance(mapperProxy)方法,开始进行代理,用的jdk的动态代理。具体的代码如下:

public class MapperProxyFactory<T> {
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, 
                                      mapperProxy);
  }
}

到此整个代理就创建完成了。这个时候我们mapper中的查询,就是调用代理类中的查询,让我们先来看看第一种只有#的查询的SQL语句的执行流程,具体的代码如下:

public class MapperProxy<T> implements InvocationHandler, Serializable {
	@Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        //你是不是调用的Object默认的方法
        return method.invoke(this, args);
      } else if (method.isDefault()) {
        //对于默认方法的处理
        if (privateLookupInMethod == null) {
          return invokeDefaultMethodJava8(proxy, method, args);
        } else {
          return invokeDefaultMethodJava9(proxy, method, args);
        }
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }
}

走来会判断你调用的是不是Object中的方法,如果是,就直接执行。还有就是判断是不是接口中的默认方法,如果是,就直接执行。很显然,这儿不是的,我们继续往下看。会执行cachedMapperMethod(method);方法,这个方法是查看缓存中有没有数据,有的话,直接从缓存中去。没有的话,就创建一个新的MapperMethod,这个MapperMethod对象创建过程我们要看下大概的流程,因为后面在执行的时候有用到,具体代码如下:

public class MapperMethod {
	public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
  }
}
public class MapperMethod {
	public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
    //selectById
    final String methodName = method.getName();
    //com.DemoMapper
    final Class<?> declaringClass = method.getDeclaringClass();
    //根据methodName和declaringClass取出对应的MappedStatement对象
    MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
                                                configuration);
    if (ms == null) {
      if (method.getAnnotation(Flush.class) != null) {
        name = null;
        type = SqlCommandType.FLUSH;
      } else {
        throw new BindingException("Invalid bound statement (not found): "
                                   + mapperInterface.getName() + "." + methodName);
      }
    } else {
      //name=com.DemoMapper.selectById
      name = ms.getId();
      //Select
      type = ms.getSqlCommandType();
      if (type == SqlCommandType.UNKNOWN) {
        throw new BindingException("Unknown execution method for: " + name);
      }
    }
  }
}
public class MapperMethod {
  public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
    Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
    //设置返回值的类型
    if (resolvedReturnType instanceof Class<?>) {
      this.returnType = (Class<?>) resolvedReturnType;
    } else if (resolvedReturnType instanceof ParameterizedType) {
      this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
    } else {
      this.returnType = method.getReturnType();
    }
    //如果返回的是空,returnsVoid就为true
    this.returnsVoid = void.class.equals(this.returnType);
    //如果返回的是集合或者数据,returnsMany就为true
    this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || 
      this.returnType.isArray();
    //返回的是否是游标对象
    this.returnsCursor = Cursor.class.equals(this.returnType);
    //返回的是否是Optional对象
    this.returnsOptional = Optional.class.equals(this.returnType);
    this.mapKey = getMapKey(method);
    //返回的是否是map
    this.returnsMap = this.mapKey != null;
    this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
    this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
    this.paramNameResolver = new ParamNameResolver(configuration, method);
  }
}

上面的代码主要讲了下MapperMethod对象的创建,对一些重要的参数进行创建,我们继续跟进mapperMethod.execute(sqlSession, args);方法,具体的代码如下:

public class MapperMethod {
	public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
          if (method.returnsOptional()
              && (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName()
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }
}

这个时候走来会判断是什么操作,很明显这儿是select查询的操作。由于我们返回的参数是List,所以这儿的返回类型是returnsMany,所以会执行executeForMany(sqlSession, args);的方法,具体的代码如下:

public class MapperMethod {
	private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
    List<E> result;
    //参数的获取和转换,这儿获取的是1
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      result = sqlSession.selectList(command.getName(), param, rowBounds);
    } else {
      result = sqlSession.selectList(command.getName(), param);
    }
    // issue #510 Collections & arrays support
    if (!method.getReturnType().isAssignableFrom(result.getClass())) {
      if (method.getReturnType().isArray()) {
        return convertToArray(result);
      } else {
        return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
      }
    }
    return result;
  }
}

这儿走来会调用method.convertArgsToSqlCommandParam(args);方法,这个是获取参数,由于这儿我们只有一个参数,我们就不细说,在后面第二种查询的时候,我会细说这个参数的获取和转换。然后判断有没有分页的参数,由于mybatis自带的分页查询不太好用,我们不会说,后面可能会讲分页插件。所以这儿会直接执行sqlSession.selectList(command.getName(), param);方法,具体的代码如下:

public class DefaultSqlSession implements SqlSession {
	@Override
  public <E> List<E> selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
  }
  @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();
    }
  }
}

上面的代码获取对应的MappedStatement对象,然后会执行executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);方法,在执行这个方法之前,先执行了wrapCollection(parameter)方法,对参数进行了一些包装,我们跟进去看看具体的代码。

public class DefaultSqlSession implements SqlSession {
	private Object wrapCollection(final Object object) {
    if (object instanceof Collection) {
      StrictMap<Object> map = new StrictMap<>();
      map.put("collection", object);
      if (object instanceof List) {
        map.put("list", object);
      }
      return map;
    } else if (object != null && object.getClass().isArray()) {
      StrictMap<Object> map = new StrictMap<>();
      map.put("array", object);
      return map;
    }
    return object;
  }
}

上面的代码判断是不是集合和数组,如果是集合或者是数组,就会将这值放到StrictMap对象中去。这是一个Map对象。这个时候看看executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);具体代码,具体的代码如下:

public class CachingExecutor implements Executor {
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    //确定缓存的key
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
}

这个时候走来会执行ms.getBoundSql(parameterObject);方法,具体的代码如下:

public final class MappedStatement {
	public BoundSql getBoundSql(Object parameterObject) {
    //获取对应的查询语句
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    //获取查询所需要的参数的列表
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.isEmpty()) {
      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;
  }
}

走来先获取对应的查询列表和查询所需要的参数的列表,如果查询的所需要的参数为空的话,就不需要绑定参数了,直接创建BoundSql对象。如果不为空,那么就需要遍历对应的参数。获取对应的ResultMap,很显然这儿获取的是空的。然后返回到最初的执行的代码地方,会执行查询缓存的方法,这儿不讲,后面会写一篇博客讲,然后会执行query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);方法,我们继续跟进代码,具体的代码如下:

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

走来先获取缓存中的数据,很明显这儿是第一次查询,所以缓存中数据也是空,所以会执行下面的方法delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);具体的代码如下:

public abstract class BaseExecutor implements Executor {
	@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.");
    }
    //判断queryStack是否等于0,判断是否需要刷新缓存,都是前面判断的,如果是select就不需要更新缓存
    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;
  }
}

上面判断数据连接是否关闭,同时判断queryStack是否等于0和判断是否要刷新缓存,如果都成立的话,直接调用clearLocalCache();方法清楚缓存。然后从缓存中去这个查询的看看有没有值,很明显这儿是空,然后会执行queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);方法,具体的代码如下:

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

走来先将这个key存入缓存,占一个键,然后执行doQuery(ms, parameter, rowBounds, resultHandler, boundSql);方法,具体的代码如下,执行完后先清楚,然后再将查询到list存入到缓存中去。

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());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
}

这个时候终于看到了jdbc代码了,那么具体的参数的绑定在什么地方呢?让我们继续看prepareStatement(handler, ms.getStatementLog());方法中代码,具体如下:

public class SimpleExecutor extends BaseExecutor {
  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }
}

public class RoutingStatementHandler implements StatementHandler {
	@Override
  public void parameterize(Statement statement) throws SQLException {
    delegate.parameterize(statement);
  }
}	

public class PreparedStatementHandler extends BaseStatementHandler {
	@Override
  public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }
}

public class DefaultParameterHandler implements ParameterHandler {
	@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;
          //propertyName=id
          String propertyName = parameterMapping.getProperty();
          //判断是否有附加参数
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) { //值为1
            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
          JdbcType jdbcType = parameterMapping.getJdbcType();
          //如果两个值都为空的话,设置jdbcType类型为OTHER
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            //调用底层的JDBC设置值的方法
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException | SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }
}

上面将参数的值取出来,进行一系列处理,最终调用typeHandler.setParameter(ps, i + 1, value, jdbcType);方法,这个方法就是调用底层JDBC的设置的方法。至此参数的绑定结束。最后执行query方法。具体代码如下:

public class RoutingStatementHandler implements StatementHandler {	
	@Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    return delegate.query(statement, resultHandler);
  }
}

public class PreparedStatementHandler extends BaseStatementHandler {
	@Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    //调用JDBC的底层的查询操作
    ps.execute();
    return resultSetHandler.handleResultSets(ps);
  }
}

我们终于看到了JDBC的底层调用了,这个时候还剩一个结果集的封装,这个时候我们需要看resultSetHandler.handleResultSets(ps);方法,具体的代码如下:

public class DefaultResultSetHandler implements ResultSetHandler {
	@Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List<Object> multipleResults = new ArrayList<>();

    int resultSetCount = 0;
    //创建ResultSet的包装类
    ResultSetWrapper rsw = getFirstResultSet(stmt);
		
    //获取对应的resultMaps
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    //校验对应的长度
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      //获取对应的resultMap
      ResultMap resultMap = resultMaps.get(resultSetCount);
      //执行结果集的处理方法
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }

    return collapseSingleResultList(multipleResults);
  }
}

上面的代码进过一系列的调用最终会调用handleResultSet(rsw, resultMap, multipleResults, null);方法,具体的代码如下:

public class DefaultResultSetHandler implements ResultSetHandler {
	private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
    try {
      //这个不会执行,由于传进来的就是null
      if (parentMapping != null) {
        handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
      } else {
        //第一次进来为空
        if (resultHandler == null) {
          //创建DefaultResultHandler
          DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
          handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
          multipleResults.add(defaultResultHandler.getResultList());
        } else {
          handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
        }
      }
    } finally {
      // issue #228 (close resultsets)
      closeResultSet(rsw.getResultSet());
    }
  }
}

然后会执行handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);方法,具体的代码如下:

public class DefaultResultSetHandler implements ResultSetHandler {
	public 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);
    }
  }
}

这个时候会执行handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);方法,我们继续跟进去查看代码,具体代码如下:

public class DefaultResultSetHandler implements ResultSetHandler {
	private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
    //创建默认的DefaultResultContext
    DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
    //获取对应ResultSet对象
    ResultSet resultSet = rsw.getResultSet();
    //分页跳过
    skipRows(resultSet, rowBounds);
    while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
      Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
      storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
    }
  }
}

上面的代码会执行resolveDiscriminatedResultMap(resultSet, resultMap, null);方法,具体的代码如下:

public class DefaultResultSetHandler implements ResultSetHandler {
	public ResultMap resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix) throws SQLException {
    Set<String> pastDiscriminators = new HashSet<>();
    Discriminator discriminator = resultMap.getDiscriminator();
    while (discriminator != null) {
      final Object value = getDiscriminatorValue(rs, discriminator, columnPrefix);
      final String discriminatedMapId = discriminator.getMapIdFor(String.valueOf(value));
      if (configuration.hasResultMap(discriminatedMapId)) {
        resultMap = configuration.getResultMap(discriminatedMapId);
        Discriminator lastDiscriminator = discriminator;
        discriminator = resultMap.getDiscriminator();
        if (discriminator == lastDiscriminator || !pastDiscriminators.add(discriminatedMapId)) {
          break;
        }
      } else {
        break;
      }
    }
    return resultMap;
  }
}

然后返回resultMap,这个时候会执行getRowValue(rsw, discriminatedResultMap, null);方法,将查询到的列和值封装成一个hashMap返回。然后执行storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);方法。具体的代码如下:

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

进行了一些的数据的封装,然后返回,这个时候这个结果集就被包装成指定的对象,并且存入到multipleResults变量中去。然后关闭结果集,有点类似JDBC中关闭结果集。让我们返回到最初代码执行的地方,具体的代码如下:

public class DefaultResultSetHandler implements ResultSetHandler {
	@Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List<Object> multipleResults = new ArrayList<>();

    int resultSetCount = 0;
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

    //获取resultSets
    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }

    return collapseSingleResultList(multipleResults);
  }
}

这个时候这个while执行结束了,然后获取resultSets的值为null,这个时候下面的if判断不会执行,然后执行collapseSingleResultList(multipleResults);方法,将刚才包装好的结果集返回。然后返回到执行的代码的地方,这个时候会执行关闭Statement的代码,也是JDBC中的代码。最终返回到如下到代码地方。

public abstract class BaseExecutor implements Executor {
	@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.");
    }
    //判断queryStack是否等于0,判断是否需要刷新缓存,都是前面判断的,如果是select就不需要更新缓存
    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 {
      //查询到栈减1
      queryStack--;
    }
    if (queryStack == 0) { //=0
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }
}

至此整个查询就结束了,就直接返回了。

我们再来看第二种的语句的执行流程,主要是$和动态标签,在这我们不会详细的介绍,这儿只会介绍不同的地方。

由于刚才我们传入的参数只有一个,所以直接返回一个,这儿有两个参数,所以这儿参数的转换是不同,具体的代码如下:

public class ParamNameResolver {
  public Object getNamedParams(Object[] args) {
    //获取参数的长度,这儿的长度是2
    final int paramCount = names.size();
    if (args == null || paramCount == 0) {
      return null;
    } else if (!hasParamAnnotation && paramCount == 1) {//判断参数的长度是不是1同时也没有加上注解
      return args[names.firstKey()];//这个是我们上一个满足的条件,直接返回参数名的值
    } else {
      final Map<String, Object> param = new ParamMap<>();
      int i = 0;
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
        //names : arg0,arg1
        // key:arg0 value:Bob;key:arg1 value:Ruby
        param.put(entry.getValue(), args[entry.getKey()]);
        // add generic param names (param1, param2, ...)
        final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
        // ensure not to overwrite parameter named with @Param
        if (!names.containsValue(genericParamName)) {
          //key:param1 value:Bob; key:param value:Ruby
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      return param;
    }
  }
}

上面的代码判断参数的长度是不是1,如果是1,直接将这个值进行返回,也不用键,因为只有一个值,所以不存在对应的键和对应的值。如果是多个参数,就会动态的生成。例如我们这儿传入的参数是字符串Bob和字符串Ruby,这儿就会往Map中添加key:arg0,value:Bob;key:arg1,value:Ruby;key:param1,value:Bob;key:param2,value:Ruby

由于第二个查询的语句有动态的查询的语句,所以当中getBoundSql方法的执行的流程和原来的静态的代码不一样,具体调用的代码如下:

public class DynamicSqlSource implements SqlSource {	
	@Override
  public BoundSql getBoundSql(Object parameterObject) {
    //获取对应的参数
    DynamicContext context = new DynamicContext(configuration, parameterObject);
    //调用执行apply的方法
    rootSqlNode.apply(context);
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    context.getBindings().forEach(boundSql::setAdditionalParameter);
    return boundSql;
  }
}

上面的代码走来会获取指定的参数,然后调用apply方法。这个时候会对动态标签中的数据进行解析,同时也会讲if标签中的参数也给封装进去,最后的SQL的语句如下:

select * from mybooks
WHERE 1=1
and author = Bob
and title = #{param2} 

所以可以得知$标签中参数会在这儿替换掉。然后会执行sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());方法将#{}替换成?,同时将这个要绑定的参数的值封装到这个对象中去。具体的代码如下:

public class SqlSourceBuilder extends BaseBuilder {
	public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
    ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
    GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
    String sql = parser.parse(originalSql);
    return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
    }
}

这个时候SQL语句就会变成下面这样

select * from mybooks
WHERE 1=1
and author = Bob
and title = ?

后面的执行操作就是一样的了。下篇博客会讲下执行流程中增删改。由于这篇博客没有参数names在哪生成的。后面的博客后有讲到。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值