MyBatis源码学习(六)——在代码中调用Mapper接口时发生了什么

本文深入探讨MyBatis框架的执行流程,从Mapper接口调用到查询结果封装,详细解析了MyBatis如何处理SQL执行、参数转换、结果映射等关键步骤。

小结:

1,开始,MapperProxy.invoke().

2,创建MapperMethod。包括创建SqlCommand,封装sql,创建MethodSignature,封装方法参数。

3,MapperMethod.execute()

4,区分增删改查,查询时,统一调用DefaultSqlSession.selectList()

5,CachingExecutor.query()

6,SimpleExecutor.doQuery()。创建StatementHandler,确定statementType:STATEMENT,PREPARED,CALLABLE。创建ResultSetHandler。

7,SimpleExecutor创建PreparedStatement。

8,StatementHandler.query()。调用PreparedStatement.execute(),查询数据。

9,DefaultResultSetHandler.handleResultSets(),开始封装数据。

10,DefaultResultSetHandler创建DefaultResultHandler。rowBounds生效。

11,创建目标类的实例。

12,自动匹配数据库字段和目标类属性,并赋值。

13,按照ResultMap的配置匹配数据库字段和目标类属性,并赋值。

14,对配置的ResultSets进行循环处理。

15,结束调用,返回指定Java实例。

 

正文:

在之前创建Mapper动态代理的文章中,可以看到最后Mybatis给Mapper接口创建的代理类是MapperProxy类,所以,当我们在代码中实际调用Mapper接口的某方法时,调用的实际上是MapperProxy类的invoke方法:

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, args);
    } else if (isDefaultMethod(method)) {
      return invokeDefaultMethod(proxy, method, args);
    }
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  return mapperMethod.execute(sqlSession, args);
}

方法中出现了几个判断,第一个:

if (Object.class.equals(method.getDeclaringClass()))

意思是方法所属的类是Object,实际上方法所属的类都是Mapper接口类,所以这里的判断是false。

下一个判断:

else if (isDefaultMethod(method))

意思是调用的方法是否是默认方法,一般来说我们在Mapper接口类中的方法都是接口方法,所以这里的判断往往都是false。

 

不过我们依然可以看一下这个isDefaultMethod()方法:

/**
 * Backport of java.lang.reflect.Method#isDefault()
 */
private boolean isDefaultMethod(Method method) {
  return (method.getModifiers()
      & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC
      && method.getDeclaringClass().isInterface();
}

mybatis特意备注了这是对java中源生的Method.isDefault()方法的补丁,不过只是去掉了一个括号,因为java源生的isDefault()方法是这样的:

public boolean isDefault() {
    // Default methods are public non-abstract instance methods
    // declared in an interface.
    return ((getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) ==
            Modifier.PUBLIC) && getDeclaringClass().isInterface();
}

大概意思就是,方法所属的必须是接口类,而且方法要有public修饰符,没有ABSTRACT修饰,没有STATIC修饰。注意,接口方法即使没有ABSTRACT修饰也算是ABSTRACT的。

这里用到了一些按位与和按位或的逻辑:

Modifier.ABSTRACT是1024

Modifier.STATIC是8

Modifier.PUBLIC是1

所以方法的modifiers想要和他们按位与而且得到Modifier.PUBLIC(也就是1),方法就不能是ABSTRACT的,不能是STATIC的,同时必须是PUBLIC的。

 

继续看上面invoke()方法的代码,经过了两个结果是false的判断之后,代码来到这一行:

final MapperMethod mapperMethod = cachedMapperMethod(method);

得到了Mapper的一个方法的封装MapperMethod,看一下cachedMapperMethod()方法:

private MapperMethod cachedMapperMethod(Method method) {
  MapperMethod mapperMethod = methodCache.get(method);
  if (mapperMethod == null) {
    mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
    methodCache.put(method, mapperMethod);
  }
  return mapperMethod;
}

方法很简单,从缓存中获取MapperMethod,没有就新建一个,然后放到缓存里,看一下MapperMethod的构造:

public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
  this.command = new SqlCommand(config, mapperInterface, method);
  this.method = new MethodSignature(config, mapperInterface, method);
}

