Mybatis-ResultHandler,Cursor,RowBounds 源码分析

Mybatis3.5.1源码分析

  1. Mybatis-SqlSessionFactoryBuilder,XMLConfigBuilder,XPathParser源码解析
  2. Mybatis-Configuration源码解析
  3. Mybatis-事务对象源码解析
  4. Mybatis-数据源源码解析
  5. Mybatis缓存策略源码解析
  6. Mybatis-DatabaseIdProvider源码解析
  7. Mybatis-TypeHandler源码解析
  8. Mybatis-Reflector源码解析
  9. Mybatis-ObjectFactory,ObjectWrapperFactory源码分析
  10. Mybatis-Mapper各类标签封装类源码解析
  11. Mybatis-XMLMapperBuilder,XMLStatmentBuilder源码分析
  12. Mybatis-MapperAnnotationBuilder源码分析
  13. [Mybatis-MetaObject,MetaClass源码解析]https://www.jianshu.com/p/f51fa552f30a)
  14. Mybatis-LanguageDriver源码解析
  15. Mybatis-SqlSource源码解析
  16. Mybatis-SqlNode源码解析
  17. Mybatis-KeyGenerator源码解析
  18. Mybatis-Executor源码解析
  19. Mybatis-ParameterHandler源码解析
  20. Mybatis-StatementHandler源码解析
  21. Mybatis-DefaultResultSetHandler(一)源码解析
  22. Mybatis-DefaultResultSetHandler(二)源码解析
  23. Mybatis-ResultHandler,Cursor,RowBounds 源码分析
  24. Mybatis-MapperProxy源码解析
  25. Mybatis-SqlSession源码解析
  26. Mybatis-Interceptor源码解析

ResultHandler

/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.session;

/**
 * 结果处理类
 * @author Clinton Begin
 */
public interface ResultHandler<T> {

  /**
   * 处理结果
   * @param resultContext 结果上下文
   */
  void handleResult(ResultContext<? extends T> resultContext);

}

DefaultResultHandler

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.executor.result;

import java.util.ArrayList;
import java.util.List;

import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;

/**
 * 默认ResultHandler,通过ObjectFactory去构建list。本质是一个list
 * @author Clinton Begin
 */
public class DefaultResultHandler implements ResultHandler<Object> {

  /**
   * 集合
   */
  private final List<Object> list;

  public DefaultResultHandler() {
    list = new ArrayList<>();
  }

  @SuppressWarnings("unchecked")
  public DefaultResultHandler(ObjectFactory objectFactory) {
    list = objectFactory.create(List.class);
  }

  @Override
  public void handleResult(ResultContext<?> context) {
    list.add(context.getResultObject());
  }

  public List<Object> getResultList() {
    return list;
  }

}

DefaultMapResultHandler

/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.executor.result;

import java.util.Map;

import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;

/**
 * 默认Map的ResultHandler
 * @author Clinton Begin
 */
public class DefaultMapResultHandler<K, V> implements ResultHandler<V> {

  /**
   * 映射结果
   */
  private final Map<K, V> mappedResults;
  /**
   * 映射Key
   * <p>
   *   指定一个字段作为返回Map中的key,这里一般也就是使用唯一键来做key.
   * </p>
   * <p>
   *     参考博客:https://blog.csdn.net/u012734441/article/details/85861337
   * </p>
   */
  private final String mapKey;
  /**
   * 对象工厂
   */
  private final ObjectFactory objectFactory;
  /**
   * 对象包装工厂
   */
  private final ObjectWrapperFactory objectWrapperFactory;
  /**
   * 反射工厂
   */
  private final ReflectorFactory reflectorFactory;

