MyBatis原理分析之查询单个对象-2

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: MyBatis原理分析之查询单个对象-2

XMLLanguageDriver创建createParameterHandler代码如下:

@Ov@Override
  public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
  }


InterceptorChain.pluginAll

InterceptorChain内部维护了一个List<Interceptor> interceptors,pluginAll方法就是对目标对象层层代理。

publ public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }


我们可以看下mybatis中的Interceptor在这里插入代码片

package org.apache.ibatis.plugin;
import java.util.Properties;
public interface Interceptor {
  Object intercept(Invocation invocation) throws Throwable;
  Object plugin(Object target);
  void setProperties(Properties properties);
}


其提供了了plugin方法用来生成代理对象,intercept方法用来在执行目标方法前进行拦截处理。


如下所示,是mybatisplus中分页插件实现的plugin方法。其调用了Plugin.wrap(target, this)来为StatementHandler实例对象生成代理对象。


@Override
  public Object plugin(Object target) {
      if (target instanceof StatementHandler) {
          return Plugin.wrap(target, this);
      }
      return target;
  }

mybatis中的org.apache.ibatis.plugin.Plugin主要属性和方法如下所示:

public class Plugin implements InvocationHandler {
  private final Object target;
  private final Interceptor interceptor;
  private final Map<Class<?>, Set<Method>> signatureMap;
  private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
    this.target = target;
    this.interceptor = interceptor;
    this.signatureMap = signatureMap;
  }
  public static Object wrap(Object target, Interceptor interceptor) {
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
      if (methods != null && methods.contains(method)) {
        return interceptor.intercept(new Invocation(target, method, args));
      }
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }
//...
}


可以看到Plugin实现了InvocationHandler接口。其wrap方法为target生成了代理对象,当执行目标方法时,会首先触发Plugininvoke方法。在invoke方法里面调用了interceptorintercept方法。


newResultSetHandler

pub  public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
      ResultHandler resultHandler, BoundSql boundSql) {
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
  }


代码解释如下:

  • ① 创建DefaultResultSetHandler实例;
  • interceptorChain.pluginAll对resultSetHandler 进行层层代理包装,返回代理对象。



顺便看一下我们最后得到的StatementHandler


③ SimpleExecutor.prepareStatement

SimpleExecutor.prepareStatement(StatementHandler, Log) 方法源码如下,这里handler就是前面创建的RoutingStatementHandler .

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

代码解释如下:


① stmt = handler.prepare(connection, transaction.getTimeout());意识是使用StatementHandler 进行预处理获取Statement 对象。

② handler.parameterize(stmt)使用handler对①获取的Statement 对象进行参数解析。

RoutingStatementHandler交给了其装饰的对象PreparedStatementHandle处理,PreparedStatementHandle调用了其抽象父类BaseStatementHandler处理。

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

BaseStatementHandlerprepare方法

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

代码解释如下:

  • ① 实例化Statement对象;
  • ② 设置超时时间;
  • ③ 设置每次批量返回的结果行数


PreparedStatementHandlerinstantiateStatement方法

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


代码解释如下:


① 获取处理前的SQL,这时候带有占位符?

select id,last_name lastName,email,gender from tbl_employee where id = ?

② 如果KeyGenerator是Jdbc3KeyGenerator类型,则根据keyColumnNames 是否为null进行不同处理;

③ 如果ResultSetType不为null,则根据ResultSetType获取Statement对象;

④ 否则只根据SQL获取Statement对象。

如下图所示,这里获取的Statement对象也是一个代理对象,实际对象是com.mysql.jdbc.JDBC42PreparedStatement@4dbb42b7: select id,last_name lastName,email,gender from tbl_employee where id = ** NOT SPECIFIED **。


1e4ca0f2e04241a281e7344ff732e740.png


handler.parameterize(stmt)

如下所示,这里RoutingStatementHandler交给了其装饰对象PreparedStatementHandler去处理。


RoutingStatementHandler的parameterize方法如下:

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

PreparedStatementHandler的parameterize方法如下

@O@Override
public void parameterize(Statement statement) throws SQLException {
  parameterHandler.setParameters((PreparedStatement) statement);
}


这里parameterHandler如下所示:


DefaultParameterHandler的setParameters方法

@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 jdbcType = parameterMapping.getJdbcType();
        if (value == null && jdbcType == null) {
          jdbcType = configuration.getJdbcTypeForNull();
        }
        try {
          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);
        }
      }
    }
  }
}


代码解释如下:


① 根据boundSql获取parameterMappings进行遍历处理;

② 如果parameterMapping.getMode() == ParameterMode.OUT,直接跳过进行下次循环;

③ 获取propertyName和value;

④ typeHandler.setParameter(ps, i + 1, value, jdbcType)对本地循环的参数进行赋值