就两个参数,SqlCommandMethodSignature

SqlCommand是MapperMethod的一个内部类,封装了方法要用到的sql等。

看一下SqlCommand的构造:

public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
  final String methodName = method.getName();
  final Class<?> declaringClass = method.getDeclaringClass();
  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 = ms.getId();
    type = ms.getSqlCommandType();
    if (type == SqlCommandType.UNKNOWN) {
      throw new BindingException("Unknown execution method for: " + name);
    }
  }
}

可以看到,构造中调用了resolveMappedStatement()方法,这个方法用来把sql封装成MappedStatement,看一下其代码:

private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
    Class<?> declaringClass, Configuration configuration) {
  String statementId = mapperInterface.getName() + "." + methodName;
  if (configuration.hasStatement(statementId)) {
    return configuration.getMappedStatement(statementId);
  } else if (mapperInterface.equals(declaringClass)) {
    return null;
  }
  for (Class<?> superInterface : mapperInterface.getInterfaces()) {
    if (declaringClass.isAssignableFrom(superInterface)) {
      MappedStatement ms = resolveMappedStatement(superInterface, methodName,
          declaringClass, configuration);
      if (ms != null) {
        return ms;
      }
    }
  }
  return null;
}

首先方法拼了一个statementId,大概长这样:

com.test.OrderMapper.getOrder

然后从SqlSession的configuration中查询是否已存在,已存在则直接返回,否则走父接口,还是从configuration中查询并返回。

 

回到SqlCommand的构造,执行完resolveMappedStatement()方法,如果没拿到方法对应的MappedStatement,则看一下此方法是否被@flush注解标识,被此注解标识的方法是用来执行sqlSession.flushStatements()用的,这种方法在Mapper.xml里面不需要有对应的动态sql配置。如果在configuration里没有,又不是@flush方法,则抛出BindingException,代表在Mapper接口类里定义了方法但在Mapper.xml里面没有对应sql的异常。

如果从configuration里拿到了对应的MappedStatement,则初始化SqlCommand的name参数为MappedStatement的id,type参数为MappedStatement的SqlCommandType,也就是Mapper.xml里对应的INSERT, UPDATE, DELETE, SELECT等。

 

初始化完SqlCommand,下面初始化MappedStatement的第二个参数MethodSignature,这个参数是用来记录方法的参数,返回值等参数,其构造是这样的:

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();
  }
  this.returnsVoid = void.class.equals(this.returnType);
  this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
  this.returnsCursor = Cursor.class.equals(this.returnType);
  this.mapKey = getMapKey(method);
  this.returnsMap = this.mapKey != null;
  this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
  this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
  this.paramNameResolver = new ParamNameResolver(configuration, method);
}

可见MethodSignature记录的参数有:

  • resolvedReturnType,返回值类型
  • returnsVoid,是否是void返回值
  • returnsMany,是否是多值查询
  • returnsCursor,是否是游标查询
  • mapKey,mapKey的值
  • returnsMap,是否map查询
  • rowBoundsIndex,标识方法参数列表中RowBounds是第几个参数
  • resultHandlerIndex,标识方法参数列表中ResultHandler是第几个参数
  • paramNameResolver,参数解析器
  • RowBounds是分页用的,不过是把数据全查出来然后取得其中的一部分并返回,数据量小的时候用一下还可以,数据量大的场景还是不要用了。
  • resultHandler是结果处理器。

 

MethodSignature初始化完成后,MapperMethod实例就创建完成了,回到MapperProxy的invoke()方法:

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, args);
    } else if (isDefaultMethod(method)) {
      return invokeDefaultMethod(proxy, method, args);
    }
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  return mapperMethod.execute(sqlSession, args);
}

创建完MapperMethod后,就开始执行MapperMethod的execute()方法:

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

可以看到execute()方法中根据SqlCommand的sql类别调用了不同的方法,下面举一个例子跟踪一下代码,比如要执行的是Mapper接口类中的如下方法:

String getOrder(Integer company,String code);

这是一个简单的接口方法,没有使用任何注解,两个参数,返回字符串类型。