  /**
   *
   * @param mapKey 指定一个字段作为返回Map中的key,这里一般也就是使用唯一键来做key.
   *               参考博客:https://blog.csdn.net/u012734441/article/details/85861337
   * @param objectFactory 对象工厂
   * @param objectWrapperFactory 对象包装工厂
   * @param reflectorFactory 反射工厂
   */
  @SuppressWarnings("unchecked")
  public DefaultMapResultHandler(String mapKey, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
    this.objectFactory = objectFactory;
    this.objectWrapperFactory = objectWrapperFactory;
    this.reflectorFactory = reflectorFactory;
    this.mappedResults = objectFactory.create(Map.class);
    this.mapKey = mapKey;
  }

  @Override
  public void handleResult(ResultContext<? extends V> context) {
    //获取结果对象
    final V value = context.getResultObject();
    //value元对象
    final MetaObject mo = MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
    // TODO is that assignment always true?
    //获取mapKey的key
    final K key = (K) mo.getValue(mapKey);
    mappedResults.put(key, value);
  }

  public Map<K, V> getMappedResults() {
    return mappedResults;
  }
}

Cursor

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cursor;

import java.io.Closeable;

/**
 * Cursor contract to handle fetching items lazily using an Iterator.
 * Cursors are a perfect fit to handle millions of items queries that would not normally fits in memory.
 * If you use collections in resultMaps then cursor SQL queries must be ordered (resultOrdered="true")
 * using the id columns of the resultMap.
 * <br/>
 * 这个游标通过迭代器来获取一条条数据
 * 游标非常适合处理数百万查询数据,这个样百万数据并不适合内存中获取
 * 如果在resultMap去收集结果,那么sql查询必须是有序的即设置(在id列进行设置resultOrdered="true")
 * <br/>
 * <a href='https://blog.csdn.net/m0_37355951/article/details/97552290'>https://blog.csdn.net/m0_37355951/article/details/97552290</a>
 * @author Guillaume Darmont / guillaume@dropinocean.com
 */
public interface Cursor<T> extends Closeable, Iterable<T> {

  /**
   * 游标开始从数据库获取数据这个就返回true
   * @return true if the cursor has started to fetch items from database.
   */
  boolean isOpen();

  /**
   * 判断所有元素是否已经获取完
   * @return true if the cursor is fully consumed and has returned all elements matching the query.
   */
  boolean isConsumed();

  /**
   * 获取数据的索引,从0开始,如果没有数据就返回-1
   * Get the current item index. The first item has the index 0.
   * @return -1 if the first cursor item has not been retrieved. The index of the current item retrieved.
   */
  int getCurrentIndex();
}

DefaultCursor

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cursor.defaults;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.NoSuchElementException;

import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.resultset.DefaultResultSetHandler;
import org.apache.ibatis.executor.resultset.ResultSetWrapper;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

/**
 * This is the default implementation of a MyBatis Cursor.
 * This implementation is not thread safe.
 * <p>
 *  这个是默认的Mybatis Cursor接口实现类
 *  该实例并不是线程安全的
 * </p>
 * <p>
 *     默认的mybatis游标实例
 * </p>
 *
 * @author Guillaume Darmont / guillaume@dropinocean.com
 */
public class DefaultCursor<T> implements Cursor<T> {

  // ResultSetHandler stuff
  /**
   * 默认的结果集处理器
   */
  private final DefaultResultSetHandler resultSetHandler;
  /**
   * Mapper.xml的resultMap标签信息封装类对象
   */
  private final ResultMap resultMap;
  /**
   * 结果集包装类对象
   */
  private final ResultSetWrapper rsw;
  /**
   * Mybatis的分页对象
   */
  private final RowBounds rowBounds;
  /**
   * 对象包装结果处理类
   */
  private final ObjectWrapperResultHandler<T> objectWrapperResultHandler = new ObjectWrapperResultHandler<>();

  /**
   * 游标迭代器
   */
  private final CursorIterator cursorIterator = new CursorIterator();

  /**
   * 迭代器打开了的标记,该标记使得迭代器不能多次打开游标,只能打开一次
   */
  private boolean iteratorRetrieved;

