ibaits中延迟加载的含义是在使用到某个对象时,再去加载具体的数据(执行查询语句),否则不会对数据进行加载。
ibatis查询数据库时,会调用ResultMap.getResults(StatementScope statementScope, ResultSet rs)方法,其实现如下:
public Object[] getResults(StatementScope statementScope, ResultSet rs)
throws SQLException {
ErrorContext errorContext = statementScope.getErrorContext();
errorContext.setActivity("applying a result map");
errorContext.setObjectId(this.getId());
errorContext.setResource(this.getResource());
errorContext.setMoreInfo("Check the result map.");
boolean foundData = false;
Object[] columnValues = new Object[getResultMappings().length];
for (int i = 0; i < getResultMappings().length; i++) {
ResultMapping mapping = (ResultMapping) getResultMappings()[i];
errorContext.setMoreInfo(mapping.getErrorString());
if (mapping.getStatementName() != null) {
if (resultClass == null) {
throw new SqlMapException("The result class was null when trying to get results for ResultMap named " + getId() + ".");
} else if (Map.class.isAssignableFrom(resultClass)) {
Class javaType = mapping.getJavaType();
if (javaType == null) {
javaType = Object.class;
}
columnValues[i] = getNestedSelectMappingValue(statementScope, rs, mapping, javaType);
} else if (DomTypeMarker.class.isAssignableFrom(resultClass)) {
Class javaType = mapping.getJavaType();
if (javaType == null) {
javaType = DomTypeMarker.class;
}
columnValues[i] = getNestedSelectMappingValue(statementScope, rs, mapping, javaType);
} else {
Probe p = ProbeFactory.getProbe(resultClass);
Class type = p.getPropertyTypeForSetter(resultClass, mapping.getPropertyName());
columnValues[i] = getNestedSelectMappingValue(statementScope, rs, mapping, type);
}
foundData = foundData || columnValues[i] != null;
} else if (mapping.getNestedResultMapName() == null) {
columnValues[i] = getPrimitiveResultMappingValue(rs, mapping);
if (columnValues[i] == null) {
columnValues[i] = doNullMapping(columnValues[i], mapping);
} else {
foundData = true;
}
}
}
statementScope.setRowDataFound(foundData);
return columnValues;
}
从代码中可以看出,如果在<result>中设置了select属性,会调用方法getNestedSelectMappingValue()。在该方法中调用ResultLoader.loadResult()方法:
public static Object loadResult(SqlMapClientImpl client, String statementName, Object parameterObject, Class targetType)
throws SQLException {
Object value = null;
if (client.isLazyLoadingEnabled()) {
if (client.isEnhancementEnabled()) {
EnhancedLazyResultLoader lazy = new EnhancedLazyResultLoader(client, statementName, parameterObject, targetType);
value = lazy.loadResult();
} else {
LazyResultLoader lazy = new LazyResultLoader(client, statementName, parameterObject, targetType);
value = lazy.loadResult();
}
} else {
value = getResult(client, statementName, parameterObject, targetType);
}
return value;
}
在该方法中使用了延迟加载技术。当然,要使用延迟加载,需要在配置文件中设置lazyLoadingEnabled为true。
<settings lazyLoadingEnabled="true" />
这样,ibatis才会使用延迟加载,否则,直接执行查询。
在ibatis中有两个负责延迟加载的类:
1、LazyResultLoader
2、EnhancedLazyResultLoader。
从以上代码可以看出,如果属性enhancementEnabled设置为true,则使用增强的延迟加载。但是要使该属性为true,除了在配置文件里设置enhancementEnabled外,还需要有net.sf.cglib.proxy.InvocationHandler类。
如果属性enhancementEnabled为false,则使用LazyResultLoader。该类实现了InvocationHandler接口,所以很明显,这种方式使用了动态代理的方式。调用LazyResultLoader.loadResult(),如果目标对象类型是Collection或其子类,则返回一个代理对象,若不是Collection类型,则直接执行查询语句。由此可知,当使用LazyResultLoader来实现延迟加载时,只是对目标对象为Collection类型的数据有效。
总结:
LazyResultLoader只能延迟加载Collection类型的属性,而EnhancedLazyResultLoader可以延迟加载Collection类型的属性,以及自定义类型的属性,而且性能方面要优于LazyResultLoader。