这是一个查询的方法,所以进入SELECT的case中,根据方法返回值类型,分别有返回void时的方法,返回多值的方法,返回Map的方法等,而上面的方法会调用else里的这两行:

Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);

首先调用了method的convertArgsToSqlCommandParam()方法,这个方法的功能是把参数列表转换成一个Map格式,MapperMethod的convertArgsToSqlCommandParam()方法:

public Object convertArgsToSqlCommandParam(Object[] args) {
  return paramNameResolver.getNamedParams(args);
}

这里的paramNameResolver是MapperMethod的ParamNameResolver类型参数,它的getNamedParams()方法用来把参数列表转为方便使用的格式:

public Object getNamedParams(Object[] args) {
  final int paramCount = names.size();
  if (args == null || paramCount == 0) {
    return null;
  } else if (!hasParamAnnotation && paramCount == 1) {
    return args[names.firstKey()];
  } else {
    final Map<String, Object> param = new ParamMap<Object>();
    int i = 0;
    for (Map.Entry<Integer, String> entry : names.entrySet()) {
      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)) {
        param.put(genericParamName, args[entry.getKey()]);
      }
      i++;
    }
    return param;
  }
}

逻辑比较简单,分了三种情况:

1,没有参数,直接返回null。

2,就一个参数,而且没写@param注解,就直接返回那个参数。

3,其他复杂场景,创建了一个Map<String, Object>类型的map,把参数名作为key,值作为value放进去,另外还放了一组键值对,key是param1,param2等,对应参数在参数列表中的位置,value还是参数值。最后把map返回。本例会执行此流程。

 

回到MapperMethod的execute()方法,执行完convertArgsToSqlCommandParam()方法后,开始执行:

result = sqlSession.selectOne(command.getName(), param);

sqlSession是由SqlSessionTemplate实现的 ,其selectOne()方法是这样的:

public <T> T selectOne(String statement, Object parameter) {
    return this.sqlSessionProxy.selectOne(statement, parameter);
}

SqlSessionTemplate的sqlSessionProxy是对DefaultSqlSession的代理,当执行selectOne()方法时,会被SqlSessionTemplate的内部拦截器拦截,调用它的invoke方法:

private class SqlSessionInterceptor implements InvocationHandler {
    private SqlSessionInterceptor() {
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);

        Object unwrapped;
        try {
            Object result = method.invoke(sqlSession, args);
            if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
                sqlSession.commit(true);
            }

            unwrapped = result;
        } catch (Throwable var11) {
            unwrapped = ExceptionUtil.unwrapThrowable(var11);
            if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                sqlSession = null;
                Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
                if (translated != null) {
                    unwrapped = translated;
                }
            }

            throw (Throwable)unwrapped;
        } finally {
            if (sqlSession != null) {
                SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
            }

        }

        return unwrapped;
    }
}

invoke()方法先是获取了sqlSession参数,实际由DefaultSqlSession来实现,然后调用了

Object result = method.invoke(sqlSession, args);

这里的method就是Java源生的Method类,综上,方法执行的是DefaultSqlSession的selectOne()方法:

@Override
public <T> T selectOne(String statement, Object parameter) {
  // Popular vote was to return null on 0 results and throw exception on too many.
  List<T> list = this.<T>selectList(statement, parameter);
  if (list.size() == 1) {
    return list.get(0);
  } else if (list.size() > 1) {
    throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
  } else {
    return null;
  }
}

可见selectOne()方法中也是用selectList()方法实现的,而且如果查询到的结果数大于1,会抛出异常。所以看一下selectList()方法的代码:

@Override
public <E> List<E> selectList(String statement, Object parameter) {
  return this.selectList(statement, parameter, RowBounds.DEFAULT);
}

可见,没有设置RowBounds时,用的是默认的RowBounds,他的offset是0,limit是Integer.MAX_VALUE。继续深入方法:

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

方法先从configuration中拿到MappedStatement,然后执行executor.query()方法,注意一下第二个参数,由wrapCollection()方法完成:

private Object wrapCollection(final Object object) {
  if (object instanceof Collection) {
    StrictMap<Object> map = new StrictMap<Object>();
    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<Object>();
    map.put("array", object);
    return map;
  }
  return object;
}

