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。