  /**
   *  游标的默认状态是创建
   */
  private CursorStatus status = CursorStatus.CREATED;
  /**
   * 分页对象的索引位置为-1
   */
  private int indexWithRowBound = -1;

  /**
   * 游标状态枚举
   */
  private enum CursorStatus {

    /**
     * A freshly created cursor, database ResultSet consuming has not started.
     * 刚刚出炉的游标,数据库的结果集还没有被消费
     */
    CREATED,
    /**
     * A cursor currently in use, database ResultSet consuming has started.
     * 游标已经被使用,同时结果集数据已经开始被使用了
     */
    OPEN,
    /**
     * A closed cursor, not fully consumed.
     * 游标被关闭,可能并没有全部被消费掉
     */
    CLOSED,
    /**
     * A fully consumed cursor, a consumed cursor is always closed.
     * 表示游标已经遍历所有数据集,这个消费完游标一直将会被关闭掉
     */
    CONSUMED
  }

  /**
   *
   * @param resultSetHandler 默认结果集处理器
   * @param resultMap Mapper.xml的resultMap标签信息封装类对象
   * @param rsw 结果集包装类对象
   * @param rowBounds Mybatis的分页对象
   */
  public DefaultCursor(DefaultResultSetHandler resultSetHandler, ResultMap resultMap, ResultSetWrapper rsw, RowBounds rowBounds) {
    this.resultSetHandler = resultSetHandler;
    this.resultMap = resultMap;
    this.rsw = rsw;
    this.rowBounds = rowBounds;
  }

  /**
   * 游标是否打开了
   */
  @Override
  public boolean isOpen() {
    //如果当前游标状态为OPEN
    return status == CursorStatus.OPEN;
  }

  /**
   * 判断游标是否被消费完了
   */
  @Override
  public boolean isConsumed() {
    //如果当前游标状态为CONSUMED
    return status == CursorStatus.CONSUMED;
  }

  /**
   * 获取当前的索引
   */
  @Override
  public int getCurrentIndex() {
    //分页偏移量+游标迭代器索引位置
    return rowBounds.getOffset() + cursorIterator.iteratorIndex;
  }

  /**
   * 获取迭代器
   */
  @Override
  public Iterator<T> iterator() {
    //如果是已经获取过一次迭代器了
    if (iteratorRetrieved) {
      //抛出只能迭代异常的异常
      throw new IllegalStateException("Cannot open more than one iterator on a Cursor");
    }
    //如果游标已经关闭
    if (isClosed()) {
      //抛出游标已关闭异常
      throw new IllegalStateException("A Cursor is already closed.");
    }
    //修改标记,防止重新调用该方法
    iteratorRetrieved = true;
    //返回游标迭代器
    return cursorIterator;
  }

  /**
   * 关闭游标
   */
  @Override
  public void close() {
    //如果游标已经关闭直接返回
    if (isClosed()) {
      return;
    }
    //获取结果集
    ResultSet rs = rsw.getResultSet();
    try {
      //如果结果集不为空
      if (rs != null) {
        //关闭结果集
        rs.close();
      }
    } catch (SQLException e) {
      // ignore 忽略关闭后异常
    } finally {
      //将游标状态设置为已关闭
      status = CursorStatus.CLOSED;
    }
  }

  /**
   * 使用Mybatis分页对象抓取下一个结果
   * @return 下一个结果
   */
  protected T fetchNextUsingRowBound() {
    //从数据库中获取下一个结果
    T result = fetchNextObjectFromDatabase();
    //通过下面的while循环,不断获取下一个结果直到达到分页对象设置的开始位置。
    //只要结果不为null 且 当前分页索引位置小于分页对象设置的开始位置 就继续获取下一个结果。
    while (result != null && indexWithRowBound < rowBounds.getOffset()) {
      //从数据库中获取下一个结果
      result = fetchNextObjectFromDatabase();
    }
    //返回结果
    return result;
  }