意思是如果参数Object是集合类型或者数组时,把参数用Map封装了一层,本例中的参数Object是个Map类型,所以直接返回了这个Map。

另外看一下query()方法的第四个参数:Executor.NO_RESULT_HANDLER,意思是没有指定具体的ResultHandler。

下面看一下executor.query()方法,这里的executor是CachingExecutor类的代理,这个query()方法会被PlugIn类拦截,PlugIn的 invoke()方法不贴了,方法中的是调用CachingExecutor的query ()方法:

@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, boundSql);
      @SuppressWarnings("unchecked")
      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);
}

查询的方式是先从缓存中取,取不到就查询,查询是调用:

delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

delegate来自CachingExecutor类的

private final Executor delegate;

由SimpleExecutor类实现,所以程序会执行SimpleExecutor类的query()方法,具体的实现在SimpleExecutor类的父类BaseExecutor中:

@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.");
  }
  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;
}

方法首先尝试从localCache中得到结果,这里的localCache是mybatis的一级缓存,如果缓存中没有,则从数据库中查询,也就是执行这一行:

list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

看一下queryFromDatabase()方法的代码:

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

方法一开始往缓存中添加了一个占位符,然后开始执行doQuery()方法,注意此处代码虽然在BaseExecutor中,实际执行此方法的对象是属于SimpleExecutor类,所以要执行的doQuery方法在SimpleExecutor类中:

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

首先用configuration创建一个StatementHandler,这是mybatis和数据交互的重要组件,此处由RoutingStatementHandler类实现,可以看一下它的构造:

public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

  switch (ms.getStatementType()) {
    case STATEMENT:
      delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    case PREPARED:
      delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    case CALLABLE:
      delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    default:
      throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
  }

}

可见,根据MappedStatement中的statementType,决定了RoutingStatementHandler中的delegate参数的实现类。

 

关于MappedStatement中的statementType,分为三种:

1,STATEMENT。直接操作sql,不进行预编译,获取数据。比如在Mapper.xml文件中这么写:${id}。

2,PREPARED。参数进行预编译,获取数据。比如在Mapper.xml文件中这么写:#{id}。

3,CALLABLE。执行存储过程。

 

回到SimpleExecutor的doQuery()方法,在创建StatementHandler后,调用prepareStatement()方法拼装出了Java的Statement,prepareStatement()方法的代码:

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

先获得Connection,然后调用handler的prepare()方法,这里handler就是上层代码创建的RoutingStatementHandler,它的prepare()方法:

@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
  return delegate.prepare(connection, transactionTimeout);
}

调用的是delegate参数的prepare()方法,前面说到delegate的实现类由MappedStatement中的statementType决定,我们看一下PREPARED(预编译)这种情况下的代码,此时delegate由PreparedStatementHandler实现,其prepare()方法是由PreparedStatementHandler的父类BaseStatementHandler实现的:

@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
  ErrorContext.instance().sql(boundSql.getSql());
  Statement statement = null;
  try {
    statement = instantiateStatement(connection);
    setStatementTimeout(statement, transactionTimeout);
    setFetchSize(statement);
    return statement;
  } catch (SQLException e) {
    closeStatement(statement);
    throw e;
  } catch (Exception e) {
    closeStatement(statement);
    throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
  }
}

通过调用instantiateStatement()方法,得到Statement,instantiateStatement()方法的代码在PreparedStatementHandler中:

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

经过无数层的调用,我们终于来到了根据Connection创建Statement的代码,终于有比较眼熟的代码了,当我们使用源生的访问数据库的代码时,往往也是这么写的。

方法中的逻辑区分了三种场景:

1,配置了主键生成器的情况。

2,ResultSetType不为空的情况。

3,其他。就是简单的生成一个Statement。

 

于是,我们终于得到了Statement。

方法回到SimpleExecutor的doQuery()方法:

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

经过调用prepareStatement()方法获得了Statement后,调用了handler的query()方法,此时的handler是RoutingStatementHandler,它的query()方法:

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

