You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2012/04/01 13:06:47 UTC

svn commit: r1308073 - in /cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src: main/java/org/apache/cayenne/access/ main/java/org/apache/cayenne/access/jdbc/ main/java/org/apache/cayenne/query/ test/java/org/apache/cayenne/access/ test/java/or...

Author: aadamchik
Date: Sun Apr  1 11:06:46 2012
New Revision: 1308073

URL: http://svn.apache.org/viewvc?rev=1308073&view=rev
Log:
CAY-1681 Third prefetch kind - DISJOINT_BY_ID

patch by Andrei Veprev

Added:
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextDisjointByIdPrefetchTest.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/Bag.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/Ball.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/Box.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/BoxInfo.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/Thing.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Bag.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Ball.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Box.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_BoxInfo.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Thing.java
Modified:
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolver.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorTreeBuilder.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/SelectAction.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/PrefetchProcessor.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/PrefetchSelectQuery.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/PrefetchTreeNode.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/SelectQueryPrefetchRouterAction.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Testmap.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/resources/testmap.map.xml

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolver.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolver.java?rev=1308073&r1=1308072&r2=1308073&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolver.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolver.java Sun Apr  1 11:06:46 2012
@@ -28,7 +28,13 @@ import java.util.Set;
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.DataRow;
 import org.apache.cayenne.Persistent;
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.map.DbJoin;
+import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.map.ObjRelationship;
 import org.apache.cayenne.query.PrefetchProcessor;
+import org.apache.cayenne.query.PrefetchSelectQuery;
 import org.apache.cayenne.query.PrefetchTreeNode;
 import org.apache.cayenne.query.QueryMetadata;
 import org.apache.cayenne.reflect.ClassDescriptor;