  /**
   * 从数据库中获取下一个结果
   * @return 下一个结果
   */
  protected T fetchNextObjectFromDatabase() {
    // //判断游标是否已经关闭,已关闭返回null
    if (isClosed()) {
      return null;
    }
    try {
      //设置当前状态是游标打开状态
      status = CursorStatus.OPEN;
      //如果结果集包装类不是已经关闭
      if (!rsw.getResultSet().isClosed()) {
        /**
         *  构建出来的结果对象,如果父级结果属性映射不为null,会将结果对象赋值到父级结果属性对应的结果对象中,
         *  否则将结果对象加入到reusltHandler中
         */
        //把结果放入objectWrapperResultHandler对象的result中
        resultSetHandler.handleRowValues(rsw, resultMap, objectWrapperResultHandler, RowBounds.DEFAULT, null);
      }
    } catch (SQLException e) {
      throw new RuntimeException(e);
    }
    // 获取对象包装处理的结果
    T next = objectWrapperResultHandler.result;
    // 如果结果不为空结果, 索引++
    if (next != null) {
      indexWithRowBound++;
    }
    // No more object or limit reached
    ///next为null或者读取条数等于偏移量+限制条数,偏移量+限制条数=可读取的条数
    if (next == null || getReadItemsCount() == rowBounds.getOffset() + rowBounds.getLimit()) {
      //关闭游标
      close();
      //将游标状态修改为已消耗完的关闭状态
      status = CursorStatus.CONSUMED;
    }
    //把结果设置为null
    objectWrapperResultHandler.result = null;
    //返回结果
    return next;
  }

  /**
   * 游标是否已经关闭
   */
  private boolean isClosed() {
    //如果当前游标状态为CLOSED或者为CONSUMED
    return status == CursorStatus.CLOSED || status == CursorStatus.CONSUMED;
  }

  /**
   * 下一个读取索引位置
   */
  private int getReadItemsCount() {
    //当前分页对象索引位置+1
    return indexWithRowBound + 1;
  }

  /**
   * 对象包装结果处理类
   */
  private static class ObjectWrapperResultHandler<T> implements ResultHandler<T> {

    /**
     * 结果对象
     */
    private T result;

    @Override
    public void handleResult(ResultContext<? extends T> context) {
      //获取结果对象
      this.result = context.getResultObject();
      //标记结果上下文为停止
      context.stop();
    }
  }

  /**
   * 游标迭代器
   */
  private class CursorIterator implements Iterator<T> {

    /**
     * Holder for the next object to be returned.
     * 保存下一个将会被返回的对象
     */
    T object;

    /**
     * 返回下一个对象的索引
     * Index of objects returned using next(), and as such, visible to users.
     */
    int iteratorIndex = -1;

    /**
     * 是否有下一个结果
     */
    @Override
    public boolean hasNext() {
      if (object == null) {
        object = fetchNextUsingRowBound();
      }
      return object != null;
    }

    /**
     * 获取下一个结果
     */
    @Override
    public T next() {
      // Fill next with object fetched from hasNext()
      T next = object;
      //执行过haNext()方法object的值才不会为null
      if (next == null) {
        ////表示没有执行hasNext()方法,所以在获取一次数据
        next = fetchNextUsingRowBound();
      }
      //如果下一个结果不为null
      if (next != null) {
        //保存下一个将会被返回的对象设置为null
        object = null;
        //下一个对象的索引++
        iteratorIndex++;
        //返回下一个结果
        return next;
      }
      //下一个结果为null时,抛出异常
      throw new NoSuchElementException();
    }

    /**
     * 不支持删除对象
     */
    @Override
    public void remove() {
      throw new UnsupportedOperationException("Cannot remove element from Cursor");
    }
  }
}

ResultLoaderMap

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.executor.loader;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.BaseExecutor;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

