mybatis(四):延迟加载

引用《mybatis技术内幕》3.3.5 嵌套查询&延迟加载,的一段话

“延迟加载”的含义是:暂时不用的对象不会真正载入到内存中, 直到真正需要使用该对 象时,才去执行数据库查询操作,将该对象加载到内存中 。在 MyBatis 中,如果 一个对象的某 个属性需要延迟加载,那么在映射该属性时,会为该属性创建相应的代理对象并返回; 当真正 要使用延迟加载的属性时,会通过代理对象执行数据库加载操作,得到 真正的数据。

1.ResultLoader

ResultLoader主要负责保存一次延迟加载操作所需的全部信息,其中的loadResult()方法执行延迟加载,我们先来看下这个类

public class ResultLoader {

  protected final Configuration configuration;
  // 用于执行延迟加载操作的 Executor 对象
  protected final Executor executor;
  protected final MappedStatement mappedStatement;
  // 记录了延迟执行的 SQL语句的实参
  protected final Object parameterObject;
  protected final Class targetType;
  protected final ObjectFactory objectFactory;
  protected final CacheKey cacheKey;
  //记录了延迟执行的 SQL语句以及相关配置信息
  protected final BoundSql boundSql;
  protected final ResultExtractor resultExtractor;
  // 创建 ResultLoader 的线程 id
  protected final long creatorThreadId;

  protected boolean loaded;

  // 延迟加载得到的结果对象
  protected Object resultObject;
  public Object loadResult() throws SQLException {
    //执行延迟加载,得到结果对象,并以 List 的形式返回
    List list = selectList();
    //将 list 集合转换成 targetType 指定类型的对象
    resultObject = resultExtractor.extractObjectFromList(list, targetType);
    return resultObject;
  }

  private  List selectList() throws SQLException {
    Executor localExecutor = executor;
    //检测调用该方法的线程是否为创建 ResultLoader 对象 的线程 、检测 localExecutor 是否
    // 关闭,检测到异常情况时,会创建新的 Executor 对象来执行延迟加载操作
    if (Thread.currentThread().getId() != this.creatorThreadId || localExecutor.isClosed()) {
      localExecutor = newExecutor();
    }
    try {
      //执行查询操作,得到延迟加载的对象
      return localExecutor.query(mappedStatement, parameterObject, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER, cacheKey, boundSql);
    } finally {
      if (localExecutor != executor) {
        localExecutor.close(false);
      }
    }
  }

  private Executor newExecutor() {
    final Environment environment = configuration.getEnvironment();
    if (environment == null) {
      throw new ExecutorException("ResultLoader could not load lazily.  Environment was not configured.");
    }
    final DataSource ds = environment.getDataSource();
    if (ds == null) {
      throw new ExecutorException("ResultLoader could not load lazily.  DataSource was not configured.");
    }
    final TransactionFactory transactionFactory = environment.getTransactionFactory();
    final Transaction tx = transactionFactory.newTransaction(ds, null, false);
    return configuration.newExecutor(tx, ExecutorType.SIMPLE);
  }
}
 
 

localExecutor.query的执行过程前面已经分析过了,不再赘述。

2.ResultLoaderMap

ResultLoaderMap是用于保存属性名和对应的延迟加载对象的对应关系的。我们来看下ResultLoaderMap。

 // 记载单个属性
  public boolean load(String property) throws SQLException {
    LoadPair pair = loaderMap.remove(property.toUpperCase(Locale.ENGLISH));
    if (pair != null) {
      pair.load();
      return true;
    }
    return false;
  }

  // 加载所有的属性
  public void loadAll() throws SQLException {
    final Set methodNameSet = loaderMap.keySet();
    String[] methodNames = methodNameSet.toArray(new String[methodNameSet.size()]);
    for (String methodName : methodNames) {
      load(methodName);
    }
  }

最终都是调用的pair.load()方法,这个方法主要用于初始化pair中的每个参数。

3.ProxyFactory

ProxyFactory的实现分为cglib和Javassit两种,基本实现一致,都是有一个内部类实现了MethodInterceptor接口的intercept方法,决定是否加载某个属性。

public Object intercept(Object enhanced, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
      final String methodName = method.getName();
      try {
        synchronized (lazyLoader) {
          // 序列化相关,忽略
          if (WRITE_REPLACE_METHOD.equals(methodName)) {
            Object original;
            if (constructorArgTypes.isEmpty()) {
              original = objectFactory.create(type);
            } else {
              original = objectFactory.create(type, constructorArgTypes, constructorArgs);
            }
            PropertyCopier.copyBeanProperties(type, enhanced, original);
            if (lazyLoader.size() > 0) {
              return new CglibSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs);
            } else {
              return original;
            }
          } else {
            if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
              //如采 aggressiveLazyLoad工ng 自己主项为 true,或是调用方法的名称存在于
              // lazyLoadTriggerMethods 列表中,则将全部的属性都加载完成
              if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
                lazyLoader.loadAll();
              }
              // 如果调用了某个属性的setter方法,从lazyLoader中移除该属性对象
              else if (PropertyNamer.isSetter(methodName)) {
                final String property = PropertyNamer.methodToProperty(methodName);
                lazyLoader.remove(property);
              }
              // 如果调用了某个属性的getter方法,并且该属性包含在lazyLoader中,则加载该属性
              else if (PropertyNamer.isGetter(methodName)) {
                final String property = PropertyNamer.methodToProperty(methodName);
                if (lazyLoader.hasLoader(property)) {
                  lazyLoader.load(property);
                }
              }
            }
          }
        }
        // 调用目标对象的方法
        return methodProxy.invokeSuper(enhanced, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
  }

4.DefaultResultSetHandler中的相关调用

在getRowValue中会调用createResultObject方法,在createResultObject方法中就会创建代理对象。

你可能感兴趣的:(mybatis(四):延迟加载)