Mybatis源码解析之核心类分析
Mybatis源码解析之初始化分析
Mybatis源码解析之执行流程解析
Mybatis源码解析之数据库连接和连接池
Mybatis源码解析之事务管理
Mybatis源码解析之缓存机制(一):一级缓存
Mybatis源码解析之缓存机制(二):二级缓存
Mybatis源码解析之插件机制
Mybatis源码解析之mapper接口的代理模式
Mybatis源码解析之DefaultResultSetHandler的handleResultSets方法解析
Mybatis源码解析之Spring集成mybatis-spring分析
在前面的文章里,我们已经提到,mybatis支持懒加载的方式去查询对象的属性。本篇文章,我们将针对懒加载的实现源码展开解析。
在全局配置的setting节点中,有3个配置属性与懒加载相关。
proxyFactory 指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。 CGLIB | JAVASSIST JAVASSIST (MyBatis 3.3 以上)
lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 true | false false
aggressiveLazyLoading 当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载(参考 lazyLoadTriggerMethods)。 true | false false (在 3.4.1 及之前的版本默认值为 true)
这些个配置对应configuration对象的lazyLoadingEnabled和aggressiveLazyLoading属性,解析代码在XMLConfigBuilder#settingsElement(properties)方法中的下面两条语句
configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
protected ProxyFactory proxyFactory = new JavassistProxyFactory();
具体到某个类对象的某个属性是否需要开启,则可以通过resultMap中子节点的fetchType 属性(eager或lazy)来覆盖全局配置,如:
对象ResultMapping类对象的lazy属性。
具体的解析代码在XMLMapperBuilder#buildResultMappingFromContext(XNode, Class>, Listboolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
语句。
ResultLoadMap用来表示需要懒加载的属性集,本质是一个HashMap:private final Map
。
在DefaultSetHandler的getRowValue方法中,进行ResultLoadMap的初始化:final ResultLoaderMap lazyLoader = new ResultLoaderMap();
。
public void addLoader(String property, MetaObject metaResultObject, ResultLoader resultLoader) {
String upperFirst = getUppercaseFirstProperty(property);
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));
}
封装成LoadPair后,加入loaderMap。
该方法在DefaultResultSetHandler的getNestedQueryMappingValue方法中被调用。
private Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
final String nestedQueryId = propertyMapping.getNestedQueryId();
final String property = propertyMapping.getProperty();
final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
final Class> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, propertyMapping, nestedQueryParameterType, columnPrefix);
Object value = null;
if (nestedQueryParameterObject != null) {
final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
final Class> targetType = propertyMapping.getJavaType();
//一级缓存中存在
if (executor.isCached(nestedQuery, key)) {
executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType);
value = DEFERED;
} else {
final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
if (propertyMapping.isLazy()) {
lazyLoader.addLoader(property, metaResultObject, resultLoader);
value = DEFERED;
} else {
value = resultLoader.loadResult();
}
}
}
return value;
}
如果嵌套查询的值在一级缓存中存在,即使配置了懒加载,也不会执行后面介绍的懒加载机制,而是从缓存中获取,不过从缓存中缓存的值也是延迟加载的。
其它的getProperties、getPropertyNames、hasLoader、remove方法等也是对LoadPair的操作。
public boolean load(String property) throws SQLException {
LoadPair pair = loaderMap.remove(property.toUpperCase(Locale.ENGLISH));
if (pair != null) {
pair.load();
return true;
}
return false;
}
加载并且将其从loaderMap中移除。
public void loadAll() throws SQLException {
final Set methodNameSet = loaderMap.keySet();
String[] methodNames = methodNameSet.toArray(new String[methodNameSet.size()]);
for (String methodName : methodNames) {
load(methodName);
}
}
遍历loaderMap调用load方法。
LoadPair是对于单个属性懒加载信息的封装,最重要的就是其load方法用来加载属性。
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.");
}
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 + "]");
}
this.metaResultObject = config.newMetaObject(userObject);
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. */
* 新建resultLoader 保证线程安全
if (this.serializationCheck == null) {
final ResultLoader old = this.resultLoader;
this.resultLoader = new ResultLoader(old.configuration, new ClosedExecutor(), old.mappedStatement,
old.parameterObject, old.targetType, old.cacheKey, old.boundSql);
}
this.metaResultObject.setValue(property, this.resultLoader.loadResult());
}
LoadPair又将具体的加载逻辑委托给ResultLoader执行。
public Object loadResult() throws SQLException {
//获取查询结果
List
在完成查询得到list后,需要进行类型转化,得到需要的类型值。具体逻辑委托给ResultExtractor#extractObjectFromList(List
public Object extractObjectFromList(List