/**
 * 用来表示需要懒加载的属性集,本质是一个HashMap
 * https://blog.csdn.net/qq_39470742/article/details/88794755
 * @author Clinton Begin
 * @author Franta Mejta
 */
public class ResultLoaderMap {

  /**
   * 懒加载属性集
   */
  private final Map<String, LoadPair> loaderMap = new HashMap<>();

  /**
   * 添加加载器到映射
   * @param property 属性名
   * @param metaResultObject 结果元对象
   * @param resultLoader  结果加载器
   */
  public void addLoader(String property, MetaObject metaResultObject, ResultLoader resultLoader) {
    /**
     *   将 {@code property} 转换成大写,如果 {@code property} 属于'xxx.xx.x'格式,
     *   则将'.'作为分割符分成数组,去数组的第一个元素转化成大写并返回出去。
     */
    String upperFirst = getUppercaseFirstProperty(property);
    //如果upperFirst忽略大小写的相等判断property不对应 且 加载器映射队列中已经含有upperFist
    if (!upperFirst.equalsIgnoreCase(property) && loaderMap.containsKey(upperFirst)) {
      throw new ExecutorException("Nested lazy loaded result property '" + property
              + "' for query id '" + resultLoader.mappedStatement.getId()
              + " already exists in the result map. The leftmost property of all lazy loaded properties must be unique within a result map.");
    }
    loaderMap.put(upperFirst, new LoadPair(property, metaResultObject, resultLoader));
  }

  public final Map<String, LoadPair> getProperties() {
    return new HashMap<>(this.loaderMap);
  }

  public Set<String> getPropertyNames() {
    return loaderMap.keySet();
  }

  public int size() {
    return loaderMap.size();
  }

  public boolean hasLoader(String property) {
    return loaderMap.containsKey(property.toUpperCase(Locale.ENGLISH));
  }

  /**
   * 执行对 {@code property} 的懒加载查询
   * @param property 属性名
   * @return 在加载器映射队列中存在该property对应的LoadPair对象时,返回true,否则返回false
   */
  public boolean load(String property) throws SQLException {
    //获取property对应的LoadPair对象,这里使用的是remove,所以只能获取一次
    LoadPair pair = loaderMap.remove(property.toUpperCase(Locale.ENGLISH));
    if (pair != null) {
      //执行懒加载查询
      pair.load();
      return true;
    }
    return false;
  }

  public void remove(String property) {
    loaderMap.remove(property.toUpperCase(Locale.ENGLISH));
  }

  /**
   * 执行对 加载器映射队列的所有元素 的懒加载查询
   */
  public void loadAll() throws SQLException {
    final Set<String> methodNameSet = loaderMap.keySet();
    String[] methodNames = methodNameSet.toArray(new String[methodNameSet.size()]);
    for (String methodName : methodNames) {
      load(methodName);
    }
  }

  /**
   * 将 {@code property} 转换成大写,如果 {@code property} 属于'xxx.xx.x'格式,则将'.'作为
   * 分割符分成数组,去数组的第一个元素转化成大写并返回出去。
   * @param property 属性名
   * @return 大写的 {@code property}
   */
  private static String getUppercaseFirstProperty(String property) {
    String[] parts = property.split("\\.");
    return parts[0].toUpperCase(Locale.ENGLISH);
  }

  /**
   * 对于单个属性懒加载信息的封装,最重要的就是其load方法用来加载属性
   * <p>
   * Property which was not loaded yet.属性仍然未加载
   */
  public static class LoadPair implements Serializable {