我们对第④步详细跟踪一下。默认情况下,如果不设置TypeHandler,那么这里获取到的TypeHandler实例为UnknownTypeHandler,其继承于BaseTypeHandler。这里会走到UnknownTypeHandler的setNonNullParameter方法。

@Override
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
 throws SQLException {
TypeHandler handler = resolveTypeHandler(parameter, jdbcType);
handler.setParameter(ps, i, parameter, jdbcType);
}


可以看到,其首先根据参数与jdbcType解析handler ,然后使用handlerps中参数占位符赋值。

其解析TypeHandler主要逻辑如下所示:

private TypeHandler<? extends Object> resolveTypeHandler(Object parameter, JdbcType jdbcType) {
  TypeHandler<? extends Object> handler;
  if (parameter == null) {
    handler = OBJECT_TYPE_HANDLER;
  } else {
    handler = typeHandlerRegistry.getTypeHandler(parameter.getClass(), jdbcType);
    // check if handler is null (issue #270)
    if (handler == null || handler instanceof UnknownTypeHandler) {
      handler = OBJECT_TYPE_HANDLER;
    }
  }
  return handler;
}



代码解释如下:

  • ① 如果parameternull,则handlerObjectTypeHandler;
  • ② 否则根据参数类型、jdbcTypetypeHandlerRegistryTYPE_HANDLER_MAP集合中获取;


处理完后的statement实例如下所示,SQL中参数占位符已经被赋予实际值,$Proxy6表示其是一个代理对象。


handler.<E>query(stmt, resultHandler)

RoutingStatementHandler交给装饰对象PreparedStatementHandler进行查询。

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


PreparedStatementHandlerquery方法如下。

@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方法,由于其是代理对象,故而会先触发其调用处理程序PreparedStatementLoggerinvoke方法。
  • ② 使用DefaultResultSetHandler处理结果集


PreparedStatementLogger.invoke方法

EXECUTE_METHODS值如下:

[addBatch, execute, executeUpdate, executeQuery]


SET_METHODS值如下:

[setBytes, setFloat, setBigDecimal, setByte, setNull, setShort, setObject, setInt, setArray, setNCharacterStream,
setDouble, setNClob, setString, setLong, setCharacterStream, setAsciiStream, setClob, setBlob, setDate, 
setBinaryStream, setNString, setTimestamp, setTime, setBoolean]


@Override
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
  try {
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, params);
    }          
    //[addBatch, execute, executeUpdate, executeQuery]
    if (EXECUTE_METHODS.contains(method.getName())) {
      if (isDebugEnabled()) {
        debug("Parameters: " + getParameterValueString(), true);
      }
      clearColumnInfo();
      if ("executeQuery".equals(method.getName())) {
        ResultSet rs = (ResultSet) method.invoke(statement, params);
        return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
      } else {
        return method.invoke(statement, params);
      }
    } else if (SET_METHODS.contains(method.getName())) {
      if ("setNull".equals(method.getName())) {
        setColumn(params[0], null);
      } else {
        setColumn(params[0], params[1]);
      }
      return method.invoke(statement, params);
    } else if ("getResultSet".equals(method.getName())) {
      ResultSet rs = (ResultSet) method.invoke(statement, params);
      return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
    } else if ("getUpdateCount".equals(method.getName())) {
      int updateCount = (Integer) method.invoke(statement, params);
      if (updateCount != -1) {
        debug("   Updates: " + updateCount, false);
      }
      return updateCount;
    } else {
      return method.invoke(statement, params);
    }
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
}

代码解释如下:


① 判断方法所属声明类型为object,则直接反射调用方法;

② 判断方法名是否在EXECUTE_METHODS内,进而判断是否为executeQuery,进行不同处理;

③ 判断方法名是否在SET_METHODS内,如果是,则首先进行setColumn处理;

④ 判断方法名是否为getResultSet;

⑤ 判断方法名是否为getUpdateCount;

⑥ 以上都没有执行,则直接进行方法反射。如下图所示,方法为public abstract boolean java.sql.PreparedStatement.execute() throws java.sql.SQLException


statement对象为JDBC42PreparedStatement,那么方法最终会走到PreparedStatement.execute() 方法。在MySQL5中public class PreparedStatement extends com.mysql.jdbc.StatementImpl implements java.sql.PreparedStatement这里可以看一下其execute方法(这里不用做深入研究,只需要知道方法执行后,就拿到了结果集resultSet):