我们看一下PREPARED(预编译)这种情况下的代码,delegate由PreparedStatementHandler实现:

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  ps.execute();
  return resultSetHandler.<E> handleResultSets(ps);
}

我们终于用PreparedStatement执行了execute()方法,完成了数据库的查询,在源生的Java访问数据库的代码中,我们往往也是这么写的。

执行完查询后,用resultSetHandler的handleResultSets()方法对返回值进行了封装,把Object的查询结果转换成我们定义的Java类。

默认情况下resultSetHandler由DefaultResultSetHandler类来实现,他的handleResultSets()方法如下:

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

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

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

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

首先,根据:

ResultSetWrapper rsw = getFirstResultSet(stmt);

这行代码得到了的查询结果中的第一条,是ResultSet的一个封装类。

下一步,针对<select>中配置的resultMaps和resultSets,分别进行循环和赋值。

resultMaps代表了数据库字段和Java实例属性的对应关系,resultSets则是在一个查询中对多个结果集进行映射时会用到的配置。

下面主要看一下对resultMaps的处理方式,有一个循环用以处理查询结果:

while (rsw != null && resultMapCount > resultSetCount) {
  ResultMap resultMap = resultMaps.get(resultSetCount);
  handleResultSet(rsw, resultMap, multipleResults, null);
  rsw = getNextResultSet(stmt);
  cleanUpAfterHandlingResultSet();
  resultSetCount++;
}

其中resultSetCount相当于查询结果的下标,循环处理,循环中的这一行:

handleResultSet(rsw, resultMap, multipleResults, null);

就是对处理结果进行封装。

下面这行:

rsw = getNextResultSet(stmt);

是获得查询结果中的下一条。

 

下面看一下handleResultSet()是如何转换查询结果的:

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
  try {
    if (parentMapping != null) {
      handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
    } else {
      if (resultHandler == null) {
        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());
  }
}

根据上一级的调用信息,parentMapping参数传的就是null,所以逻辑直接进入else的部分。

在没有配置resultHandler的情况下,代码会创建一个默认的resultHandler,由DefaultResultHandler实现,DefaultResultHandler中维护了一个list参数,用于存放转换后的对象列表,在对象赋值完成后,这个列表会被放进multipleResults中,multipleResults也就是最终要返回的列表。

DefaultResultHandler中的list参数:

private final List<Object> list;

一个简单的List参数。

DefaultResultHandler的构造方法:

public DefaultResultHandler(ObjectFactory objectFactory) {
  list = objectFactory.create(List.class);
}

其实就是创建了一个空的List。

 

创建好DefaultResultHandler,调用handleRowValues()方法:

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

根据ResultMap中是否有嵌套映射,把逻辑分为两部分,如果没有嵌套,则直接调用handleRowValuesForSimpleResultMap()方法:

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

可以看到,rowBounds参数在这个方法中生效了:

skipRows(rsw.getResultSet(), rowBounds);

这个方法就是从查询结果的ResultSet中跳过某些结果。可见,rowBounds的策略是查询出所有结果之后再进行截取。

 

接下来进入一个循环,循环条件中的shouldProcessMoreRows()方法就是结合rowBounds的数据,判断是否还需要继续循环下面的ResultSet元素。

下面看一下循环中的代码,

1,首先是这一行:

ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);

这行调用的resolveDiscriminatedResultMap()方法代表mybatis的鉴别器在此处生效。,

2,下一行:

Object rowValue = getRowValue(rsw, discriminatedResultMap);

这行调用的getRowValue()方法就是实际的类型转换方法,方法代码如下:

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
  final ResultLoaderMap lazyLoader = new ResultLoaderMap();
  Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
  if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    final MetaObject metaObject = configuration.newMetaObject(rowValue);
    boolean foundValues = this.useConstructorMappings;
    if (shouldApplyAutomaticMappings(resultMap, false)) {
      foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
    }
    foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
    foundValues = lazyLoader.size() > 0 || foundValues;
    rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
  }
  return rowValue;
}

从代码逻辑上来看,先是调用createResultObject()方法创建目标类,然后调用applyAutomaticMappings()方法进行了一些自动映射的工作。