    private static final long serialVersionUID = 20130412;
    /**
     * Name of factory method which returns database connection.
     * <p>
     *     返回数据库连接的工厂方法名
     * </p>
     */
    private static final String FACTORY_METHOD = "getConfiguration";
    /**
     * Object to check whether we went through serialization..
     * <p>
     *     对象用于来检查我们是否经过序列化。
     * </p>
     */
    private final transient Object serializationCheck = new Object();
    /**
     * Meta object which sets loaded properties.
     * <p>
     *     已加载属性的集合元对象
     * </p>
     */
    private transient MetaObject metaResultObject;
    /**
     * Result loader which loads unread properties.
     * <p>
     *     加载未读取的属性的结果加载器
     * </p>
     */
    private transient ResultLoader resultLoader;
    /**
     * Wow, logger.
     * <p>
     *     日志对象
     * </p>
     */
    private transient Log log;
    /**
     * Factory class through which we get database connection.
     * <p>
     *     用于获取数据库连接的工厂类
     * </p>
     */
    private Class<?> configurationFactory;
    /**
     * Name of the unread property.
     * <p>
     *     未读取的属性名
     * </p>
     */
    private String property;
    /**
     * ID of SQL statement which loads the property.
     * <p>
     *     用于加载 {@link #property} 的ID的SQL脚本
     * </p>
     */
    private String mappedStatement;
    /**
     * Parameter of the sql statement.
     * <p>
     *     {@link #mappedStatement} 的参数对象
     * </p>
     */
    private Serializable mappedParameter;

    /**
     *
     * @param property 属性名
     * @param metaResultObject 已加载属性的集合元对象
     * @param resultLoader 结果加载器
     */
    private LoadPair(final String property, MetaObject metaResultObject, ResultLoader resultLoader) {
      this.property = property;
      this.metaResultObject = metaResultObject;
      this.resultLoader = resultLoader;

      //保存所需的信息只有原始对象可以序列化;
      /* Save required information only if original object can be serialized. */
      if (metaResultObject != null && metaResultObject.getOriginalObject() instanceof Serializable) {
        //获取参数对象
        final Object mappedStatementParameter = resultLoader.parameterObject;

        /* @todo May the parameter be null? */
        if (mappedStatementParameter instanceof Serializable) {
          //  Mapper.xml文件的select,delete,update,insert这些DML标签的封装类对象的Id
          this.mappedStatement = resultLoader.mappedStatement.getId();
          //参数对象序列化赋值
          this.mappedParameter = (Serializable) mappedStatementParameter;
          //获取数据库连接的工厂类
          this.configurationFactory = resultLoader.configuration.getConfigurationFactory();
        } else {
          //日志信息打印
          Log log = this.getLogger();
          if (log.isDebugEnabled()) {
            log.debug("Property [" + this.property + "] of ["
                    + metaResultObject.getOriginalObject().getClass() + "] cannot be loaded "
                    + "after deserialization. Make sure it's loaded before serializing "
                    + "forenamed object.");
          }
        }
      }
    }

    /**
     *  执行懒加载查询
     */
    public void load() throws SQLException {
      /* These field should not be null unless the loadpair was serialized.
       * Yet in that case this method should not be called. */
      //这些字段不能空,除非loadpair序列化,然而,在这种情况下这个方法不应该被称为。
      if (this.metaResultObject == null) {
        throw new IllegalArgumentException("metaResultObject is null");
      }
      if (this.resultLoader == null) {
        throw new IllegalArgumentException("resultLoader is null");
      }

      this.load(null);
    }

