阶段一 调用SQLSession的查询方法时:
- 例如执行代码“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方法时
- 执行t.get()方法时,此时实际上是会调用的代理对象的invoke()方法
- Mybatis的拦截器会拦截所有invoke()方法
- 此时会判断该方法是否设置了延迟加载,如果设置为延迟加载那么就会发送事先保存好的关联查询的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);
}
}
}
- 接着将调用t.set()方法把查询的结果放入属性中
- 至此,延迟加载的属性就有了值