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">


Reply via email to