public boolean execute() throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
            MySQLConnection locallyScopedConn = this.connection;
            if (!checkReadOnlySafeStatement()) {
                throw SQLError.createSQLException(Messages.getString("PreparedStatement.20") + Messages.getString("PreparedStatement.21"),
                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
            }
            ResultSetInternalMethods rs = null;
            CachedResultSetMetaData cachedMetadata = null;
            this.lastQueryIsOnDupKeyUpdate = false;
            if (this.retrieveGeneratedKeys) {
                this.lastQueryIsOnDupKeyUpdate = containsOnDuplicateKeyUpdateInSQL();
            }
            clearWarnings();
            setupStreamingTimeout(locallyScopedConn);
            this.batchedGeneratedKeys = null;
            Buffer sendPacket = fillSendPacket();
            String oldCatalog = null;
            if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) {
                oldCatalog = locallyScopedConn.getCatalog();
                locallyScopedConn.setCatalog(this.currentCatalog);
            }
            //
            // Check if we have cached metadata for this query...
            //
            if (locallyScopedConn.getCacheResultSetMetadata()) {
                cachedMetadata = locallyScopedConn.getCachedMetaData(this.originalSql);
            }
            Field[] metadataFromCache = null;
            if (cachedMetadata != null) {
                metadataFromCache = cachedMetadata.fields;
            }
            boolean oldInfoMsgState = false;
            if (this.retrieveGeneratedKeys) {
                oldInfoMsgState = locallyScopedConn.isReadInfoMsgEnabled();
                locallyScopedConn.setReadInfoMsgEnabled(true);
            }
            //
            // Only apply max_rows to selects
            //
            locallyScopedConn.setSessionMaxRows(this.firstCharOfStmt == 'S' ? this.maxRows : -1);
            rs = executeInternal(this.maxRows, sendPacket, createStreamingResultSet(), (this.firstCharOfStmt == 'S'), metadataFromCache, false);
            if (cachedMetadata != null) {
                locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql, cachedMetadata, rs);
            } else {
                if (rs.reallyResult() && locallyScopedConn.getCacheResultSetMetadata()) {
                    locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql, null /* will be created */, rs);
                }
            }
            if (this.retrieveGeneratedKeys) {
                locallyScopedConn.setReadInfoMsgEnabled(oldInfoMsgState);
                rs.setFirstCharOfQuery(this.firstCharOfStmt);
            }
            if (oldCatalog != null) {
                locallyScopedConn.setCatalog(oldCatalog);
            }
            if (rs != null) {
                this.lastInsertId = rs.getUpdateID();
                this.results = rs;
            }
            return ((rs != null) && rs.reallyResult());
        }
    }



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;
    // 得到第一个ResultSet,也就是before the first row  
    ResultSetWrapper rsw = getFirstResultSet(stmt);
  // 获取当前statement的实体-属性结果映射
    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 ;

② 获取resultMaps ;

③ 对resultMaps进行循环处理;

④ 获取resultSets 然后进行循环处理;

⑥ 如果multipleResults集合只有一个元素则返回第一个元素,否则返回multipleResults获取到的ResultSetWrapper rsw如下图所示:

此时指针并没有指向数据集的第一条,而是Before start of result set,currRowIndex=-1。rs是个代理对象。



获取到的 List<ResultMap> resultMaps如下图所示(xml中当前查询没有指定resultMap,这是mybatis默认创建的):



这里我们跟踪一下handleResultSet(rsw, resultMap, multipleResults, null);如下所示。

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


这里会创建默认的DefaultResultHandler 实例对象,然后调用核心方法handleRowValues处理行记录值。这里会把defaultResultHandler.getResultList()放到multipleResults中,multipleResults是一个List<Object>。DefaultResultHandler 只有一个List<Object> list属性,其作为当前上下文处理结果的容器,存放结果集(RowValue或者说ResultObject集合)。

public class DefaultResultHandler implements ResultHandler<Object> {
  private final List<Object> list;
  public DefaultResultHandler() {
    list = new ArrayList<Object>();
  }
  @SuppressWarnings("unchecked")
  public DefaultResultHandler(ObjectFactory objectFactory) {
    list = objectFactory.create(List.class);
  }
  @Override
  public void handleResult(ResultContext<? extends Object> context) {
    list.add(context.getResultObject());
  }
  public List<Object> getResultList() {
    return list;
  }
}


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


会根据是否有嵌套结果映射来走不同的处理逻辑,这里我们走的是handleRowValuesForSimpleResultMap


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



代码解释如下:


① 获取resultContext 然后跳到下一行(第一次next方法执行前指针位置是before start of resultset);

② 如果有下一行记录需要处理并且有对应的resultSet,则进行迭代循环处理;

③ 处理Discriminator;

④ 获取RowValue也就是行记录返回值,这里是Employee对象。


  • storeObject方法会把当前context.getResultObject()放到DefaultResultHandler的成员List<Object> list