下面看一下createResultObject()方法:

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
  this.useConstructorMappings = false; // reset previous mapping result
  final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
  final List<Object> constructorArgs = new ArrayList<Object>();
  Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
  if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
    for (ResultMapping propertyMapping : propertyMappings) {
      // issue gcode #109 && issue #149
      if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
        resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
        break;
      }
    }
  }
  this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
  return resultObject;
}

从代码逻辑上来看,方法做了两件事,一是调用createResultObject()方法创建目标对象,二是根据ResultMap的配置,把目标类的字段和数据库的字段进行对应。

首先看一下createResultObject()方法:

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
    throws SQLException {
  final Class<?> resultType = resultMap.getType();
  final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
  final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
  if (hasTypeHandlerForResultObject(rsw, resultType)) {
    return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
  } else if (!constructorMappings.isEmpty()) {
    return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
  } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
    return objectFactory.create(resultType);
  } else if (shouldApplyAutomaticMappings(resultMap, false)) {
    return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);
  }
  throw new ExecutorException("Do not know how to create an instance of " + resultType);
}

方法首先得到了要转换的目标类,也就是这一行:

final Class<?> resultType = resultMap.getType();

然后按顺序判断4种情况:

1,存在适合的typeHandler类。

2,有参构造的映射。

3,接口或无参构造的映射。

4,有参构造的自动映射。

 

以第3种情况为例,返回自定义的JavaBean,有无参构造的情况,调用的是

return objectFactory.create(resultType);

这里的objectFactory是由DefaultObjectFactory类实现的,他的create()方法:

@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
  Class<?> classToCreate = resolveInterface(type);
  // we know types are assignable
  return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
}

第一行执行的resolveInterface()方法,是针对一些常用的接口类进行了类型的切换,方法的代码是这样的:

protected Class<?> resolveInterface(Class<?> type) {
  Class<?> classToCreate;
  if (type == List.class || type == Collection.class || type == Iterable.class) {
    classToCreate = ArrayList.class;
  } else if (type == Map.class) {
    classToCreate = HashMap.class;
  } else if (type == SortedSet.class) { // issue #510 Collections Support
    classToCreate = TreeSet.class;
  } else if (type == Set.class) {
    classToCreate = HashSet.class;
  } else {
    classToCreate = type;
  }
  return classToCreate;
}

可以看到,当目标类是List,Map等接口类时,方法选择了接口的某一个实现类作为新的目标类,以后用来生成实例。

  • 当目标接口是List时,将会返回ArrayList类,
  • 当目标接口是Map时,将会返回HashMap类,
  • 当目标接口是SortedSet时,将会返回TreeSet类,
  • 当目标接口是Set时,将会返回HashSet类。

 

回到create()方法,确定了目标类,就开始执行instantiateClass()方法创建目标类实例,方法代码是:

private  <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
  try {
    Constructor<T> constructor;
    if (constructorArgTypes == null || constructorArgs == null) {
      constructor = type.getDeclaredConstructor();
      if (!constructor.isAccessible()) {
        constructor.setAccessible(true);
      }
      return constructor.newInstance();
    }
    constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
    if (!constructor.isAccessible()) {
      constructor.setAccessible(true);
    }
    return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
  } catch (Exception e) {
    StringBuilder argTypes = new StringBuilder();
    if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
      for (Class<?> argType : constructorArgTypes) {
        argTypes.append(argType.getSimpleName());
        argTypes.append(",");
      }
      argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing ,
    }
    StringBuilder argValues = new StringBuilder();
    if (constructorArgs != null && !constructorArgs.isEmpty()) {
      for (Object argValue : constructorArgs) {
        argValues.append(String.valueOf(argValue));
        argValues.append(",");
      }
      argValues.deleteCharAt(argValues.length() - 1); // remove trailing ,
    }
    throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
  }
}

方法的逻辑中实际上区分了创建有参的构造的实例和无参的构造的实例,如果前面的代码是从无参构造的方法调用到这里,代码执行的就是if之中的逻辑,也就是这一段:

constructor = type.getDeclaredConstructor();
if (!constructor.isAccessible()) {
  constructor.setAccessible(true);
}
return constructor.newInstance();