    /**
     * 执行懒加载查询,获取数据并且赋值到userObject中返回
     * @param userObject 用户对象
     */
    public void load(final Object userObject) throws SQLException {
      //合法性校验
      if (this.metaResultObject == null || this.resultLoader == null) {
        if (this.mappedParameter == null) {
          throw new ExecutorException("Property [" + this.property + "] cannot be loaded because "
                  + "required parameter of mapped statement ["
                  + this.mappedStatement + "] is not serializable.");
        }

        //获取mappedstatement并且校验
        final Configuration config = this.getConfiguration();
        final MappedStatement ms = config.getMappedStatement(this.mappedStatement);
        if (ms == null) {
          throw new ExecutorException("Cannot lazy load property [" + this.property
                  + "] of deserialized object [" + userObject.getClass()
                  + "] because configuration does not contain statement ["
                  + this.mappedStatement + "]");
        }

        //使用userObject构建metaobject
        this.metaResultObject = config.newMetaObject(userObject);
        //重新构建resultloader对象
        this.resultLoader = new ResultLoader(config, new ClosedExecutor(), ms, this.mappedParameter,
                metaResultObject.getSetterType(this.property), null, null);
      }

      /* We are using a new executor because we may be (and likely are) on a new thread
       * and executors aren't thread safe. (Is this sufficient?)
       *
       * A better approach would be making executors thread safe. */
      /**
       * 我们使用一个新的执行器,因为可能是新的线程和执行器不是线程安全
       * 一个更好的方法会使执行器线程安全
       */
      /**
       * this.serializationCheck == null表示如果序列化过,因为serializationCheck
       * 被tranisent关键字修饰着,所以不参与对象序列化,所以在
       * 反序列化后serializationCheck会变成null,从而得知loadPair对象是否序列化过
       */
      if (this.serializationCheck == null) {
        final ResultLoader old = this.resultLoader;
        //新建一个新的resultLoader对象,
        this.resultLoader = new ResultLoader(old.configuration, new ClosedExecutor(), old.mappedStatement,
                old.parameterObject, old.targetType, old.cacheKey, old.boundSql);
      }
      /**
       * 调用resultLoader的loadResult获取查询结果并赋值到metaResultObject中。
       *
       * 这里可能有会有疑问,resultLoader调用loadResult方法因为其resultLoader对象的executoer对象
       * 经过反序列后,executoer对象会被赋值成ClosedExecutoer对象,当调用ClosedExecutoer对象的
       * 关于执行SQL脚本的方法时会抛出异常,那岂不是得不到查询结果?
       * 其实resultLoader并没有使用ClosedExecutoer对象直接执行SQL脚本,而是调用executoer对象的
       * isClose()看看执行器是否已经关闭,若关闭了,通过构建一个SimpleExecutor对象来执行SQL脚本
       */
      this.metaResultObject.setValue(property, this.resultLoader.loadResult());
    }

    /**
     * 获取配置
     */
    private Configuration getConfiguration() {
      //如果数据库连接工厂类为null
      if (this.configurationFactory == null) {
        throw new ExecutorException("Cannot get Configuration as configuration factory was not set.");
      }

      Object configurationObject;
      try {
        //获取工厂对象的返回数据库连接方法
        final Method factoryMethod = this.configurationFactory.getDeclaredMethod(FACTORY_METHOD);
        //如果返回数据库连接方法不是静态方法
        if (!Modifier.isStatic(factoryMethod.getModifiers())) {
          throw new ExecutorException("Cannot get Configuration as factory method ["
                  + this.configurationFactory + "]#["
                  + FACTORY_METHOD + "] is not static.");
        }

        //如果返回数据库连接方法不能访问
        if (!factoryMethod.isAccessible()) {
          /**
           * AccessController.doPrivileged是一个在AccessController类中的静态方法,允许在一个类实例中的代码通知这个AccessController:它的代码主体是享受"privileged(特权的)",它单独负责对它的可得的资源的访问请求,而不管这个请求是由什么代码所引发的。
           * 这就是说,一个调用者在调用doPrivileged方法时,可被标识为 "特权"。在做访问控制决策时,如果checkPermission方法遇到一个通过doPrivileged调用而被表示为 "特权"的调用者,并且没有上下文自变量,checkPermission方法则将终止检查。如果那个调用者的域具有特定的许可,则不做进一步检查,checkPermission安静地返回,表示那个访问请求是被允许的;如果那个域没有特定的许可,则象通常一样,一个异常被抛出。
           */
          configurationObject = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
            try {
              //设置成可访问
              factoryMethod.setAccessible(true);
              //执行方法
              return factoryMethod.invoke(null);
            } finally {
              //设置成不可访问
              factoryMethod.setAccessible(false);
            }
          });
        } else {
          //执行方法
          configurationObject = factoryMethod.invoke(null);
        }
      } catch (final ExecutorException ex) {
        throw ex;
      } catch (final NoSuchMethodException ex) {
        throw new ExecutorException("Cannot get Configuration as factory class ["
                + this.configurationFactory + "] is missing factory method of name ["
                + FACTORY_METHOD + "].", ex);
      } catch (final PrivilegedActionException ex) {
        throw new ExecutorException("Cannot get Configuration as factory method ["
                + this.configurationFactory + "]#["
                + FACTORY_METHOD + "] threw an exception.", ex.getCause());
      } catch (final Exception ex) {
        throw new ExecutorException("Cannot get Configuration as factory method ["
                + this.configurationFactory + "]#["
                + FACTORY_METHOD + "] threw an exception.", ex);
      }

