MyBatis原理--查询操作

上一篇文章中分析了插入操作的流程,这次看一下查询操作是怎么做的,获取SqlSession的过程跟插入操作一样,这里直接看查询操作的执行,入口依旧在DefaultSqlSession中:

public class DefaultSqlSession implements SqlSession {
  public <E> List<E> selectList(String statement, Object parameter) {
    return this.<E>selectList(statement, parameter, RowBounds.DEFAULT);
  }

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

//selectOne最终调用的也是selectList方法,然后从list重获取第一个元素。
  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;
    }
  }
}

CachingExecutor:

public class CachingExecutor implements Executor {
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //获取BoundSql的过程和insert操作是一样的
    BoundSql boundSql = ms.getBoundSql(parameterObject);

    //创建cacheKey,根据mappedstatement的id,rowBounds以及parameterObject生成
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);

    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

  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, key, parameterObject, boundSql);

        //如果当前的缓存有效,则获取读锁,然后从缓存中获取结果。
        if (!dirty) {
          cache.getReadWriteLock().readLock().lock();
          try {
            @SuppressWarnings("unchecked")
            List<E> cachedList = (List<E>) cache.getObject(key);
            if (cachedList != null) return cachedList;
          } finally {
            cache.getReadWriteLock().readLock().unlock();
          }
        }
        //被代理的对象执行真正的query逻辑
        List<E> list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        //将结果放到缓存中
        tcm.putObject(cache, key, list); // issue #578. Query must be not synchronized to prevent deadlocks
        return list;
      }
    }
    return delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
}

BaseExecutor:

public abstract class BaseExecutor implements Executor {
  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) {
        //如果缓存结果不为空,对于callable类型的statement,处理一下本地缓存的output。
        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();
      }
      deferredLoads.clear(); // issue #601
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        clearLocalCache(); // issue #482
      }
    }
    return list;
  }


  private void handleLocallyCachedOutputParameters(MappedStatement ms, CacheKey key, Object parameter, BoundSql boundSql) {
    if (ms.getStatementType() == StatementType.CALLABLE) {
      final Object cachedParameter = localOutputParameterCache.getObject(key);
      if (cachedParameter != null && parameter != null) {
        final MetaObject metaCachedParameter = configuration.newMetaObject(cachedParameter);
        final MetaObject metaParameter = configuration.newMetaObject(parameter);
        for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
          if (parameterMapping.getMode() != ParameterMode.IN) {
            final String parameterName = parameterMapping.getProperty();
            final Object cachedValue = metaCachedParameter.getValue(parameterName);
            metaParameter.setValue(parameterName, cachedValue);
          }
        }
      }
    }
  }

  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 {
        //真正的执行逻辑,具体调用SimpleExecutor中的实现
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }

    //缓存到本地
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
        //对于callable类型的语句,结果缓存到本地。
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }
}

SimpleExecutor:

public class SimpleExecutor extends BaseExecutor {
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      //获取connection, StatementHandler和prepare statement的过程同insert操作一致,不多说了。
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());

      //真正的query操作在PreparedStatementHandler中
      return handler.<E>query(stmt, resultHandler);
    } finally {
      //最终关闭statement
      closeStatement(stmt);
    }
  }
}

PreparedStatementHandler:

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

FastResultSetHandler:

public class FastResultSetHandler implements ResultSetHandler {
  //
  // HANDLE RESULT SETS
  //

  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    final List<Object> multipleResults = new ArrayList<Object>();
    final List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    int resultSetCount = 0;
    ResultSet rs = stmt.getResultSet();

    while (rs == null) {
      // move forward to get the first resultset in case the driver
      // doesn't return the resultset as the first result (HSQLDB 2.1)
      if (stmt.getMoreResults()) {
        rs = stmt.getResultSet();
      } else {
        if (stmt.getUpdateCount() == -1) {
          // no more results.  Must be no resultset
          break;
        }
      }
    }

