Author: cbegin Date: Sat Sep 5 22:46:17 2009 New Revision: 811723 URL: http://svn.apache.org/viewvc?rev=811723&view=rev Log: Broke up defaultresulthandler into multiple classes that are more meaningful, to prepare for a larger refactoring that will resolve a few outstanding issues.
Added: ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/DiscriminatorHandler.java ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/NestedResultSetHandler.java ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/NestedSelectHandler.java ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/NoValue.java ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/Reference.java Modified: ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java ibatis/java/ibatis-3/trunk/ibatis-3-core/src/test/java/org/apache/ibatis/builder/BlogMapper.xml Modified: ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java URL: http://svn.apache.org/viewvc/ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java?rev=811723&r1=811722&r2=811723&view=diff ============================================================================== --- ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java (original) +++ ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java Sat Sep 5 22:46:17 2009 @@ -1,6 +1,6 @@ package org.apache.ibatis.executor.resultset; -import org.apache.ibatis.cache.CacheKey; +import static org.apache.ibatis.executor.resultset.NoValue.*; import org.apache.ibatis.executor.*; import org.apache.ibatis.executor.loader.*; import org.apache.ibatis.executor.parameter.ParameterHandler; @@ -15,10 +15,7 @@ public class DefaultResultSetHandler implements ResultSetHandler { - private static final Object NO_VALUE = new Object(); - - private Configuration configuration; - private final Executor executor; + private final Configuration configuration; private final ObjectFactory objectFactory; private final TypeHandlerRegistry typeHandlerRegistry; private final MappedStatement mappedStatement; @@ -26,25 +23,26 @@ private final int rowLimit; private final Object parameterObject; - private final Map nestedResultObjects; - private final ResultHandler resultHandler; private final BoundSql boundSql; - - private CacheKey currentNestedKey; + private final NestedSelectHandler nestedSelectHandler; + private final NestedResultSetHandler nestedResultSetHandler; + private final DiscriminatorHandler discriminatorHandler; public DefaultResultSetHandler(Configuration configuration, Executor executor, MappedStatement mappedStatement, ParameterHandler parameterHandler, int rowOffset, int rowLimit, ResultHandler resultHandler, BoundSql boundSql) { this.configuration = configuration; - this.executor = executor; this.objectFactory = mappedStatement.getConfiguration().getObjectFactory(); this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry(); this.mappedStatement = mappedStatement; this.rowOffset = rowOffset; this.rowLimit = rowLimit; this.parameterObject = parameterHandler.getParameterObject(); - this.nestedResultObjects = new HashMap(); this.resultHandler = resultHandler; this.boundSql = boundSql; + + this.nestedResultSetHandler = new NestedResultSetHandler(configuration,mappedStatement,this); + this.nestedSelectHandler = new NestedSelectHandler(executor,configuration,mappedStatement); + this.discriminatorHandler = new DiscriminatorHandler(mappedStatement); } public List handleResultSets(Statement statement) throws SQLException { @@ -53,6 +51,7 @@ if (rs != null) { try { for (int i = 0, n = mappedStatement.getResultMaps().size(); i < n; i++) { + ResultMap resultMap = mappedStatement.getResultMaps().get(i); ErrorContext.instance().activity("handling result set").object(resultMap.getId()); if (resultHandler == null) { @@ -63,8 +62,8 @@ handleResults(rs, resultMap, resultHandler, rowOffset, rowLimit); } if (moveToNextResultsSafely(statement)) { + nestedResultSetHandler.reset(); rs = statement.getResultSet(); - nestedResultObjects.clear(); } else { break; } @@ -88,8 +87,6 @@ ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT) { if ("java.sql.ResultSet".equalsIgnoreCase(parameterMapping.getJavaType().getName())) { - // TODO: We need an easy way to unit test this without installing Oracle. - // Mocks are obvious, but will they be effective enough? DBunit? ResultSet rs = (ResultSet) callableStatement.getObject(i + 1); String resultMapId = parameterMapping.getResultMapId(); if (resultMapId != null) { @@ -114,8 +111,7 @@ ResultContext context = new ResultContext(); while ((maxResults == Executor.NO_ROW_LIMIT || context.getResultCount() < maxResults) && !context.isStopped() && rs.next()) { - currentNestedKey = null; - ResultMap rm = resolveSubMap(rs, resultMap); + ResultMap rm = discriminatorHandler.resolveSubMap(rs, resultMap); Object resultObject = loadResultObject(rs, rm, new Reference(false)); if (resultObject != NO_VALUE) { if (resultObject instanceof PlatformTypeHolder) { @@ -128,28 +124,34 @@ } } - private Object loadResultObject(ResultSet rs, ResultMap rm, Reference<Boolean> foundValues) throws SQLException { + public Object loadResultObject(ResultSet rs, ResultMap rm, Reference<Boolean> foundValues) throws SQLException { if (rm.getType() == null) { throw new ExecutorException("The result class was null when trying to get results for ResultMap " + rm.getId()); } - Object resultObject = createResultObject(rs, rm); ResultLoaderRegistry lazyLoader = null; if (this.mappedStatement.getConfiguration().isEnhancementEnabled()) { lazyLoader = new ResultLoaderRegistry(); resultObject = ResultObjectProxy.createProxy(rm.getType(), resultObject, lazyLoader); } - List<ResultMapping> appliedResultMappings = new ArrayList<ResultMapping>(); resultObject = mapResults(rs, rm, lazyLoader, resultObject, appliedResultMappings, foundValues); - resultObject = processNestedJoinResults(rs, appliedResultMappings, resultObject); + resultObject = nestedResultSetHandler.processNestedJoinResults(rs, appliedResultMappings, resultObject); return resultObject; } private Object mapResults(ResultSet rs, ResultMap rm, ResultLoaderRegistry lazyLoader, Object resultObject, List<ResultMapping> appliedResultMappings, Reference<Boolean> foundValues) throws SQLException { - MetaObject metaResultObject = MetaObject.forObject(resultObject); - Set<String> propSet = new HashSet<String>(); Set<String> colSet = new HashSet<String>(); + Map<String, ResultMapping> autoMappings = createResultMappingsForColumnsThatMatchPropertyNames(rs, resultObject, colSet); + // Map results/ignore missing + resultObject = mapSpecifiedResultsIgnoringMissingColumns(rs, rm, lazyLoader, resultObject, appliedResultMappings, foundValues, colSet, autoMappings); + // Automap remaining results + resultObject = automaticallyMapRemainingColumnsThatMatchPropertyNames(rs, rm, lazyLoader, resultObject, appliedResultMappings, foundValues, autoMappings); + return resultObject; + } + + private Map<String, ResultMapping> createResultMappingsForColumnsThatMatchPropertyNames(ResultSet rs, Object resultObject, Set<String> colSet) throws SQLException { + MetaObject metaResultObject = MetaObject.forObject(resultObject); Map<String, ResultMapping> autoMappings = new HashMap<String, ResultMapping>(); ResultSetMetaData rsmd = rs.getMetaData(); for (int i = 1, n = rsmd.getColumnCount(); i <= n; i++) { @@ -159,7 +161,6 @@ String propName = metaResultObject.findProperty(columnLabel); colSet.add(columnLabel); if (propName != null && metaResultObject.hasSetter(propName)) { - propSet.add(propName); Class javaType = metaResultObject.getSetterType(propName); TypeHandler typeHandler = typeHandlerRegistry.getTypeHandler(javaType); ResultMapping resultMapping = new ResultMapping.Builder(configuration, propName, columnLabel, typeHandler) @@ -167,7 +168,21 @@ autoMappings.put(propName, resultMapping); } } - // Map results/ignore missing + return autoMappings; + } + + private Object automaticallyMapRemainingColumnsThatMatchPropertyNames(ResultSet rs, ResultMap rm, ResultLoaderRegistry lazyLoader, Object resultObject, List<ResultMapping> appliedResultMappings, Reference<Boolean> foundValues, Map<String, ResultMapping> autoMappings) throws SQLException { + for (String key : autoMappings.keySet()) { + ResultMapping autoMapping = autoMappings.get(key); + if (autoMapping.getTypeHandler() != null) { + resultObject = processResult(rs, rm, autoMapping, lazyLoader, resultObject, foundValues); + appliedResultMappings.add(autoMapping); + } + } + return resultObject; + } + + private Object mapSpecifiedResultsIgnoringMissingColumns(ResultSet rs, ResultMap rm, ResultLoaderRegistry lazyLoader, Object resultObject, List<ResultMapping> appliedResultMappings, Reference<Boolean> foundValues, Set<String> colSet, Map<String, ResultMapping> autoMappings) throws SQLException { for (ResultMapping resultMapping : rm.getPropertyResultMappings()) { String propName = resultMapping.getProperty(); String colName = resultMapping.getColumn(); @@ -178,14 +193,6 @@ appliedResultMappings.add(resultMapping); } } - // Automap remaining results - for (String key : autoMappings.keySet()) { - ResultMapping autoMapping = autoMappings.get(key); - if (autoMapping.getTypeHandler() != null) { - resultObject = processResult(rs, rm, autoMapping, lazyLoader, resultObject, foundValues); - appliedResultMappings.add(autoMapping); - } - } return resultObject; } @@ -210,41 +217,9 @@ return resultObject; } - private Object processNestedSelectResult(MappedStatement nestedQuery, ResultMap rm, ResultMapping resultMapping, ResultLoaderRegistry lazyLoader, Object parameterObject, Object resultObject) { - MetaObject metaResultObject = MetaObject.forObject(resultObject); - Object value = null; - try { - if (parameterObject != null) { - CacheKey key = executor.createCacheKey(nestedQuery, parameterObject, Executor.NO_ROW_OFFSET, Executor.NO_ROW_LIMIT); - if (executor.isCached(nestedQuery, key)) { - executor.deferLoad(nestedQuery, metaResultObject, resultMapping.getProperty(), key); - } else { - ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, parameterObject, resultMapping.getJavaType()); - if (lazyLoader == null) { - value = resultLoader.loadResult(); - } else { - lazyLoader.registerLoader(resultMapping.getProperty(), metaResultObject, resultLoader); - } - } - } - } catch (Exception e) { - throw new ExecutorException("Error setting nested bean property. Cause: " + e, e); - } - if (typeHandlerRegistry.hasTypeHandler(rm.getType())) { - resultObject = value; - } else if (value != null) { - metaResultObject.setValue(resultMapping.getProperty(), value); - } - return resultObject; - } - private Object processResult(ResultSet rs, ResultMap rm, ResultMapping resultMapping, ResultLoaderRegistry lazyLoader, Object resultObject, Reference<Boolean> foundValues) throws SQLException { if (resultMapping.getNestedQueryId() != null) { - Configuration configuration = mappedStatement.getConfiguration(); - MappedStatement nestedQuery = configuration.getMappedStatement(resultMapping.getNestedQueryId()); - Class parameterType = nestedQuery.getParameterMap().getType(); - Object parameterObject = prepareNestedParameterObject(rs, resultMapping, parameterType); - resultObject = processNestedSelectResult(nestedQuery, rm, resultMapping, lazyLoader, parameterObject, resultObject); + resultObject = nestedSelectHandler.processNestedSelect(rs, rm, resultMapping, lazyLoader, resultObject); } else if (resultMapping.getNestedResultMapId() == null) { resultObject = processSimpleResult(rs, rm, resultMapping, resultObject, foundValues); } @@ -264,130 +239,11 @@ return resultObject; } - private Object processNestedJoinResults(ResultSet rs, List<ResultMapping> resultMappings, Object resultObject) { - CacheKey previousKey = currentNestedKey; - try { - currentNestedKey = createUniqueResultKey(resultMappings, resultObject, currentNestedKey); - if (nestedResultObjects.containsKey(currentNestedKey)) { - // Unique key is already known, so get the existing result object and process additional results. - resultObject = NO_VALUE; - } else if (currentNestedKey != null) { - // Unique key is NOT known, so create a new result object and then process additional results. - nestedResultObjects.put(currentNestedKey, resultObject); - } - Object knownResultObject = nestedResultObjects.get(currentNestedKey); - if (knownResultObject != null && knownResultObject != NO_VALUE) { - for (ResultMapping resultMapping : resultMappings) { - Configuration configuration = mappedStatement.getConfiguration(); - String nestedResultMapId = resultMapping.getNestedResultMapId(); - if (nestedResultMapId != null) { - ResultMap nestedResultMap = configuration.getResultMap(nestedResultMapId); - try { - - // get the discriminated submap if it exists - nestedResultMap = resolveSubMap(rs, nestedResultMap); - - Class type = resultMapping.getJavaType(); - String propertyName = resultMapping.getProperty(); - - MetaObject metaObject = MetaObject.forObject(knownResultObject); - Object propertyValue = metaObject.getValue(propertyName); - if (propertyValue == null) { - if (type == null) { - type = metaObject.getSetterType(propertyName); - } - - try { - // create the object if is it a Collection. If not a Collection - // then we will just set the property to the object created - // in processing the nested result map - if (Collection.class.isAssignableFrom(type)) { - propertyValue = objectFactory.create(type); - metaObject.setValue(propertyName, propertyValue); - } - } catch (Exception e) { - throw new ExecutorException("Error instantiating collection property for result '" + resultMapping.getProperty() + "'. Cause: " + e, e); - } - } - - Reference<Boolean> foundValues = new Reference(false); - Object nestedResultObject = loadResultObject(rs, nestedResultMap, foundValues); - if (nestedResultObject != null && nestedResultObject != NO_VALUE) { - if (propertyValue != null && propertyValue instanceof Collection) { - if (foundValues.get()) { - ((Collection) propertyValue).add(nestedResultObject); - } - } else { - metaObject.setValue(propertyName, nestedResultObject); - } - } - } catch (Exception e) { - throw new ExecutorException("Error getting nested result map values for '" + resultMapping.getProperty() + "'. Cause: " + e, e); - } - } - } - } - } finally { - currentNestedKey = previousKey; - } - return resultObject; - } - - private Object prepareNestedParameterObject(ResultSet rs, ResultMapping resultMapping, Class parameterType) throws SQLException { - Object parameterObject; - if (typeHandlerRegistry.hasTypeHandler(parameterType)) { - TypeHandler th = typeHandlerRegistry.getTypeHandler(parameterType); - parameterObject = th.getResult(rs, resultMapping.getColumn()); - } else { - if (parameterType == null) { - parameterObject = new HashMap(); - } else { - parameterObject = objectFactory.create(parameterType); - } - if (resultMapping.isCompositeResult()) { - MetaObject metaObject = MetaObject.forObject(parameterObject); - for (ResultMapping innerResultMapping : resultMapping.getComposites()) { - Class propType = metaObject.getSetterType(innerResultMapping.getProperty()); - TypeHandler typeHandler = typeHandlerRegistry.getTypeHandler(propType); - Object propValue = typeHandler.getResult(rs, innerResultMapping.getColumn()); - metaObject.setValue(innerResultMapping.getProperty(), propValue); - } - } else { - String columnName = resultMapping.getColumn(); - TypeHandler typeHandler = typeHandlerRegistry.getTypeHandler(parameterType); - if (typeHandler == null) { - typeHandler = typeHandlerRegistry.getUnkownTypeHandler(); - } - parameterObject = typeHandler.getResult(rs, columnName); - } - } - return parameterObject; - } ////////////////////////////////////////// // UTILITY METHODS ////////////////////////////////////////// - private ResultMap resolveSubMap(ResultSet rs, ResultMap rm) throws SQLException { - ResultMap subMap = rm; - Discriminator discriminator = rm.getDiscriminator(); - if (discriminator != null) { - ResultMapping resultMapping = discriminator.getResultMapping(); - Object value = getPrimitiveResultMappingValue(rs, resultMapping); - String subMapId = discriminator.getMapIdFor(String.valueOf(value)); - - try { - subMap = mappedStatement.getConfiguration().getResultMap(subMapId); - } catch (Exception e) { - subMap = rm; - } - - if (subMap != rm) { - subMap = resolveSubMap(rs, subMap); - } - } - return subMap; - } private Object getPrimitiveResultMappingValue(ResultSet rs, ResultMapping resultMapping) throws SQLException { Object value; @@ -400,31 +256,6 @@ return value; } - private CacheKey createUniqueResultKey(List<ResultMapping> resultMappings, Object resultObject, CacheKey parentCacheKey) { - if (resultObject == null) { - return null; - } else { - MetaObject metaResultObject = MetaObject.forObject(resultObject); - CacheKey cacheKey = new CacheKey(); - cacheKey.update(parentCacheKey); - boolean updated = false; - if (typeHandlerRegistry.hasTypeHandler(resultObject.getClass())) { - cacheKey.update(resultObject); - } else { - for (ResultMapping resultMapping : resultMappings) { - if (resultMapping.getNestedQueryId() == null) { - String propName = resultMapping.getProperty(); - if (propName != null) { - cacheKey.update(metaResultObject.getValue(propName)); - updated = true; - } - } - } - } - return updated ? cacheKey : null; - } - } - private ResultSet getFirstResultSet(Statement statement) throws SQLException { ResultSet rs = null; boolean hasMoreResults = true; @@ -468,20 +299,5 @@ } } - private static class Reference<T> { - private T value; - - private Reference(T value) { - this.value = value; - } - - public T get() { - return value; - } - - public void set(T value) { - this.value = value; - } - } } Added: ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/DiscriminatorHandler.java URL: http://svn.apache.org/viewvc/ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/DiscriminatorHandler.java?rev=811723&view=auto ============================================================================== --- ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/DiscriminatorHandler.java (added) +++ ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/DiscriminatorHandler.java Sat Sep 5 22:46:17 2009 @@ -0,0 +1,53 @@ +package org.apache.ibatis.executor.resultset; + +import org.apache.ibatis.mapping.ResultMap; +import org.apache.ibatis.mapping.Discriminator; +import org.apache.ibatis.mapping.ResultMapping; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.type.TypeHandler; +import org.apache.ibatis.executor.ExecutorException; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class DiscriminatorHandler { + + private final MappedStatement mappedStatement; + + public DiscriminatorHandler(MappedStatement mappedStatement) { + this.mappedStatement = mappedStatement; + } + + public ResultMap resolveSubMap(ResultSet rs, ResultMap rm) throws SQLException { + ResultMap subMap = rm; + Discriminator discriminator = rm.getDiscriminator(); + if (discriminator != null) { + ResultMapping resultMapping = discriminator.getResultMapping(); + Object value = getPrimitiveResultMappingValue(rs, resultMapping); + String subMapId = discriminator.getMapIdFor(String.valueOf(value)); + + try { + subMap = mappedStatement.getConfiguration().getResultMap(subMapId); + } catch (Exception e) { + subMap = rm; + } + + if (subMap != rm) { + subMap = resolveSubMap(rs, subMap); + } + } + return subMap; + } + + private Object getPrimitiveResultMappingValue(ResultSet rs, ResultMapping resultMapping) throws SQLException { + Object value; + TypeHandler typeHandler = resultMapping.getTypeHandler(); + if (typeHandler != null) { + value = typeHandler.getResult(rs, resultMapping.getColumn()); + } else { + throw new ExecutorException("No type handler could be found to map the property '" + resultMapping.getProperty() + "' to the column '" + resultMapping.getColumn() + "'. One or both of the types, or the combination of types is not supported."); + } + return value; + } + +} Added: ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/NestedResultSetHandler.java URL: http://svn.apache.org/viewvc/ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/NestedResultSetHandler.java?rev=811723&view=auto ============================================================================== --- ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/NestedResultSetHandler.java (added) +++ ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/NestedResultSetHandler.java Sat Sep 5 22:46:17 2009 @@ -0,0 +1,141 @@ +package org.apache.ibatis.executor.resultset; + +import static org.apache.ibatis.executor.resultset.NoValue.*; + +import org.apache.ibatis.mapping.ResultMapping; +import org.apache.ibatis.mapping.Configuration; +import org.apache.ibatis.mapping.ResultMap; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.cache.CacheKey; +import org.apache.ibatis.reflection.MetaObject; +import org.apache.ibatis.reflection.factory.ObjectFactory; +import org.apache.ibatis.executor.ExecutorException; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.type.TypeHandlerRegistry; + +import java.sql.ResultSet; +import java.util.List; +import java.util.Collection; +import java.util.Map; +import java.util.HashMap; + +public class NestedResultSetHandler { + + private final ObjectFactory objectFactory; + private final TypeHandlerRegistry typeHandlerRegistry; + private final MappedStatement mappedStatement; + private final Map nestedResultObjects; + + private CacheKey currentNestedKey; + private final DiscriminatorHandler discriminatorHandler; + private DefaultResultSetHandler resultSetHandler; + + public NestedResultSetHandler(Configuration configuration, MappedStatement mappedStatement, DefaultResultSetHandler resultSetHandler) { + this.objectFactory = configuration.getObjectFactory(); + this.typeHandlerRegistry = configuration.getTypeHandlerRegistry(); + this.mappedStatement = mappedStatement; + this.nestedResultObjects = new HashMap(); + this.discriminatorHandler = new DiscriminatorHandler(mappedStatement); + this.resultSetHandler = resultSetHandler; + } + + public Object processNestedJoinResults(ResultSet rs, List<ResultMapping> resultMappings, Object resultObject) { + CacheKey previousKey = currentNestedKey; + try { + currentNestedKey = createUniqueResultKey(resultMappings, resultObject, currentNestedKey); + if (nestedResultObjects.containsKey(currentNestedKey)) { + // Unique key is already known, so get the existing result object and process additional results. + resultObject = NO_VALUE; + } else if (currentNestedKey != null) { + // Unique key is NOT known, so create a new result object and then process additional results. + nestedResultObjects.put(currentNestedKey, resultObject); + } + Object knownResultObject = nestedResultObjects.get(currentNestedKey); + if (knownResultObject != null && knownResultObject != NO_VALUE) { + for (ResultMapping resultMapping : resultMappings) { + Configuration configuration = mappedStatement.getConfiguration(); + String nestedResultMapId = resultMapping.getNestedResultMapId(); + if (nestedResultMapId != null) { + ResultMap nestedResultMap = configuration.getResultMap(nestedResultMapId); + try { + + // get the discriminated submap if it exists + nestedResultMap = discriminatorHandler.resolveSubMap(rs, nestedResultMap); + + Class type = resultMapping.getJavaType(); + String propertyName = resultMapping.getProperty(); + + MetaObject metaObject = MetaObject.forObject(knownResultObject); + Object propertyValue = metaObject.getValue(propertyName); + if (propertyValue == null) { + if (type == null) { + type = metaObject.getSetterType(propertyName); + } + + try { + // create the object if is it a Collection. If not a Collection + // then we will just set the property to the object created + // in processing the nested result map + if (Collection.class.isAssignableFrom(type)) { + propertyValue = objectFactory.create(type); + metaObject.setValue(propertyName, propertyValue); + } + } catch (Exception e) { + throw new ExecutorException("Error instantiating collection property for result '" + resultMapping.getProperty() + "'. Cause: " + e, e); + } + } + + Reference<Boolean> foundValues = new Reference(false); + Object nestedResultObject = resultSetHandler.loadResultObject(rs, nestedResultMap, foundValues); + if (nestedResultObject != null && nestedResultObject != NO_VALUE) { + if (propertyValue != null && propertyValue instanceof Collection) { + if (foundValues.get()) { + ((Collection) propertyValue).add(nestedResultObject); + } + } else { + metaObject.setValue(propertyName, nestedResultObject); + } + } + } catch (Exception e) { + throw new ExecutorException("Error getting nested result map values for '" + resultMapping.getProperty() + "'. Cause: " + e, e); + } + } + } + } + } finally { + currentNestedKey = previousKey; + } + return resultObject; + } + + private CacheKey createUniqueResultKey(List<ResultMapping> resultMappings, Object resultObject, CacheKey parentCacheKey) { + if (resultObject == null) { + return null; + } else { + MetaObject metaResultObject = MetaObject.forObject(resultObject); + CacheKey cacheKey = new CacheKey(); + cacheKey.update(parentCacheKey); + boolean updated = false; + if (typeHandlerRegistry.hasTypeHandler(resultObject.getClass())) { + cacheKey.update(resultObject); + } else { + for (ResultMapping resultMapping : resultMappings) { + if (resultMapping.getNestedQueryId() == null) { + String propName = resultMapping.getProperty(); + if (propName != null) { + cacheKey.update(metaResultObject.getValue(propName)); + updated = true; + } + } + } + } + return updated ? cacheKey : null; + } + } + + + public void reset() { + nestedResultObjects.clear(); + currentNestedKey = null; + } +} Added: ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/NestedSelectHandler.java URL: http://svn.apache.org/viewvc/ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/NestedSelectHandler.java?rev=811723&view=auto ============================================================================== --- ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/NestedSelectHandler.java (added) +++ ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/NestedSelectHandler.java Sat Sep 5 22:46:17 2009 @@ -0,0 +1,108 @@ +package org.apache.ibatis.executor.resultset; + +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.ResultMap; +import org.apache.ibatis.mapping.ResultMapping; +import org.apache.ibatis.mapping.Configuration; +import org.apache.ibatis.executor.loader.ResultLoaderRegistry; +import org.apache.ibatis.executor.loader.ResultLoader; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.executor.ExecutorException; +import org.apache.ibatis.reflection.MetaObject; +import org.apache.ibatis.reflection.factory.ObjectFactory; +import org.apache.ibatis.cache.CacheKey; +import org.apache.ibatis.type.TypeHandler; +import org.apache.ibatis.type.TypeHandlerRegistry; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; + +public class NestedSelectHandler { + + private final Configuration configuration; + private final Executor executor; + private final ObjectFactory objectFactory; + private final TypeHandlerRegistry typeHandlerRegistry; + private final MappedStatement mappedStatement; + + public NestedSelectHandler(Executor executor, Configuration configuration, MappedStatement mappedStatement) { + this.executor = executor; + this.configuration = configuration; + this.mappedStatement = mappedStatement; + this.objectFactory = configuration.getObjectFactory(); + this.typeHandlerRegistry = configuration.getTypeHandlerRegistry(); + } + + public Object processNestedSelect(ResultSet rs, ResultMap rm, ResultMapping resultMapping, ResultLoaderRegistry lazyLoader, Object resultObject) + throws SQLException { + Configuration configuration = mappedStatement.getConfiguration(); + MappedStatement nestedQuery = configuration.getMappedStatement(resultMapping.getNestedQueryId()); + Class parameterType = nestedQuery.getParameterMap().getType(); + Object parameterObject = prepareNestedParameterObject(rs, resultMapping, parameterType); + resultObject = processNestedSelectResult(nestedQuery, rm, resultMapping, lazyLoader, parameterObject, resultObject); + return resultObject; + } + + private Object processNestedSelectResult(MappedStatement nestedQuery, ResultMap rm, ResultMapping resultMapping, ResultLoaderRegistry lazyLoader, Object parameterObject, Object resultObject) { + MetaObject metaResultObject = MetaObject.forObject(resultObject); + Object value = null; + try { + if (parameterObject != null) { + CacheKey key = executor.createCacheKey(nestedQuery, parameterObject, Executor.NO_ROW_OFFSET, Executor.NO_ROW_LIMIT); + if (executor.isCached(nestedQuery, key)) { + executor.deferLoad(nestedQuery, metaResultObject, resultMapping.getProperty(), key); + } else { + ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, parameterObject, resultMapping.getJavaType()); + if (lazyLoader == null) { + value = resultLoader.loadResult(); + } else { + lazyLoader.registerLoader(resultMapping.getProperty(), metaResultObject, resultLoader); + } + } + } + } catch (Exception e) { + throw new ExecutorException("Error setting nested bean property. Cause: " + e, e); + } + if (typeHandlerRegistry.hasTypeHandler(rm.getType())) { + resultObject = value; + } else if (value != null) { + metaResultObject.setValue(resultMapping.getProperty(), value); + } + return resultObject; + } + + + private Object prepareNestedParameterObject(ResultSet rs, ResultMapping resultMapping, Class parameterType) throws SQLException { + Object parameterObject; + if (typeHandlerRegistry.hasTypeHandler(parameterType)) { + TypeHandler th = typeHandlerRegistry.getTypeHandler(parameterType); + parameterObject = th.getResult(rs, resultMapping.getColumn()); + } else { + if (parameterType == null) { + parameterObject = new HashMap(); + } else { + parameterObject = objectFactory.create(parameterType); + } + if (resultMapping.isCompositeResult()) { + MetaObject metaObject = MetaObject.forObject(parameterObject); + for (ResultMapping innerResultMapping : resultMapping.getComposites()) { + Class propType = metaObject.getSetterType(innerResultMapping.getProperty()); + TypeHandler typeHandler = typeHandlerRegistry.getTypeHandler(propType); + Object propValue = typeHandler.getResult(rs, innerResultMapping.getColumn()); + metaObject.setValue(innerResultMapping.getProperty(), propValue); + } + } else { + String columnName = resultMapping.getColumn(); + TypeHandler typeHandler = typeHandlerRegistry.getTypeHandler(parameterType); + if (typeHandler == null) { + typeHandler = typeHandlerRegistry.getUnkownTypeHandler(); + } + parameterObject = typeHandler.getResult(rs, columnName); + } + } + return parameterObject; + } + + +} Added: ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/NoValue.java URL: http://svn.apache.org/viewvc/ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/NoValue.java?rev=811723&view=auto ============================================================================== --- ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/NoValue.java (added) +++ ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/NoValue.java Sat Sep 5 22:46:17 2009 @@ -0,0 +1,5 @@ +package org.apache.ibatis.executor.resultset; + +public enum NoValue { + NO_VALUE +} Added: ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/Reference.java URL: http://svn.apache.org/viewvc/ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/Reference.java?rev=811723&view=auto ============================================================================== --- ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/Reference.java (added) +++ ibatis/java/ibatis-3/trunk/ibatis-3-core/src/main/java/org/apache/ibatis/executor/resultset/Reference.java Sat Sep 5 22:46:17 2009 @@ -0,0 +1,17 @@ +package org.apache.ibatis.executor.resultset; + +public class Reference<T> { + private T value; + + public Reference(T value) { + this.value = value; + } + + public T get() { + return value; + } + + public void set(T value) { + this.value = value; + } +} \ No newline at end of file Modified: ibatis/java/ibatis-3/trunk/ibatis-3-core/src/test/java/org/apache/ibatis/builder/BlogMapper.xml URL: http://svn.apache.org/viewvc/ibatis/java/ibatis-3/trunk/ibatis-3-core/src/test/java/org/apache/ibatis/builder/BlogMapper.xml?rev=811723&r1=811722&r2=811723&view=diff ============================================================================== --- ibatis/java/ibatis-3/trunk/ibatis-3-core/src/test/java/org/apache/ibatis/builder/BlogMapper.xml (original) +++ ibatis/java/ibatis-3/trunk/ibatis-3-core/src/test/java/org/apache/ibatis/builder/BlogMapper.xml Sat Sep 5 22:46:17 2009 @@ -33,6 +33,7 @@ <resultMap id="joinedComment" type="domain.blog.Comment"> <id property="id" column="comment_id"/> + <!--<association property="post" column="post_id" resultMap="joinedPost"/>--> </resultMap> <resultMap id="joinedTag" type="domain.blog.Tag">