是常规的用构造方法创建对象的代码。

 

至此,我们得到了目标类的一个实例,后面的逻辑将会是把这个实例进行赋值并返回。

随着调用栈出栈,代码回到DefaultResultSetHandler的createResultObject()方法:

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
  this.useConstructorMappings = false; // reset previous mapping result
  final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
  final List<Object> constructorArgs = new ArrayList<Object>();
  Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
  if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
    for (ResultMapping propertyMapping : propertyMappings) {
      // issue gcode #109 && issue #149
      if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
        resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
        break;
      }
    }
  }
  this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
  return resultObject;
}

刚刚我们已经从createResultObject()方法得到了目标类的实例,方法后面的代码实际上是对嵌套查询的处理,也就是这一段:

if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
  final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
  for (ResultMapping propertyMapping : propertyMappings) {
    // issue gcode #109 && issue #149
    if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
      resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
      break;
    }
  }
}

方法一开始从ResultMap中得到ResultMapping的列表,也就是我们平时在Mapper.xml文件中的<resultMap>中的这种配置:

<result column="order_code" property="orderCode" jdbcType="VARCHAR"/>

所表示的内容,然后根据propertyMapping是否有NestedQueryId,也就是嵌套查询的目标类id,如果有,需要重新创建一个目标类的代理。

 

调用栈继续出栈,来到DefaultResultSetHandler的getRowValue()方法:

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
  final ResultLoaderMap lazyLoader = new ResultLoaderMap();
  Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
  if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    final MetaObject metaObject = configuration.newMetaObject(rowValue);
    boolean foundValues = this.useConstructorMappings;
    if (shouldApplyAutomaticMappings(resultMap, false)) {
      foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
    }
    foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
    foundValues = lazyLoader.size() > 0 || foundValues;
    rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
  }
  return rowValue;
}

刚才通过调用createResultObject()方法得到了目标类的实例,还没赋值,方法后面的代码就是把查询结果的字段值赋值给实例的对应属性。

先是这一部分:

if (shouldApplyAutomaticMappings(resultMap, false)) {
  foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
}

这段是用mybatis的自动映射进行字段名匹配,也就是在Mapper.xml中使用:

<setting name="autoMappingBehavior" value="FULL" />

或者使用

autoMapping="true"

这种配置时生效的结果。

 

接下来是下一行:

foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;

这行代码就是进行数据库字段和返回值参数的映射和赋值,看一下applyPropertyMappings()方法的代码:

private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
    throws SQLException {
  final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
  boolean foundValues = false;
  final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
  for (ResultMapping propertyMapping : propertyMappings) {
    String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
    if (propertyMapping.getNestedResultMapId() != null) {
      // the user added a column attribute to a nested result map, ignore it
      column = null;
    }
    if (propertyMapping.isCompositeResult()
        || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
        || propertyMapping.getResultSet() != null) {
      Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
      // issue #541 make property optional
      final String property = propertyMapping.getProperty();
      if (property == null) {
        continue;
      } else if (value == DEFERED) {
        foundValues = true;
        continue;
      }
      if (value != null) {
        foundValues = true;
      }
      if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
        // gcode issue #377, call setter on nulls (value is not 'found')
        metaObject.setValue(property, value);
      }
    }
  }
  return foundValues;
}

方法一开始得到了两个列表:

final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);

是从配置的<ResultMap>中获得已经匹配的数据库字段。

然后:

final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();

是从配置的<ResultMap>中获得配置的<result>数据。

然后循环propertyMappings,在循环中调用prependPrefix()获得要匹配的数据库字段名,然后调用这一行:

Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);

获得查询结果中column字段的值,看一下getPropertyMappingValue()方法的代码:

private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
    throws SQLException {
  if (propertyMapping.getNestedQueryId() != null) {
    return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
  } else if (propertyMapping.getResultSet() != null) {
    addPendingChildRelation(rs, metaResultObject, propertyMapping);   // TODO is that OK?
    return DEFERED;
  } else {
    final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
    final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
    return typeHandler.getResult(rs, column);
  }
}

