Mybatis延迟加载原理

阶段一 调用SQLSession的查询方法时:

  1. 例如执行代码“T t = sqlSession.selecOne();”时Mybatis会返回一个T类型的代理对象,部分源码如图
 // 创建映射后的结果对象
        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;
                }
            }
        }

(由图中可以看出,其中有一段propertyMapping.isLazy(),就是我们在mapper中设置关联对象时,设置的fetchType是不是Lazy,如果是Lazy,就会创建代理对象)

阶段二 调用get方法时

  1. 执行t.get()方法时,此时实际上是会调用的代理对象的invoke()方法
  2. Mybatis的拦截器会拦截所有invoke()方法
  3. 此时会判断该方法是否设置了延迟加载,如果设置为延迟加载那么就会发送事先保存好的关联查询的SQL语句,invoke方法部分代码如图:

    if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
        // 加载所有延迟加载的属性
        if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
            lazyLoader.loadAll();
            // 如果调用了 setting 方法,则不在使用延迟加载
        } else if (PropertyNamer.isSetter(methodName)) {
            final String property = PropertyNamer.methodToProperty(methodName);
            lazyLoader.remove(property); // 移除
            // 如果调用了 getting 方法,则执行延迟加载
        } else if (PropertyNamer.isGetter(methodName)) {
            final String property = PropertyNamer.methodToProperty(methodName);
            if (lazyLoader.hasLoader(property)) {
                lazyLoader.load(property);
            }
        }
    }
  1. 接着将调用t.set()方法把查询的结果放入属性中
  2. 至此,延迟加载的属性就有了值

你可能感兴趣的:(学习笔记,mybatis,mybatis)