获取行记录返回结果方法如下所示:

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
  final ResultLoaderMap lazyLoader = new ResultLoaderMap();
  //这里是空对象 空对象,在下面过程中会对其进行属性赋值
  Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null);
  if (resultObject != null && !hasTypeHandlerForResultObject(rsw, 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;
}


代码解释如下:


① 获取返回结果对象resultObject ,这里得到的Employee是一个空对象,成员值都是null;

② 根据resultObject 获取MetaObject 对象;

③ 判断是否可以进行自动映射(也就是查询返回的column与对象property一致),如果是则进行applyAutomaticMappings;

④ 尝试进行applyPropertyMappings,也就是自定义了resultMap,做了列与属性映射;

⑤ 判断是返回null还是resultObject;

处理完当前ResultSet后,会获取nextResultSet,将得到的结果存储起来。迭代处理完后,依次返回。其中这里应用了大量的代理模式、装饰模式以及mybatis结果映射的知识。另外,这里只是在单mybatis环境下对单个查询进行分析,那么在SSM、SpringBoot环境下的流程细节可能有所不同。这里补充一下ResultSet、ResultSetImpl与NativeResultset获取数据的过程。


ResultSet是一个接口,我们代码中看到的也是一个代理对象(可能是多次代理哦,比如ResultSetImpl-HikariProxyResultSet-org.apache.ibatis.logging.jdbc.ResultSetLogger),其会将操作委派给ResultSetImpl去处理。


ResultSetImpl继承自NativeResultset,后者拥有属性ResultsetRows rowData维护了数据库查询的数据以及属性Row thisRow维护当前数据。每一次的resultSet.next()造成的结果就是this.thisRow = this.rowData.next();。


rowData如下图所示,内维护了一个内部字节数组,存放每一行的非空属性。数组下标对应列定义下标。

ac59c99cb5df4fae802d98bde669c8e0.png


那么列定义是什么?直接看图吧


rowData什么时候被赋值的?

在执行完数据库查询(或其他操作)后创建ResultSetImpl时为rowData赋值,多行结果集时rowData中rows有多个元素,如下图所示:

fb9a66d4d10240aba180d7e1e255778f.png

参考博文:

MyBatis中XML映射器使用详解

MyBatis中映射器之结果映射详解

设计模式之装饰者模式

设计模式之代理模式

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
2月前
|
SQL Java 数据库连接
springboot~mybatis-pagehelper原理与使用
【7月更文挑战第15天】MyBatis-PageHelper是用于MyBatis的分页插件,基于MyBatis的拦截器机制实现。它通过在SQL执行前动态修改SQL语句添加LIMIT子句以支持分页。使用时需在`pom.xml`添加依赖并配置方言等参数。示例代码: PageHelper.startPage(2, 10); List&lt;User&gt; users = userMapper.getAllUsers(); PageInfo&lt;User&gt; pageInfo = new PageInfo&lt;&gt;(users); 这使得分页查询变得简单且能获取总记录数等信息。
|
2月前
|
Java 数据库连接 mybatis
Mybatis查询传递单个参数和传递多个参数用法
Mybatis查询传递单个参数和传递多个参数用法
42 11
MybatisPlus-标准CRUD制作,新增boolean save(T t),删除 ~ delete(int id),修改 ~ update(T t),根据id查询,T getById....
MybatisPlus-标准CRUD制作,新增boolean save(T t),删除 ~ delete(int id),修改 ~ update(T t),根据id查询,T getById....
|
3月前
|
SQL Java 数据库连接
深入探索MyBatis Dynamic SQL:发展、原理与应用
深入探索MyBatis Dynamic SQL:发展、原理与应用
|
2月前
|
SQL Java 数据库连接
Java面试题:简述ORM框架(如Hibernate、MyBatis)的工作原理及其优缺点。
Java面试题:简述ORM框架(如Hibernate、MyBatis)的工作原理及其优缺点。
39 0
MyBatisPlus如何根据id批量查询?Required request parameter ‘id‘ for method 解决方法是看青戈大佬MybatisPlus的教程
MyBatisPlus如何根据id批量查询?Required request parameter ‘id‘ for method 解决方法是看青戈大佬MybatisPlus的教程
MybatisPlus介绍新增用户,根据id查询,引入MybatisPlus的起步依赖,增删改查最简单的写法
MybatisPlus介绍新增用户,根据id查询,引入MybatisPlus的起步依赖,增删改查最简单的写法
|
2月前
|
Java 数据库连接 mybatis
mybatis包扫描环境分析,最简单的环境准备
mybatis包扫描环境分析,最简单的环境准备
|
2月前
|
Java 数据库连接 Maven
Private method ‘getVideoList()‘ is never used,mybatis必须指定Mapper文件和实体目录,在参考其他人写的代码,要认真分析别人的代码,不要丢失
Private method ‘getVideoList()‘ is never used,mybatis必须指定Mapper文件和实体目录,在参考其他人写的代码,要认真分析别人的代码,不要丢失
|
3月前
|
Java 数据库连接 mybatis
使用Mybatis获取sqlSession对象老爆红的问题解决
使用Mybatis获取sqlSession对象老爆红的问题解决