方法区分了嵌套查询,嵌套ResultSet,和无嵌套这三种场景,无嵌套时,方法从propertyMapping中得到了TypeHandler,不同类型的数据会使用不同的TypeHandler实现类,数字类型字段会用IntegerTypeHandler,他的getResult()方法在其父类BaseTypeHandler中:

@Override
public T getResult(ResultSet rs, String columnName) throws SQLException {
  T result;
  try {
    result = getNullableResult(rs, columnName);
  } catch (Exception e) {
    throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set.  Cause: " + e, e);
  }
  if (rs.wasNull()) {
    return null;
  } else {
    return result;
  }
}

主要是其中的getNullableResult()方法,其代码实现在IntegerTypeHandler中:

@Override
public Integer getNullableResult(ResultSet rs, String columnName)
    throws SQLException {
  return rs.getInt(columnName);
}

很简单,调用的就是ResultSet.getInt()方法,从ResultSet中获得字段值。

于是,我们从查询结果中取出了字段的值。

 

调用栈出栈,代码回到DefaultResultSetHandler的applyPropertyMappings()方法:

private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
    throws SQLException {
  final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
  boolean foundValues = false;
  final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
  for (ResultMapping propertyMapping : propertyMappings) {
    String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
    if (propertyMapping.getNestedResultMapId() != null) {
      // the user added a column attribute to a nested result map, ignore it
      column = null;
    }
    if (propertyMapping.isCompositeResult()
        || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
        || propertyMapping.getResultSet() != null) {
      Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
      // issue #541 make property optional
      final String property = propertyMapping.getProperty();
      if (property == null) {
        continue;
      } else if (value == DEFERED) {
        foundValues = true;
        continue;
      }
      if (value != null) {
        foundValues = true;
      }
      if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
        // gcode issue #377, call setter on nulls (value is not 'found')
        metaObject.setValue(property, value);
      }
    }
  }
  return foundValues;
}

之前的代码已经通过调用:

Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);

得到了查询结果中的某个具体值,下面的:

final String property = propertyMapping.getProperty();

是得到返回类中的属性名。

最后调用:

metaObject.setValue(property, value);

完成对返回对象的属性赋值。同时,只要对象的任何一个属性被成功赋值,foundValues参数就会被设为true。

另外,metaObject在创建实例时把要返回的rowValue当成了自己的一个参数,所以,当metaObject被赋值时,rowValue也被赋值了。

 

调用栈继续出栈,回到DefaultResultSetHandler的getRowValue()方法:

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
  final ResultLoaderMap lazyLoader = new ResultLoaderMap();
  Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
  if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    final MetaObject metaObject = configuration.newMetaObject(rowValue);
    boolean foundValues = this.useConstructorMappings;
    if (shouldApplyAutomaticMappings(resultMap, false)) {
      foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
    }
    foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
    foundValues = lazyLoader.size() > 0 || foundValues;
    rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
  }
  return rowValue;
}

前面的代码已经通过调用applyPropertyMappings()方法给rowValue的每个变量赋了值。

在方法的最后还调用了这样一行代码:

rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;

这行代码中提到了configuration的一个变量:returnInstanceForEmptyRow,这个变量决定了当我们查不到数据的时候是返回空值对象还是返回null,returnInstanceForEmptyRow变量我们可以在Mybatis的配置文件中这么配置:

<settings>
  <setting name="returnInstanceForEmptyRow" value="true"/>
</settings>

 

调用栈继续出栈,来到了DefaultResultSetHandler的handleResultSets方法:

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

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

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

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

前面的代码通过对ResultMaps的处理,得到了要返回的查询结果multipleResults,下面是对配置的ResultSets进行循环处理,处理流程和ResultMaps的处理差不多。

在方法的最后,调用了这么一行:

return collapseSingleResultList(multipleResults);

方法的代码是这样的:

private List<Object> collapseSingleResultList(List<Object> multipleResults) {
  return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;
}

也就是说,如果multipleResults列表只有一个元素时,就直接返回这个元素(也是一个List)。

 

至此,我们成功的把查询结果封装成了我们配置的类型并得以返回,后面再经过几次调用栈的出栈,整个调用Mapper接口的流程就结束了。

(本文结束)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值