@@ -53,7 +59,7 @@ class HierarchicalObjectResolver {
     }
 
     HierarchicalObjectResolver(DataContext context, QueryMetadata metadata,
-            ClassDescriptor descriptor, boolean needToSaveDuplicates) {
+                               ClassDescriptor descriptor, boolean needToSaveDuplicates) {
         this(context, metadata);
         this.descriptor = descriptor;
         this.needToSaveDuplicates = needToSaveDuplicates;
@@ -118,6 +124,65 @@ class HierarchicalObjectResolver {
             return true;
         }
 
+        public boolean startDisjointByIdPrefetch(PrefetchTreeNode node) {
+            PrefetchProcessorNode processorNode = (PrefetchProcessorNode) node;
+
+            if (node.getParent().isPhantom()) {
+                // TODO: doing nothing in current implementation if parent node is phantom
+                return true;
+            }
+
+            PrefetchProcessorNode parentProcessorNode = (PrefetchProcessorNode) processorNode.getParent();
+            ObjRelationship relationship = processorNode.getIncoming().getRelationship();
+
+            PrefetchSelectQuery query = new PrefetchSelectQuery(node.getPath(), relationship);
+
+            for (Object dataRow : parentProcessorNode.getDataRows()) {
+                List<DbRelationship> dbRelationships = relationship.getReverseRelationship().getDbRelationships();
+                DbRelationship lastDbRelationship = dbRelationships.get(dbRelationships.size() - 1);
+                
+                String pathPrefix = "";
+                if (dbRelationships.size() > 1) {
+                    // we need path prefix for flattened relationships
+                    List<DbRelationship> headingDbRelationships
+                            = dbRelationships.subList(0, dbRelationships.size() - 1);
+                    StringBuilder pathPrefixBuilder = new StringBuilder();
+                    for (DbRelationship dbRelationship : headingDbRelationships) {
+                        pathPrefixBuilder.append(dbRelationship.getName());
+                    }
+                    pathPrefix = pathPrefixBuilder.toString() + ".";
+                }
+
+                Expression allJoinsQualifier = null;
+                for (DbJoin join : lastDbRelationship.getJoins()) {
+                    // we have reversed db relationship here, so target and source are interchanged
+                    Object targetValue = ((DataRow) dataRow).get(join.getTargetName());
+                    Expression joinQualifier
+                            = ExpressionFactory.matchDbExp(pathPrefix + join.getSourceName(), targetValue);
+                    if (allJoinsQualifier == null) {
+                        allJoinsQualifier = joinQualifier;
+                    } else {
+                        allJoinsQualifier = allJoinsQualifier.andExp(joinQualifier);
+                    }
+                }
+
+                query.orQualifier(allJoinsQualifier);
+            }
+
+            query.setFetchingDataRows(true);
+            if (relationship.isSourceIndependentFromTargetChange()) {
+                // setup extra result columns to be able to relate result rows to the parent
+                // result objects.
+                query.addResultPath("db:"
+                        + relationship.getReverseDbRelationshipPath());
+            }
+
+            List dataRows = context.performQuery(query);
+            processorNode.setDataRows(dataRows);
+
+            return startDisjointPrefetch(node);
+        }
+
         public boolean startJointPrefetch(PrefetchTreeNode node) {
 
             // delegate processing of the top level joint prefetch to a joint processor,
@@ -221,6 +286,10 @@ class HierarchicalObjectResolver {
             return node == rootNode;
         }
 
+        public boolean startDisjointByIdPrefetch(PrefetchTreeNode node) {
+            return startDisjointByIdPrefetch(node);
+        }
+
         public boolean startJointPrefetch(PrefetchTreeNode node) {
             PrefetchProcessorJointNode processorNode = (PrefetchProcessorJointNode) node;
 
@@ -278,6 +347,10 @@ class HierarchicalObjectResolver {
             return true;
         }
 
+        public boolean startDisjointByIdPrefetch(PrefetchTreeNode node) {
+            return startDisjointPrefetch(node);
+        }
+
         public boolean startJointPrefetch(PrefetchTreeNode node) {
             PrefetchProcessorJointNode processorNode = (PrefetchProcessorJointNode) node;
 

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorTreeBuilder.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorTreeBuilder.java?rev=1308073&r1=1308072&r2=1308073&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorTreeBuilder.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorTreeBuilder.java Sun Apr  1 11:06:46 2012
@@ -79,17 +79,20 @@ final class PrefetchProcessorTreeBuilder
         }
     }
 
-    public boolean startDisjointPrefetch(PrefetchTreeNode node) {
-
-        // look ahead for joint children as joint children will require a different
-        // node type.
-
+    private PrefetchProcessorNode createDisjointNode(PrefetchTreeNode node) {
         // TODO, Andrus, 11/16/2005 - minor inefficiency: 'adjacentJointNodes' would
         // grab ALL nodes, we just need to find first and stop...
         PrefetchProcessorNode decorated = !node.adjacentJointNodes().isEmpty()
                 ? new PrefetchProcessorJointNode(getParent(), node.getName())
                 : new PrefetchProcessorNode(getParent(), node.getName());
         decorated.setPhantom(false);
+        return decorated;
+    }
+
+    public boolean startDisjointPrefetch(PrefetchTreeNode node) {
+        // look ahead for joint children as joint children will require a different
+        // node type.
+        PrefetchProcessorNode decorated = createDisjointNode(node);
 
         // semantics has to be "DISJOINT" even if the node is joint, as semantics
         // defines relationship with parent..
@@ -97,6 +100,13 @@ final class PrefetchProcessorTreeBuilder
         return addNode(decorated);
     }
 
+    public boolean startDisjointByIdPrefetch(PrefetchTreeNode node) {
+        // see startDisjointPrefetch for comments
+        PrefetchProcessorNode decorated = createDisjointNode(node);
+        decorated.setSemantics(PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS);
+        return addNode(decorated);
+    }
+
     public boolean startJointPrefetch(PrefetchTreeNode node) {
         PrefetchProcessorJointNode decorated = new PrefetchProcessorJointNode(
                 getParent(),

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/SelectAction.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/SelectAction.java?rev=1308073&r1=1308072&r2=1308073&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/SelectAction.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/SelectAction.java Sun Apr  1 11:06:46 2012
@@ -115,6 +115,11 @@ public class SelectAction extends BaseSQ
                         return rootPrefetch == node;
                     }
 
+                    public boolean startDisjointByIdPrefetch(PrefetchTreeNode node) {
+                        // continue to children only if we are at root
+                        return rootPrefetch == node;
+                    }
+
                     public boolean startUnknownPrefetch(PrefetchTreeNode node) {
                         // continue to children only if we are at root
                         return rootPrefetch == node;

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/PrefetchProcessor.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/PrefetchProcessor.java?rev=1308073&r1=1308072&r2=1308073&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/PrefetchProcessor.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/PrefetchProcessor.java Sun Apr  1 11:06:46 2012
@@ -22,7 +22,7 @@ package org.apache.cayenne.query;
 /**
  * A hierarchical visitor interface for traversing a tree of PrefetchTreeNodes. If any of
  * the processing methods return false, node's children will be skipped from traversal.
- * 
+ *
  * @since 1.2
  * @see org.apache.cayenne.query.PrefetchTreeNode#traverse(PrefetchProcessor)
  */
@@ -32,6 +32,8 @@ public interface PrefetchProcessor {
 
     boolean startDisjointPrefetch(PrefetchTreeNode node);
 
+    boolean startDisjointByIdPrefetch(PrefetchTreeNode prefetchTreeNode);
+
     boolean startJointPrefetch(PrefetchTreeNode node);
 
     boolean startUnknownPrefetch(PrefetchTreeNode node);

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/PrefetchSelectQuery.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/PrefetchSelectQuery.java?rev=1308073&r1=1308072&r2=1308073&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/PrefetchSelectQuery.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/PrefetchSelectQuery.java Sun Apr  1 11:06:46 2012
@@ -30,10 +30,13 @@ import org.apache.cayenne.util.Util;
 /**
  * A SelectQuery to perform a prefetch based on another query. Used internally by Cayenne
  * and is normally never used directly.
- * 
  */
 public class PrefetchSelectQuery extends SelectQuery {
 
+    /**
+     * @deprecated since 3.1 unused
+     */
+    @Deprecated
     protected SelectQuery parentQuery;
 
     /**
@@ -52,9 +55,23 @@ public class PrefetchSelectQuery extends
 
     /**
      * Creates a new disjoint prefetch select query.
+     *
+     * @since 3.1
+     */
+    public PrefetchSelectQuery(String prefetchPath, ObjRelationship lastPrefetchHint) {
+        setRoot(lastPrefetchHint.getTargetEntity());
+        this.prefetchPath = prefetchPath;
+        this.lastPrefetchHint = lastPrefetchHint;
+    }
+
+    /**
+     * Creates a new disjoint prefetch select query.
      * 
      * @since 1.2
+     * @deprecated Since 3.1 use another constructor without parentQuery parameter
+     *             instead.
      */
+    @Deprecated
     public PrefetchSelectQuery(SelectQuery parentQuery, String prefetchPath,
             ObjRelationship lastPrefetchHint) {
 
@@ -78,8 +95,6 @@ public class PrefetchSelectQuery extends
 
     /**
      * Returns the prefetchPath.
-     * 
-     * @return String
      */
     public String getPrefetchPath() {
         return prefetchPath;
@@ -96,14 +111,18 @@ public class PrefetchSelectQuery extends
 
     /**
      * @since 1.1
+     * @deprecated since 3.1
      */
+    @Deprecated
     public SelectQuery getParentQuery() {
         return parentQuery;
     }
 
     /**
      * @since 1.1
+     * @deprecated since 3.1
      */
+    @Deprecated
     public void setParentQuery(SelectQuery parentQuery) {
         this.parentQuery = parentQuery;
     }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/PrefetchTreeNode.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/PrefetchTreeNode.java?rev=1308073&r1=1308072&r2=1308073&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/PrefetchTreeNode.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/PrefetchTreeNode.java Sun Apr  1 11:06:46 2012
@@ -41,6 +41,7 @@ public class PrefetchTreeNode implements
     public static final int UNDEFINED_SEMANTICS = 0;
     public static final int JOINT_PREFETCH_SEMANTICS = 1;
     public static final int DISJOINT_PREFETCH_SEMANTICS = 2;
+    public static final int DISJOINT_BY_ID_PREFETCH_SEMANTICS = 3;
 
     protected String name;
     protected boolean phantom;
@@ -130,7 +131,7 @@ public class PrefetchTreeNode implements
      */
     public Collection<PrefetchTreeNode> jointNodes() {
         Collection<PrefetchTreeNode> c = new ArrayList<PrefetchTreeNode>();
-        traverse(new CollectionBuilderOperation(c, false, true, false, false));
+        traverse(new CollectionBuilderOperation(c, false, false, true, false, false));
         return c;
     }
 
@@ -139,7 +140,17 @@ public class PrefetchTreeNode implements
      */
     public Collection<PrefetchTreeNode> disjointNodes() {
         Collection<PrefetchTreeNode> c = new ArrayList<PrefetchTreeNode>();
-        traverse(new CollectionBuilderOperation(c, true, false, false, false));
+        traverse(new CollectionBuilderOperation(c, true, false, false, false, false));
+        return c;
+    }
+
+    /**
+     * Returns a collection of PrefetchTreeNodes with disjoint semantics
+     * @since 3.1
+     */
+    public Collection<PrefetchTreeNode> disjointByIdNodes() {
+        Collection<PrefetchTreeNode> c = new ArrayList<PrefetchTreeNode>();
+        traverse(new CollectionBuilderOperation(c, false, true, false, false, false));
         return c;
     }
 
@@ -148,7 +159,7 @@ public class PrefetchTreeNode implements
      */
     public Collection<PrefetchTreeNode> nonPhantomNodes() {
         Collection<PrefetchTreeNode> c = new ArrayList<PrefetchTreeNode>();
-        traverse(new CollectionBuilderOperation(c, true, true, true, false));
+        traverse(new CollectionBuilderOperation(c, true, true, true, true, false));
         return c;
     }
 
@@ -166,6 +177,9 @@ public class PrefetchTreeNode implements
         else if (isDisjointPrefetch()) {
             result = processor.startDisjointPrefetch(this);
         }
+        else if (isDisjointByIdPrefetch()) {
+            result = processor.startDisjointByIdPrefetch(this);
+        }
         else if (isJointPrefetch()) {
             result = processor.startJointPrefetch(this);
         }
@@ -345,7 +359,11 @@ public class PrefetchTreeNode implements
     public boolean isDisjointPrefetch() {
         return semantics == DISJOINT_PREFETCH_SEMANTICS;
     }
-    
+
+    public boolean isDisjointByIdPrefetch() {
+        return semantics == DISJOINT_BY_ID_PREFETCH_SEMANTICS;
+    }
+
     public String getEjbqlPathEntityId() {
         return ejbqlPathEntityId;
     }
@@ -401,6 +419,13 @@ public class PrefetchTreeNode implements
             return true;
         }
 
+        public boolean startDisjointByIdPrefetch(PrefetchTreeNode node) {
+            encoder.print("<prefetch type=\"disjointById\">");
+            encoder.print(node.getPath());
+            encoder.println("</prefetch>");
+            return true;
+        }
+
         public boolean startJointPrefetch(PrefetchTreeNode node) {
             encoder.print("<prefetch type=\"joint\">");
             encoder.print(node.getPath());
@@ -427,14 +452,16 @@ public class PrefetchTreeNode implements
         Collection<PrefetchTreeNode> nodes;
         boolean includePhantom;
         boolean includeDisjoint;
+        boolean includeDisjointById;
         boolean includeJoint;
         boolean includeUnknown;
 
         CollectionBuilderOperation(Collection<PrefetchTreeNode> nodes, boolean includeDisjoint,
-                boolean includeJoint, boolean includeUnknown, boolean includePhantom) {
+                boolean includeDisjointById, boolean includeJoint, boolean includeUnknown, boolean includePhantom) {
             this.nodes = nodes;
 
             this.includeDisjoint = includeDisjoint;
+            this.includeDisjointById = includeDisjointById;
             this.includeJoint = includeJoint;
             this.includeUnknown = includeUnknown;
             this.includePhantom = includePhantom;
@@ -455,6 +482,13 @@ public class PrefetchTreeNode implements
             return true;
         }
 
+        public boolean startDisjointByIdPrefetch(PrefetchTreeNode node) {
+            if (includeDisjointById) {
+                nodes.add(node);
+            }
+            return true;
+        }
+
         public boolean startJointPrefetch(PrefetchTreeNode node) {
             if (includeJoint) {
                 nodes.add(node);
@@ -489,6 +523,10 @@ public class PrefetchTreeNode implements
             return node == PrefetchTreeNode.this;
         }
 
+        public boolean startDisjointByIdPrefetch(PrefetchTreeNode node) {
+            return startDisjointPrefetch(node);
+        }
+
         public boolean startJointPrefetch(PrefetchTreeNode node) {
             if (node != PrefetchTreeNode.this) {
                 nodes.add(node);

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/SelectQueryPrefetchRouterAction.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/SelectQueryPrefetchRouterAction.java?rev=1308073&r1=1308072&r2=1308073&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/SelectQueryPrefetchRouterAction.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/SelectQueryPrefetchRouterAction.java Sun Apr  1 11:06:46 2012
@@ -97,9 +97,9 @@ class SelectQueryPrefetchRouterAction im
 
         // create and configure PrefetchSelectQuery
         PrefetchSelectQuery prefetchQuery = new PrefetchSelectQuery(
-                query,
                 prefetchPath,
                 relationship);
+        prefetchQuery.setStatementFetchSize(query.getStatementFetchSize());
 
         prefetchQuery.setQualifier(classDescriptor.getEntity().translateToRelatedEntity(
                 queryQualifier,
@@ -120,6 +120,11 @@ class SelectQueryPrefetchRouterAction im
         return true;
     }
 
+    public boolean startDisjointByIdPrefetch(PrefetchTreeNode prefetchTreeNode) {
+        // simply pass through
+        return true;
+    }
+
     public boolean startJointPrefetch(PrefetchTreeNode node) {
         // simply pass through
         return true;

Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextDisjointByIdPrefetchTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextDisjointByIdPrefetchTest.java?rev=1308073&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextDisjointByIdPrefetchTest.java (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextDisjointByIdPrefetchTest.java Sun Apr  1 11:06:46 2012
@@ -0,0 +1,233 @@
+package org.apache.cayenne.access;
+
+import org.apache.cayenne.ValueHolder;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.query.PrefetchTreeNode;
+import org.apache.cayenne.query.SelectQuery;
+import org.apache.cayenne.test.jdbc.DBHelper;
+import org.apache.cayenne.test.jdbc.TableHelper;
+import org.apache.cayenne.testdo.testmap.Bag;
+import org.apache.cayenne.testdo.testmap.Ball;
+import org.apache.cayenne.testdo.testmap.Box;
+import org.apache.cayenne.testdo.testmap.BoxInfo;
+import org.apache.cayenne.unit.di.DataChannelInterceptor;
+import org.apache.cayenne.unit.di.UnitTestClosure;
+import org.apache.cayenne.unit.di.server.ServerCase;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+
+import java.util.List;
+
+import static org.apache.cayenne.exp.ExpressionFactory.matchExp;
+
+@UseServerRuntime(ServerCase.TESTMAP_PROJECT)
+public class DataContextDisjointByIdPrefetchTest extends ServerCase {
+    @Inject
+    protected DataContext context;
+
+    @Inject
+    protected DBHelper dbHelper;
+
+    @Inject
+    protected DataChannelInterceptor queryInterceptor;
+
+    protected TableHelper tBag;
+    protected TableHelper tBox;
+    protected TableHelper tBoxInfo;
+    protected TableHelper tBall;
+    protected TableHelper tThing;
+    protected TableHelper tBoxThing;
+
+    @Override
+    protected void setUpAfterInjection() throws Exception {
+        dbHelper.deleteAll("BALL");
+        dbHelper.deleteAll("BOX_THING");
+        dbHelper.deleteAll("THING");
+        dbHelper.deleteAll("BOX_INFO");
+        dbHelper.deleteAll("BOX");
+        dbHelper.deleteAll("BAG");
+
+        tBag = new TableHelper(dbHelper, "BAG");
+        tBag.setColumns("ID");
+
+        tBox = new TableHelper(dbHelper, "BOX");
+        tBox.setColumns("ID", "BAG_ID");
+
+        tBoxInfo = new TableHelper(dbHelper, "BOX_INFO");
+        tBoxInfo.setColumns("ID", "BOX_ID", "COLOR");
+
+        tBall = new TableHelper(dbHelper, "BALL");
+        tBall.setColumns("ID", "BOX_ID", "THING_WEIGHT", "THING_VOLUME");
+
+        tThing = new TableHelper(dbHelper, "THING");
+        tThing.setColumns("ID", "WEIGHT", "VOLUME");
+
+        tBoxThing = new TableHelper(dbHelper, "BOX_THING");
+        tBoxThing.setColumns("BOX_ID", "THING_WEIGHT", "THING_VOLUME");
+    }
+
+    private void createBagWithTwoBoxesDataSet() throws Exception {
+        tBag.insert(1);
+        tBox.insert(1, 1);
+        tBox.insert(2, 1);
+    }
+
+    private void createThreeBagsWithPlentyOfBoxesDataSet() throws Exception {
+        tBag.insert(1);
+        tBag.insert(2);
+        tBag.insert(3);
+
+        tBox.insert(1, 1);
+        tBox.insert(2, 1);
+        tBox.insert(3, 1);
+        tBox.insert(4, 1);
+        tBox.insert(5, 1);
+
+        tBox.insert(6, 2);
+        tBox.insert(7, 2);
+
+        tBox.insert(8, 3);
+        tBox.insert(9, 3);
+        tBox.insert(10, 3);
+    }
+
+    private void createBagWithTwoBoxesAndPlentyOfBallsDataSet() throws Exception {
+        tBag.insert(1);
+        tBox.insert(1, 1);
+        tBoxInfo.insert(1, 1, "red");
+        tBox.insert(2, 1);
+        tBoxInfo.insert(2, 2, "green");
+
+        tThing.insert(1, 10, 10);
+        tBoxThing.insert(1, 10, 10);
+        tBall.insert(1, 1, 10, 10);
+
+        tThing.insert(2, 20, 20);
+        tBoxThing.insert(1, 20, 20);
+        tBall.insert(2, 1, 20, 20);
+
+        tThing.insert(3, 30, 30);
+        tBoxThing.insert(2, 30, 30);
+        tBall.insert(3, 2, 30, 30);
+
+        tThing.insert(4, 40, 40);
+        tBoxThing.insert(2, 40, 40);
+        tBall.insert(4, 2, 40, 40);
+
+        tThing.insert(5, 10, 20);
+        tBoxThing.insert(2, 10, 20);
+        tBall.insert(5, 2, 10, 20);
+
+        tThing.insert(6, 30, 40);
+        tBoxThing.insert(2, 30, 40);
+        tBall.insert(6, 2, 30, 40);
+    }
+
+    public void testBasic() throws Exception {
+        createBagWithTwoBoxesDataSet();
+
+        SelectQuery query = new SelectQuery(Bag.class);
+        query.addPrefetch(Bag.BOXES_PROPERTY).setSemantics(
+                PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS);
+
+        final List<Bag> result = context.performQuery(query);
+        queryInterceptor.runWithQueriesBlocked(new UnitTestClosure() {
+
+            public void execute() {
+                assertFalse(result.isEmpty());
+                Bag b1 = result.get(0);
+                List<?> toMany = (List<?>) b1.readPropertyDirectly(Bag.BOXES_PROPERTY);
+                assertNotNull(toMany);
+                assertFalse(((ValueHolder) toMany).isFault());
+            }
+        });
+    }
+
+    public void testFetchLimit() throws Exception {
+        createThreeBagsWithPlentyOfBoxesDataSet();
+
+        final SelectQuery query = new SelectQuery(Bag.class);
+        query.addPrefetch(Bag.BOXES_PROPERTY).setSemantics(
+                PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS);
+        query.setFetchLimit(2);
+
+        // There will be only 2 bags in a result. The first bag has 5 boxes and
+        // the second has 2. So we are expecting exactly 9 snapshots in the data
+        // row store after performing the query.
+        context.performQuery(query);
+
+        assertEquals(9, context.getObjectStore().getDataRowCache().size());
+    }
+
+    public void testToOneRelationship() throws Exception {
+        createBagWithTwoBoxesAndPlentyOfBallsDataSet();
+
+        SelectQuery query = new SelectQuery(Box.class);
+        query.addPrefetch(Box.BOX_INFO_PROPERTY)
+                .setSemantics(PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS);
+        final List<Box> result = context.performQuery(query);
+        queryInterceptor.runWithQueriesBlocked(new UnitTestClosure() {
+            public void execute() {
+                assertFalse(result.isEmpty());
+                Box b1 = result.get(0);
+                BoxInfo info = (BoxInfo) b1.readPropertyDirectly(Box.BOX_INFO_PROPERTY);
+                assertNotNull(info);
+                assertEquals("red", info.getColor());
+            }
+        });
+    }
+
+    public void testFlattenedRelationship() throws Exception {
+        createBagWithTwoBoxesAndPlentyOfBallsDataSet();
+
+        SelectQuery query = new SelectQuery(Bag.class);
+        query.addPrefetch(Bag.BALLS_PROPERTY)
+                .setSemantics(PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS);
+        final List<Bag> result = context.performQuery(query);
+
+        queryInterceptor.runWithQueriesBlocked(new UnitTestClosure() {
+            public void execute() {
+                assertFalse(result.isEmpty());
+                Bag b1 = result.get(0);
+                List<?> toMany = (List<?>) b1.readPropertyDirectly(Bag.BALLS_PROPERTY);
+                assertNotNull(toMany);
+                assertFalse(((ValueHolder) toMany).isFault());
+                assertEquals(6, toMany.size());
+            }
+        });
+    }
+
+    public void testMultiColumnRelationship() throws Exception {
+        createBagWithTwoBoxesAndPlentyOfBallsDataSet();
+
+        SelectQuery query = new SelectQuery(Ball.class);
+        query.orQualifier(
+                matchExp(Ball.THING_VOLUME_PROPERTY, 40).andExp(matchExp(Ball.THING_WEIGHT_PROPERTY, 30)));
+        query.orQualifier(
+                matchExp(Ball.THING_VOLUME_PROPERTY, 20).andExp(matchExp(Ball.THING_WEIGHT_PROPERTY, 10)));
+        query.addPrefetch(Ball.THING_PROPERTY)
+                .setSemantics(PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS);
+        context.performQuery(query);
+
+        assertEquals(4, context.getObjectStore().getDataRowCache().size());
+    }
+
+    public void testFlattenedMultiColumnRelationship() throws Exception {
+        createBagWithTwoBoxesAndPlentyOfBallsDataSet();
+
+        SelectQuery query = new SelectQuery(Box.class);
+        query.addPrefetch(Box.THINGS_PROPERTY)
+                .setSemantics(PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS);
+        final List<Box> result = context.performQuery(query);
+
+        queryInterceptor.runWithQueriesBlocked(new UnitTestClosure() {
+            public void execute() {
+                assertFalse(result.isEmpty());
+                Box b1 = result.get(0);
+                List<?> toMany = (List<?>) b1.readPropertyDirectly(Box.THINGS_PROPERTY);
+                assertNotNull(toMany);
+                assertFalse(((ValueHolder) toMany).isFault());
+                assertEquals(2, toMany.size());
+            }
+        });
+    }
+}

Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/Bag.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/Bag.java?rev=1308073&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/Bag.java (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/Bag.java Sun Apr  1 11:06:46 2012
@@ -0,0 +1,7 @@
+package org.apache.cayenne.testdo.testmap;
+
+import org.apache.cayenne.testdo.testmap.auto._Bag;
+
+public class Bag extends _Bag {
+
+}

Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/Ball.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/Ball.java?rev=1308073&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/Ball.java (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/Ball.java Sun Apr  1 11:06:46 2012
@@ -0,0 +1,7 @@
+package org.apache.cayenne.testdo.testmap;
+
+import org.apache.cayenne.testdo.testmap.auto._Ball;
+
+public class Ball extends _Ball {
+
+}

Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/Box.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/Box.java?rev=1308073&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/Box.java (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/Box.java Sun Apr  1 11:06:46 2012
@@ -0,0 +1,7 @@
+package org.apache.cayenne.testdo.testmap;
+
+import org.apache.cayenne.testdo.testmap.auto._Box;
+
+public class Box extends _Box {
+
+}

Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/BoxInfo.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/BoxInfo.java?rev=1308073&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/BoxInfo.java (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/BoxInfo.java Sun Apr  1 11:06:46 2012
@@ -0,0 +1,7 @@
+package org.apache.cayenne.testdo.testmap;
+
+import org.apache.cayenne.testdo.testmap.auto._BoxInfo;
+
+public class BoxInfo extends _BoxInfo {
+
+}

Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/Thing.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/Thing.java?rev=1308073&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/Thing.java (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/Thing.java Sun Apr  1 11:06:46 2012
@@ -0,0 +1,7 @@
+package org.apache.cayenne.testdo.testmap;
+
+import org.apache.cayenne.testdo.testmap.auto._Thing;
+
+public class Thing extends _Thing {
+
+}

Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Bag.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Bag.java?rev=1308073&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Bag.java (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Bag.java Sun Apr  1 11:06:46 2012
@@ -0,0 +1,40 @@
+package org.apache.cayenne.testdo.testmap.auto;
+
+import java.util.List;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.testdo.testmap.Ball;
+import org.apache.cayenne.testdo.testmap.Box;
+
+/**
+ * Class _Bag was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _Bag extends CayenneDataObject {
+
+    public static final String BALLS_PROPERTY = "balls";
+    public static final String BOXES_PROPERTY = "boxes";
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    @SuppressWarnings("unchecked")
+    public List<Ball> getBalls() {
+        return (List<Ball>)readProperty("balls");
+    }
+
+
+    public void addToBoxes(Box obj) {
+        addToManyTarget("boxes", obj, true);
+    }
+    public void removeFromBoxes(Box obj) {
+        removeToManyTarget("boxes", obj, true);
+    }
+    @SuppressWarnings("unchecked")
+    public List<Box> getBoxes() {
+        return (List<Box>)readProperty("boxes");
+    }
+
+
+}

Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Ball.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Ball.java?rev=1308073&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Ball.java (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Ball.java Sun Apr  1 11:06:46 2012
@@ -0,0 +1,54 @@
+package org.apache.cayenne.testdo.testmap.auto;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.testdo.testmap.Box;
+import org.apache.cayenne.testdo.testmap.Thing;
+
+/**
+ * Class _Ball was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _Ball extends CayenneDataObject {
+
+    public static final String THING_VOLUME_PROPERTY = "thingVolume";
+    public static final String THING_WEIGHT_PROPERTY = "thingWeight";
+    public static final String BOX_PROPERTY = "box";
+    public static final String THING_PROPERTY = "thing";
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public void setThingVolume(Integer thingVolume) {
+        writeProperty("thingVolume", thingVolume);
+    }
+    public Integer getThingVolume() {
+        return (Integer)readProperty("thingVolume");
+    }
+
+    public void setThingWeight(Integer thingWeight) {
+        writeProperty("thingWeight", thingWeight);
+    }
+    public Integer getThingWeight() {
+        return (Integer)readProperty("thingWeight");
+    }
+
+    public void setBox(Box box) {
+        setToOneTarget("box", box, true);
+    }
+
+    public Box getBox() {
+        return (Box)readProperty("box");
+    }
+
+
+    public void setThing(Thing thing) {
+        setToOneTarget("thing", thing, true);
+    }
+
+    public Thing getThing() {
+        return (Thing)readProperty("thing");
+    }
+
+
+}

Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Box.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Box.java?rev=1308073&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Box.java (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Box.java Sun Apr  1 11:06:46 2012
@@ -0,0 +1,62 @@
+package org.apache.cayenne.testdo.testmap.auto;
+
+import java.util.List;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.testdo.testmap.Bag;
+import org.apache.cayenne.testdo.testmap.Ball;
+import org.apache.cayenne.testdo.testmap.BoxInfo;
+import org.apache.cayenne.testdo.testmap.Thing;
+
+/**
+ * Class _Box was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _Box extends CayenneDataObject {
+
+    public static final String BAG_PROPERTY = "bag";
+    public static final String BALLS_PROPERTY = "balls";
+    public static final String BOX_INFO_PROPERTY = "boxInfo";
+    public static final String THINGS_PROPERTY = "things";
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public void setBag(Bag bag) {
+        setToOneTarget("bag", bag, true);
+    }
+
+    public Bag getBag() {
+        return (Bag)readProperty("bag");
+    }
+
+
+    public void addToBalls(Ball obj) {
+        addToManyTarget("balls", obj, true);
+    }
+    public void removeFromBalls(Ball obj) {
+        removeToManyTarget("balls", obj, true);
+    }
+    @SuppressWarnings("unchecked")
+    public List<Ball> getBalls() {
+        return (List<Ball>)readProperty("balls");
+    }
+
+
+    public void setBoxInfo(BoxInfo boxInfo) {
+        setToOneTarget("boxInfo", boxInfo, true);
+    }
+
+    public BoxInfo getBoxInfo() {
+        return (BoxInfo)readProperty("boxInfo");
+    }
+
+
+    @SuppressWarnings("unchecked")
+    public List<Thing> getThings() {
+        return (List<Thing>)readProperty("things");
+    }
+
+
+}

Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_BoxInfo.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_BoxInfo.java?rev=1308073&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_BoxInfo.java (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_BoxInfo.java Sun Apr  1 11:06:46 2012
@@ -0,0 +1,35 @@
+package org.apache.cayenne.testdo.testmap.auto;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.testdo.testmap.Box;
+
+/**
+ * Class _BoxInfo was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _BoxInfo extends CayenneDataObject {
+
+    public static final String COLOR_PROPERTY = "color";
+    public static final String BOX_PROPERTY = "box";
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public void setColor(String color) {
+        writeProperty("color", color);
+    }
+    public String getColor() {
+        return (String)readProperty("color");
+    }
+
+    public void setBox(Box box) {
+        setToOneTarget("box", box, true);
+    }
+
+    public Box getBox() {
+        return (Box)readProperty("box");
+    }
+
+
+}

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Testmap.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Testmap.java?rev=1308073&r1=1308072&r2=1308073&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Testmap.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Testmap.java Sun Apr  1 11:06:46 2012
@@ -42,6 +42,8 @@ public class _Testmap {
 
     public static final String SELECT_DATE_TEST_QUERYNAME = "SelectDateTest";
 
+    public static final String SELECT_RETURN_TYPES_LOBS_MAP1_QUERYNAME = "SelectReturnTypesLobsMap1";
+
     public static final String SELECT_RETURN_TYPES_MAP1_QUERYNAME = "SelectReturnTypesMap1";
 
     public static final String SELECT_RETURN_TYPES_MAP2_QUERYNAME = "SelectReturnTypesMap2";

Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Thing.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Thing.java?rev=1308073&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Thing.java (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Thing.java Sun Apr  1 11:06:46 2012
@@ -0,0 +1,53 @@
+package org.apache.cayenne.testdo.testmap.auto;
+
+import java.util.List;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.testdo.testmap.Ball;
+import org.apache.cayenne.testdo.testmap.Box;
+
+/**
+ * Class _Thing was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _Thing extends CayenneDataObject {
+
+    public static final String VOLUME_PROPERTY = "volume";
+    public static final String WEIGHT_PROPERTY = "weight";
+    public static final String BALL_PROPERTY = "ball";
+    public static final String BOX_PROPERTY = "box";
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public void setVolume(Integer volume) {
+        writeProperty("volume", volume);
+    }
+    public Integer getVolume() {
+        return (Integer)readProperty("volume");
+    }
+
+    public void setWeight(Integer weight) {
+        writeProperty("weight", weight);
+    }
+    public Integer getWeight() {
+        return (Integer)readProperty("weight");
+    }
+
+    public void setBall(Ball ball) {
+        setToOneTarget("ball", ball, true);
+    }
+
+    public Ball getBall() {
+        return (Ball)readProperty("ball");
+    }
+
+
+    @SuppressWarnings("unchecked")
+    public List<Box> getBox() {
+        return (List<Box>)readProperty("box");
+    }
+
+
+}

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/resources/testmap.map.xml
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/resources/testmap.map.xml?rev=1308073&r1=1308072&r2=1308073&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/resources/testmap.map.xml (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/resources/testmap.map.xml Sun Apr  1 11:06:46 2012
@@ -51,6 +51,15 @@
 		<db-attribute name="ARTIST_ID" type="BIGINT" isPrimaryKey="true" isMandatory="true"/>
 		<db-attribute name="GROUP_ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
 	</db-entity>
+	<db-entity name="BAG">
+		<db-attribute name="ID" type="BIGINT" isPrimaryKey="true" isGenerated="true" isMandatory="true"/>
+	</db-entity>
+	<db-entity name="BALL">
+		<db-attribute name="BOX_ID" type="BIGINT" isMandatory="true"/>
+		<db-attribute name="ID" type="BIGINT" isPrimaryKey="true" isGenerated="true" isMandatory="true"/>
+		<db-attribute name="THING_VOLUME" type="INTEGER" isMandatory="true"/>
+		<db-attribute name="THING_WEIGHT" type="INTEGER" isMandatory="true"/>
+	</db-entity>
 	<db-entity name="BIGDECIMAL_ENTITY">
 		<db-attribute name="BIGDECIMAL_FIELD" type="NUMERIC" length="12" scale="2"/>
 		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
@@ -80,6 +89,20 @@
 		<db-attribute name="BOOLEAN_COLUMN" type="BOOLEAN" isMandatory="true"/>
 		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
 	</db-entity>
+	<db-entity name="BOX">
+		<db-attribute name="BAG_ID" type="BIGINT" isMandatory="true"/>
+		<db-attribute name="ID" type="BIGINT" isPrimaryKey="true" isMandatory="true"/>
+	</db-entity>
+	<db-entity name="BOX_INFO">
+		<db-attribute name="BOX_ID" type="BIGINT" isMandatory="true"/>
+		<db-attribute name="COLOR" type="VARCHAR" isMandatory="true" length="200"/>
+		<db-attribute name="ID" type="BIGINT" isPrimaryKey="true" isGenerated="true" isMandatory="true"/>
+	</db-entity>
+	<db-entity name="BOX_THING">
+		<db-attribute name="BOX_ID" type="BIGINT" isMandatory="true"/>
+		<db-attribute name="THING_VOLUME" type="INTEGER" isMandatory="true"/>
+		<db-attribute name="THING_WEIGHT" type="INTEGER" isMandatory="true"/>
+	</db-entity>
 	<db-entity name="CALENDAR_TEST">
 		<db-attribute name="CALENDAR_FIELD" type="TIMESTAMP"/>
 		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
@@ -245,6 +268,11 @@
 		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
 		<db-attribute name="SMALLINT_COL" type="SMALLINT"/>
 	</db-entity>
+	<db-entity name="THING">
+		<db-attribute name="ID" type="BIGINT" isPrimaryKey="true" isGenerated="true" isMandatory="true"/>
+		<db-attribute name="VOLUME" type="INTEGER" isMandatory="true"/>
+		<db-attribute name="WEIGHT" type="INTEGER" isMandatory="true"/>
+	</db-entity>
 	<db-entity name="TINYINT_TEST">
 		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
 		<db-attribute name="TINYINT_COL" type="TINYINT"/>
@@ -319,6 +347,12 @@
 	</obj-entity>
 	<obj-entity name="ArtistExhibit" className="org.apache.cayenne.testdo.testmap.ArtistExhibit" dbEntityName="ARTIST_EXHIBIT">
 	</obj-entity>
+	<obj-entity name="Bag" className="org.apache.cayenne.testdo.testmap.Bag" clientClassName="test.client.Bag" dbEntityName="BAG" superClassName="org.apache.cayenne.CayenneDataObject" clientSuperClassName="org.apache.cayenne.PersistentObject">
+	</obj-entity>
+	<obj-entity name="Ball" className="org.apache.cayenne.testdo.testmap.Ball" clientClassName="test.client.Ball" dbEntityName="BALL" superClassName="org.apache.cayenne.CayenneDataObject" clientSuperClassName="org.apache.cayenne.PersistentObject">
+		<obj-attribute name="thingVolume" type="java.lang.Integer" db-attribute-path="THING_VOLUME"/>
+		<obj-attribute name="thingWeight" type="java.lang.Integer" db-attribute-path="THING_WEIGHT"/>
+	</obj-entity>
 	<obj-entity name="BigDecimalEntity" className="org.apache.cayenne.testdo.testmap.BigDecimalEntity" dbEntityName="BIGDECIMAL_ENTITY">
 		<obj-attribute name="bigDecimalField" type="java.math.BigDecimal" db-attribute-path="BIGDECIMAL_FIELD"/>
 	</obj-entity>
@@ -343,6 +377,11 @@
 	<obj-entity name="BooleanTestEntity" className="org.apache.cayenne.testdo.testmap.BooleanTestEntity" dbEntityName="BOOLEAN_TEST">
 		<obj-attribute name="booleanColumn" type="java.lang.Boolean" db-attribute-path="BOOLEAN_COLUMN"/>
 	</obj-entity>
+	<obj-entity name="Box" className="org.apache.cayenne.testdo.testmap.Box" clientClassName="test.client.Box" dbEntityName="BOX" superClassName="org.apache.cayenne.CayenneDataObject" clientSuperClassName="org.apache.cayenne.PersistentObject">
+	</obj-entity>
+	<obj-entity name="BoxInfo" className="org.apache.cayenne.testdo.testmap.BoxInfo" clientClassName="test.client.BoxInfo" dbEntityName="BOX_INFO" superClassName="org.apache.cayenne.CayenneDataObject" clientSuperClassName="org.apache.cayenne.PersistentObject">
+		<obj-attribute name="color" type="java.lang.String" db-attribute-path="COLOR"/>
+	</obj-entity>
 	<obj-entity name="CalendarEntity" className="org.apache.cayenne.testdo.testmap.CalendarEntity" dbEntityName="CALENDAR_TEST">
 		<obj-attribute name="calendarField" type="java.util.Calendar" db-attribute-path="CALENDAR_FIELD"/>
 	</obj-entity>
@@ -525,6 +564,10 @@
 	<obj-entity name="SubPainting" className="org.apache.cayenne.testdo.testmap.SubPainting" dbEntityName="PAINTING">
 		<obj-attribute name="paintingTitle" type="java.lang.String" db-attribute-path="PAINTING_TITLE"/>
 	</obj-entity>
+	<obj-entity name="Thing" className="org.apache.cayenne.testdo.testmap.Thing" clientClassName="test.client.Thing" dbEntityName="THING" superClassName="org.apache.cayenne.CayenneDataObject" clientSuperClassName="org.apache.cayenne.PersistentObject">
+		<obj-attribute name="volume" type="java.lang.Integer" db-attribute-path="VOLUME"/>
+		<obj-attribute name="weight" type="java.lang.Integer" db-attribute-path="WEIGHT"/>
+	</obj-entity>
 	<obj-entity name="TinyintTestEntity" className="org.apache.cayenne.testdo.testmap.TinyintTestEntity" dbEntityName="TINYINT_TEST">
 		<obj-attribute name="tinyintCol" type="java.lang.Byte" db-attribute-path="TINYINT_COL"/>
 	</obj-entity>
@@ -561,12 +604,44 @@
 	<db-relationship name="toGroup" source="ARTIST_GROUP" target="ARTGROUP" toMany="false">
 		<db-attribute-pair source="GROUP_ID" target="GROUP_ID"/>
 	</db-relationship>
+	<db-relationship name="BOXES" source="BAG" target="BOX" toMany="true">
+		<db-attribute-pair source="ID" target="BAG_ID"/>
+	</db-relationship>
+	<db-relationship name="BOX" source="BALL" target="BOX" toMany="false">
+		<db-attribute-pair source="BOX_ID" target="ID"/>
+	</db-relationship>
+	<db-relationship name="THING" source="BALL" target="THING" toMany="false">
+		<db-attribute-pair source="THING_VOLUME" target="VOLUME"/>
+		<db-attribute-pair source="THING_WEIGHT" target="WEIGHT"/>
+	</db-relationship>
 	<db-relationship name="binaryPKDetails" source="BINARY_PK_TEST1" target="BINARY_PK_TEST2" toMany="true">
 		<db-attribute-pair source="BIN_ID" target="FK_ID"/>
 	</db-relationship>
 	<db-relationship name="toBinaryPKMaster" source="BINARY_PK_TEST2" target="BINARY_PK_TEST1" toMany="false">
 		<db-attribute-pair source="FK_ID" target="BIN_ID"/>
 	</db-relationship>
+	<db-relationship name="BAG" source="BOX" target="BAG" toMany="false">
+		<db-attribute-pair source="BAG_ID" target="ID"/>
+	</db-relationship>
+	<db-relationship name="BALLS" source="BOX" target="BALL" toMany="true">
+		<db-attribute-pair source="ID" target="BOX_ID"/>
+	</db-relationship>
+	<db-relationship name="BOX_INFO" source="BOX" target="BOX_INFO" toMany="false">
+		<db-attribute-pair source="ID" target="BOX_ID"/>
+	</db-relationship>
+	<db-relationship name="BOX_THING" source="BOX" target="BOX_THING" toMany="true">
+		<db-attribute-pair source="ID" target="BOX_ID"/>
+	</db-relationship>
+	<db-relationship name="BOX" source="BOX_INFO" target="BOX" toMany="false">
+		<db-attribute-pair source="BOX_ID" target="ID"/>
+	</db-relationship>
+	<db-relationship name="BOX" source="BOX_THING" target="BOX" toMany="false">
+		<db-attribute-pair source="BOX_ID" target="ID"/>
+	</db-relationship>
+	<db-relationship name="THING" source="BOX_THING" target="THING" toMany="false">
+		<db-attribute-pair source="THING_VOLUME" target="VOLUME"/>
+		<db-attribute-pair source="THING_WEIGHT" target="WEIGHT"/>
+	</db-relationship>
 	<db-relationship name="toCharPK" source="CHAR_FK_TEST" target="CHAR_PK_TEST" toMany="false">
 		<db-attribute-pair source="FK_COL" target="PK_COL"/>
 	</db-relationship>
@@ -650,6 +725,14 @@
 	<db-relationship name="painting" source="PAINTING_INFO" target="PAINTING" toMany="false">
 		<db-attribute-pair source="PAINTING_ID" target="PAINTING_ID"/>
 	</db-relationship>
+	<db-relationship name="BALL" source="THING" target="BALL" toMany="false">
+		<db-attribute-pair source="VOLUME" target="THING_VOLUME"/>
+		<db-attribute-pair source="WEIGHT" target="THING_WEIGHT"/>
+	</db-relationship>
+	<db-relationship name="BOX_THING" source="THING" target="BOX_THING" toMany="true">
+		<db-attribute-pair source="VOLUME" target="THING_VOLUME"/>
+		<db-attribute-pair source="WEIGHT" target="THING_WEIGHT"/>
+	</db-relationship>
 	<obj-relationship name="artistArray" source="ArtGroup" target="Artist" deleteRule="Nullify" db-relationship-path="artistGroupArray.toArtist"/>
 	<obj-relationship name="childGroupsArray" source="ArtGroup" target="ArtGroup" deleteRule="Nullify" db-relationship-path="toChildGroups"/>
 	<obj-relationship name="toParentGroup" source="ArtGroup" target="ArtGroup" deleteRule="Nullify" db-relationship-path="toParentGroup"/>
@@ -658,8 +741,17 @@
 	<obj-relationship name="paintingArray" source="Artist" target="Painting" deleteRule="Cascade" db-relationship-path="paintingArray"/>
 	<obj-relationship name="toArtist" source="ArtistExhibit" target="Artist" deleteRule="Nullify" db-relationship-path="toArtist"/>
 	<obj-relationship name="toExhibit" source="ArtistExhibit" target="Exhibit" deleteRule="Nullify" db-relationship-path="toExhibit"/>
+	<obj-relationship name="balls" source="Bag" target="Ball" deleteRule="Deny" db-relationship-path="BOXES.BALLS"/>
+	<obj-relationship name="boxes" source="Bag" target="Box" deleteRule="Deny" db-relationship-path="BOXES"/>
+	<obj-relationship name="box" source="Ball" target="Box" deleteRule="Nullify" db-relationship-path="BOX"/>
+	<obj-relationship name="thing" source="Ball" target="Thing" deleteRule="Nullify" db-relationship-path="THING"/>
 	<obj-relationship name="binaryPKDetails" source="BinaryPKTest1" target="BinaryPKTest2" db-relationship-path="binaryPKDetails"/>
 	<obj-relationship name="toBinaryPKMaster" source="BinaryPKTest2" target="BinaryPKTest1" db-relationship-path="toBinaryPKMaster"/>
+	<obj-relationship name="bag" source="Box" target="Bag" deleteRule="Nullify" db-relationship-path="BAG"/>
+	<obj-relationship name="balls" source="Box" target="Ball" deleteRule="Deny" db-relationship-path="BALLS"/>
+	<obj-relationship name="boxInfo" source="Box" target="BoxInfo" deleteRule="Deny" db-relationship-path="BOX_INFO"/>
+	<obj-relationship name="things" source="Box" target="Thing" deleteRule="Deny" db-relationship-path="BOX_THING.THING"/>
+	<obj-relationship name="box" source="BoxInfo" target="Box" deleteRule="Nullify" db-relationship-path="BOX"/>
 	<obj-relationship name="toCharPK" source="CharFkTestEntity" target="CharPkTestEntity" db-relationship-path="toCharPK"/>
 	<obj-relationship name="charFKs" source="CharPkTestEntity" target="CharFkTestEntity" db-relationship-path="charFKs"/>
 	<obj-relationship name="clobValue" source="ClobTestEntity" target="ClobTestRelation" db-relationship-path="clob"/>
@@ -694,6 +786,8 @@
 	<obj-relationship name="painting" source="PaintingInfo" target="Painting" deleteRule="Nullify" db-relationship-path="painting"/>
 	<obj-relationship name="paintingArray" source="ROArtist" target="Painting" deleteRule="Deny" db-relationship-path="paintingArray"/>
 	<obj-relationship name="toArtist" source="ROPainting" target="Artist" deleteRule="Nullify" db-relationship-path="toArtist"/>
+	<obj-relationship name="ball" source="Thing" target="Ball" deleteRule="Nullify" db-relationship-path="BALL"/>
+	<obj-relationship name="box" source="Thing" target="Box" deleteRule="Deny" db-relationship-path="BOX_THING.BOX"/>
 	<query name="EjbqlQueryTest" factory="org.apache.cayenne.map.EjbqlBuilder">
 		<property name="cayenne.GenericSelectQuery.fetchingDataRows" value="true"/>
 		<property name="cayenne.GenericSelectQuery.cacheStrategy" value="SHARED_CACHE"/>