引用《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<Object> list = selectList();
//将 list 集合转换成 targetType 指定类型的对象
resultObject = resultExtractor.extractObjectFromList(list, targetType);
return resultObject;
}
private <E> List<E> 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<String> 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方法中就会创建代理对象。