    validateResultMapsCount(rs, resultMapCount);
    //此处这么做是为了支持sql语句的结果有多个resultSet
    while (rs != null && resultMapCount > resultSetCount) {
      final ResultMap resultMap = resultMaps.get(resultSetCount);

      //解析resultSet中的metaDate,获取到每个结果的columnName,JDBCType以及className
      ResultColumnCache resultColumnCache = new ResultColumnCache(rs.getMetaData(), configuration);

      //处理resultSet
      handleResultSet(rs, resultMap, multipleResults, resultColumnCache);

      //获取下一个resultSet
      rs = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }
    return collapseSingleResultList(multipleResults);
  }

  protected void handleResultSet(ResultSet rs, ResultMap resultMap, List<Object> multipleResults, ResultColumnCache resultColumnCache) throws SQLException {
    try {
      if (resultHandler == null) {
        //如果resultHandler为空,则使用默认的
        DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);

        //获取每一行的结果,并将其添加到结果集中
        handleRowValues(rs, resultMap, defaultResultHandler, rowBounds, resultColumnCache);
        multipleResults.add(defaultResultHandler.getResultList());
      } else {
        handleRowValues(rs, resultMap, resultHandler, rowBounds, resultColumnCache);
      }
    } finally {
      closeResultSet(rs); // issue #228 (close resultsets)
    }
  }

  protected void handleRowValues(ResultSet rs, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultColumnCache resultColumnCache) throws SQLException {
    final DefaultResultContext resultContext = new DefaultResultContext();

    //根据rowBounds,跳过部分结果
    skipRows(rs, rowBounds);

    //读取结果,直到读取了要求的条数,或者没有更多结果
    while (shouldProcessMoreRows(rs, resultContext, rowBounds)) {

      //解析DiscriminatedResultMap,即根据结果决定使用的resultMap,配置的时候形如:
      //<discriminator javaType="int" column="draft">
      //  <case value="1" resultType="DraftPost"/>
      //</discriminator>
      final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rs, resultMap, null);

      //获取每一行的结果
      Object rowValue = getRowValue(rs, discriminatedResultMap, null, resultColumnCache);

      //将结果添加到resultHandler中
      callResultHandler(resultHandler, resultContext, rowValue);
    }
  }

  //
  // DISCRIMINATOR
  //

  public ResultMap resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix) throws SQLException {
    Set<String> pastDiscriminators = new HashSet<String>();
    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;
  }

  //
  // GET VALUE FROM ROW
  //

  protected Object getRowValue(ResultSet rs, ResultMap resultMap, CacheKey rowKey, ResultColumnCache resultColumnCache) throws SQLException {
    //如果不允许懒加载,则lazyLoader为空
    final ResultLoaderMap lazyLoader = instantiateResultLoaderMap();

    //初始化返回结果对象
    Object resultObject = createResultObject(rs, resultMap, lazyLoader, null, resultColumnCache);

    //填充返回结果对象
    if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
      final MetaObject metaObject = configuration.newMetaObject(resultObject);
      boolean foundValues = resultMap.getConstructorResultMappings().size() > 0;
      if (shouldApplyAutomaticMappings(resultMap, !AutoMappingBehavior.NONE.equals(configuration.getAutoMappingBehavior()))) {
        //获取resultSet中,那些不在resultMap中的字段
        final List<String> unmappedColumnNames = resultColumnCache.getUnmappedColumnNames(resultMap, null);
        //将未映射的字段的值,设置到返回结果中。如果无法设置任何一个未映射的字段,则返回结果为false。
        foundValues = applyAutomaticMappings(rs, unmappedColumnNames, metaObject, null, resultColumnCache) || foundValues;
      }

      //获取resultSet中,所有在resultMap中的字段
      final List<String> mappedColumnNames = resultColumnCache.getMappedColumnNames(resultMap, null);
      //将映射的字段的值,设置到返回结果中,同时处理关联对象。果无法设置任何一个映射的字段,则返回结果为false。
      foundValues = applyPropertyMappings(rs, resultMap, mappedColumnNames, metaObject, lazyLoader, null) || foundValues;
      foundValues = (lazyLoader != null && lazyLoader.size() > 0) || foundValues;
      resultObject = foundValues ? resultObject : null;
      return resultObject;
    }
    return resultObject;
    //至此,结果的处理就结束了。
  }


  //下面是一些方法的展开
  //
  // INSTANTIATION & CONSTRUCTOR MAPPING
  //

  protected Object createResultObject(ResultSet rs, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix, ResultColumnCache resultColumnCache) throws SQLException {
    final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
    final List<Object> constructorArgs = new ArrayList<Object>();

    //初始化返回结果对象
    final Object resultObject = createResultObject(rs, resultMap, constructorArgTypes, constructorArgs, columnPrefix, resultColumnCache);

    //如果懒加载,则对返回结果对象创建代理,在通过方法对关联对象进行调用的时候,加载关联对象。(不是很明白这里为什么要确保resultMap的type对应的handler不存在)
    //如果非懒加载,则直接返回结果对象,在后续设置值的时候再去查询。
    if (resultObject != null && configuration.isLazyLoadingEnabled() && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
      final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
      for (ResultMapping propertyMapping : propertyMappings) {
        if (propertyMapping.getNestedQueryId() != null) { // issue #109 (avoid creating proxies for leaf objects)

          //对返回结果进行代理,这个地方仅仅是设置了代理,还没有设置在代理对象中去加载那些关联对象
          return proxyFactory.createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
        }
      }
    }
    return resultObject;
  }

  protected boolean applyAutomaticMappings(ResultSet rs, List<String> unmappedColumnNames, MetaObject metaObject, String columnPrefix, ResultColumnCache resultColumnCache) throws SQLException {
    boolean foundValues = false;
    for (String columnName : unmappedColumnNames) {
      String propertyName = columnName;
      if (columnPrefix != null && columnPrefix.length() > 0) {
        // When columnPrefix is specified,
        // ignore columns without the prefix.
        if (columnName.startsWith(columnPrefix)) {
          propertyName = columnName.substring(columnPrefix.length());
        } else {
          //如果前缀不符合要求,则跳过
          continue;
        }
      }
      final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
      if (property != null) {
        final Class<?> propertyType = metaObject.getSetterType(property);
        if (typeHandlerRegistry.hasTypeHandler(propertyType)) {
          final TypeHandler<?> typeHandler = resultColumnCache.getTypeHandler(propertyType, columnName);
          final Object value = typeHandler.getResult(rs, columnName);
          if (value != null || configuration.isCallSettersOnNulls()) { // issue #377, call setter on nulls
            metaObject.setValue(property, value);
            foundValues = true;
          }
        }
      }
    }
    return foundValues;
  }

  //
  // PROPERTY MAPPINGS
  //

  protected boolean applyPropertyMappings(ResultSet rs, ResultMap resultMap, List<String> mappedColumnNames, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
    boolean foundValues = false;
    final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
    for (ResultMapping propertyMapping : propertyMappings) {
      final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
      if (propertyMapping.isCompositeResult() || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))) {
        //获取property的值
        Object value = getPropertyMappingValue(rs, metaObject, propertyMapping, lazyLoader, columnPrefix);
        if (value != OMIT && (value != null || configuration.isCallSettersOnNulls())) { // issue #377, call setter on nulls
          final String property = propertyMapping.getProperty(); // issue #541 make property optional
          if (property != null) {
            metaObject.setValue(property, value);
            foundValues = true;
          }
        }
      }
    }
    return foundValues;
  }


  protected Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
    final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
    if (propertyMapping.getNestedQueryId() != null) {   

      //如果property是一个关联属性
      return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
    } else if (typeHandler != null) {
      final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
      return typeHandler.getResult(rs, column);
    }
    return OMIT;
  }

  protected Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
    final String nestedQueryId = propertyMapping.getNestedQueryId();
    final String property = propertyMapping.getProperty();
    final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
    final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
    final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, propertyMapping, nestedQueryParameterType, columnPrefix);
    Object value = OMIT;
    if (nestedQueryParameterObject != null) {
      final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
      final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
      final Class<?> targetType = propertyMapping.getJavaType();
      final Object nestedQueryCacheObject = getNestedQueryCacheObject(nestedQuery, key);
      if (nestedQueryCacheObject != null && nestedQueryCacheObject instanceof List) {
        value = resultExtractor.extractObjectFromList((List<Object>)nestedQueryCacheObject, targetType);
      } else if (executor.isCached(nestedQuery, key)) {
        executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType);
      } else {
        final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);

        //如果支持懒加载,则将该关联属性的获取操作添加到lazyLoader中,这样在代理对象中可以去获取该关联属性。
        if (configuration.isLazyLoadingEnabled()) {
          lazyLoader.addLoader(property, metaResultObject, resultLoader);
        } else {
            //否则直接获取关联属性的值
          value = resultLoader.loadResult();
        }
      }
    }
    return value;
  }
}
MyBatis 是一个持久层框架,它提供了一种简单且灵活的方式来执行数据库操作。在 MyBatis 中,分页查询可以通过两种方式实现:基于数据库的分页和基于应用程序的分页。 1. 基于数据库的分页: 基于数据库的分页是通过 SQL 语句的 LIMIT 子句来实现的。LIMIT 子句用于指定返回结果的偏移量和限制数量。在 MyBatis 中,我们可以使用 `<select>` 元素的 `offset` 和 `limit` 属性来指定分页查询的偏移量和限制数量。 例如,假设我们要查询第 11-20 条记录,可以这样编写 SQL 语句: ```sql SELECT * FROM table_name LIMIT 10, 10; ``` 在 MyBatis 中,对应的 XML 配置可以这样写: ```xml <select id="selectPage" resultType="com.example.User"> SELECT * FROM table_name LIMIT #{offset}, #{limit} </select> ``` 2. 基于应用程序的分页: 基于应用程序的分页是通过在代码中手动处理结果集来实现的。在 MyBatis 中,我们可以使用 `<select>` 元素的 `resultMap` 属性来处理结果集。 例如,假设我们要查询第 11-20 条记录,可以通过查询所有记录,然后在代码中截取指定范围的记录来实现分页。 在 MyBatis 中,对应的 XML 配置可以这样写: ```xml <select id="selectPage" resultMap="userResultMap"> SELECT * FROM table_name </select> ``` 在代码中,可以通过获取查询结果列表后,使用 `subList` 方法截取指定范围的记录: ```java List<User> userList = sqlSession.selectList("selectPage"); List<User> pageList = userList.subList(offset, offset + limit); ``` 以上是 MyBatis 分页查询原理。根据实际情况和需求,可以选择使用基于数据库的分页或基于应用程序的分页。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值