      //如果configurationObject没有继承Configuration
      if (!(configurationObject instanceof Configuration)) {
        throw new ExecutorException("Cannot get Configuration as factory method ["
                + this.configurationFactory + "]#["
                + FACTORY_METHOD + "] didn't return [" + Configuration.class + "] but ["
                + (configurationObject == null ? "null" : configurationObject.getClass()) + "].");
      }
      //将configuationObject强制转换成Configuration类型
      return Configuration.class.cast(configurationObject);
    }

    private Log getLogger() {
      if (this.log == null) {
        this.log = LogFactory.getLog(this.getClass());
      }
      return this.log;
    }
  }

  /**
   * 已关闭的执行器,不能执行任何SQL脚本,isClosed方法为true,表示已经关闭。
   * 如果执行任何SQL脚本,都会抛出执行UnsupportedOperationException异常
   */
  private static final class ClosedExecutor extends BaseExecutor {

    public ClosedExecutor() {
      super(null, null);
    }

    @Override
    public boolean isClosed() {
      return true;
    }

    @Override
    protected int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
      throw new UnsupportedOperationException("Not supported.");
    }

    @Override
    protected List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
      throw new UnsupportedOperationException("Not supported.");
    }

    @Override
    protected <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
      throw new UnsupportedOperationException("Not supported.");
    }

    @Override
    protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
      throw new UnsupportedOperationException("Not supported.");
    }
  }
}

RowBound

/**
 *    Copyright 2009-2017 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.session;

/**
 * Mybatis的分页对象
 * <p>
 *     Mybatis也支持分页查询,可以借助RowBounds对象,对数据分页,但是,基于RowBounds的分页是对ResultSet结果集进行分页,也就是逻辑分页,并不是物理分页。换句话说,也就是先把数据库中的数据全部查询出来,然后在进行过滤。
 * </p>
 * 参考博客:<a href='https://www.jianshu.com/p/84599f9a49d0'>https://www.jianshu.com/p/84599f9a49d0</a>
 * @author Clinton Begin
 */
public class RowBounds {
  /**
   * 从第0条开始
   */
  public static final int NO_ROW_OFFSET = 0;
  /**
   * 取最大记录数
   */
  public static final int NO_ROW_LIMIT = Integer.MAX_VALUE;
  public static final RowBounds DEFAULT = new RowBounds();

  /**
   * 从第几条开始
   */
  private final int offset;
  /**
   * 取多少条
   */
  private final int limit;

  public RowBounds() {
    this.offset = NO_ROW_OFFSET;
    this.limit = NO_ROW_LIMIT;
  }

  /**
   *
   * @param offset 从第几条开始
   * @param limit 取多少条
   */
  public RowBounds(int offset, int limit) {
    this.offset = offset;
    this.limit = limit;
  }

  public int getOffset() {
    return offset;
  }

  public int getLimit() {
    return limit;
  }

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容