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 2009/09/07 02:33:46 UTC

svn commit: r811957 - in /cayenne/main/trunk: docs/doc/src/main/resources/ framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/ framework/cayen...

Author: aadamchik
Date: Mon Sep  7 00:33:46 2009
New Revision: 811957

URL: http://svn.apache.org/viewvc?rev=811957&view=rev
Log:
CAY-1250 Prefetching doesn't work with prefetched subentity

Modified:
    cayenne/main/trunk/docs/doc/src/main/resources/RELEASE-NOTES.txt
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ObjectResolver.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ObjectTreeResolver.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorJointNode.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorNode.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/SelectTranslator.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/EntityInheritanceTree.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/access/DataContextPrefetchMultistepTest.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/InheritanceTest.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/JointPrefetchTest.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/PrefetchProcessorTreeBuilderTest.java
    cayenne/main/trunk/itests/pojo/src/test/java/org/apache/cayenne/itest/pojo/OneToManyObjectTest.java

Modified: cayenne/main/trunk/docs/doc/src/main/resources/RELEASE-NOTES.txt
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/docs/doc/src/main/resources/RELEASE-NOTES.txt?rev=811957&r1=811956&r2=811957&view=diff
==============================================================================
--- cayenne/main/trunk/docs/doc/src/main/resources/RELEASE-NOTES.txt (original)
+++ cayenne/main/trunk/docs/doc/src/main/resources/RELEASE-NOTES.txt Mon Sep  7 00:33:46 2009
@@ -38,6 +38,7 @@
 CAY-1240 Schema generation dialog has "uncheck all tables" checkbox incorrectly checked by default
 CAY-1246 Naming strategy getting stuck in (incorrect) preferences location
 CAY-1248 ClassCastException with OracleLOBBatchAction
+CAY-1250 Prefetching doesn't work with prefetched subentity
 CAY-1252 Bad XML generated when saving DBEntity qualifiers
 CAY-1265 error while search inheritance relationship
 
@@ -78,6 +79,7 @@
 CAY-1215 Move tool-like items into new cayenne-tools module
 CAY-1218 Query caching loose ends
 CAY-1221 Exclude JPA from releases
+CAY-1250 Prefetching doesn't work with prefetched subentity
 
 Bug Fixes Since M5:
 

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ObjectResolver.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ObjectResolver.java?rev=811957&r1=811956&r2=811957&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ObjectResolver.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ObjectResolver.java Mon Sep  7 00:33:46 2009
@@ -21,6 +21,7 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -35,6 +36,7 @@
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.EntityInheritanceTree;
+import org.apache.cayenne.map.EntityResolver;
 import org.apache.cayenne.map.ObjEntity;
 import org.apache.cayenne.reflect.ClassDescriptor;
 
@@ -143,15 +145,33 @@
             return new ArrayList<Persistent>(1);
         }
 
-        ObjEntity sourceObjEntity = (ObjEntity) node
-                .getIncoming()
-                .getRelationship()
-                .getSourceEntity();
-        String relatedIdPrefix = node
-                .getIncoming()
-                .getRelationship()
-                .getReverseDbRelationshipPath()
-                + ".";
+        boolean linkToParent = node.getParent() != null && !node.getParent().isPhantom();
+
+        String relatedIdPrefix = null;
+
+        // since we do not add descriptor columns for the source entity in the result,
+        // will need to try all subentities to find parent object...
+        Collection<ObjEntity> sourceEntities = null;
+
+        if (linkToParent) {
+            ClassDescriptor parentDescriptor = ((PrefetchProcessorNode) node.getParent())
+                    .getResolver()
+                    .getDescriptor();
+            relatedIdPrefix = node
+                    .getIncoming()
+                    .getRelationship()
+                    .getReverseDbRelationshipPath()
+                    + ".";
+
+            if (parentDescriptor.getEntityInheritanceTree() == null) {
+                sourceEntities = Collections.singletonList(parentDescriptor.getEntity());
+            }
+            else {
+                sourceEntities = parentDescriptor
+                        .getEntityInheritanceTree()
+                        .allSubEntities();
+            }
+        }
 
         List<Persistent> results = new ArrayList<Persistent>(rows.size());
 
@@ -188,31 +208,52 @@
                 seen.put(anId, object);
             }
 
+            // keep the dupe objects (and data rows) around, as there maybe an attached
+            // joint prefetch...
             results.add(object);
 
-            // link with parent
+            if (linkToParent) {
 
-            // The algorithm below of building an ID doesn't take inheritance into
-            // account, so there maybe a miss...
-            ObjectId id = createObjectId(row, sourceObjEntity, relatedIdPrefix);
-            if (id == null) {
-                throw new CayenneRuntimeException("Can't build ObjectId from row: "
-                        + row
-                        + ", entity: "
-                        + sourceObjEntity.getName()
-                        + ", prefix: "
-                        + relatedIdPrefix);
-            }
-            Persistent parentObject = (Persistent) context.getObjectStore().getNode(id);
+                Persistent parentObject = null;
 
-            // don't attach to hollow objects
-            if (parentObject != null
-                    && parentObject.getPersistenceState() != PersistenceState.HOLLOW) {
-                node.linkToParent(object, parentObject);
+                for (ObjEntity entity : sourceEntities) {
+                    if (entity.isAbstract()) {
+                        continue;
+                    }
+
+                    ObjectId id = createObjectId(row, entity, relatedIdPrefix);
+                    if (id == null) {
+                        throw new CayenneRuntimeException(
+                                "Can't build ObjectId from row: "
+                                        + row
+                                        + ", entity: "
+                                        + entity.getName()
+                                        + ", prefix: "
+                                        + relatedIdPrefix);
+                    }
+
+                    parentObject = (Persistent) context.getObjectStore().getNode(id);
+
+                    if (parentObject != null) {
+                        break;
+                    }
+                }
+
+                // don't attach to hollow objects
+                if (parentObject != null
+                        && parentObject.getPersistenceState() != PersistenceState.HOLLOW) {
+                    node.linkToParent(object, parentObject);
+                }
             }
         }
 
         // now deal with snapshots
+
+        // TODO: refactoring: dupes will clutter the lists and cause extra processing...
+        // removal of dupes happens only downstream, as we need the objects matching
+        // fetched rows for joint prefetch resolving... maybe pushback unique and
+        // non-unique lists to the "node", instead of returning a single list from this
+        // method
         cache.snapshotsUpdatedForObjects(results, rows, refreshObjects);
 
         return results;
@@ -292,6 +333,10 @@
         return descriptor;
     }
 
+    EntityResolver getEntityResolver() {
+        return context.getEntityResolver();
+    }
+
     ObjectId createObjectId(DataRow dataRow, ObjEntity objEntity, String namePrefix) {
 
         Collection<DbAttribute> pk = objEntity == this.descriptor.getEntity()

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ObjectTreeResolver.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ObjectTreeResolver.java?rev=811957&r1=811956&r2=811957&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ObjectTreeResolver.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ObjectTreeResolver.java Mon Sep  7 00:33:46 2009
@@ -20,20 +20,18 @@
 package org.apache.cayenne.access;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.DataRow;
-import org.apache.cayenne.ObjectId;
-import org.apache.cayenne.PersistenceState;
 import org.apache.cayenne.Persistent;
-import org.apache.cayenne.map.ObjEntity;
 import org.apache.cayenne.query.PrefetchProcessor;
 import org.apache.cayenne.query.PrefetchTreeNode;
 import org.apache.cayenne.query.QueryMetadata;
-import org.apache.cayenne.reflect.ArcProperty;
 
 /**
  * Processes a number of DataRow sets corresponding to a given prefetch tree, resolving
@@ -77,7 +75,8 @@
         // create a copy of the tree using DecoratedPrefetchNodes and then traverse it
         // resolving objects...
         PrefetchProcessorNode decoratedTree = new PrefetchProcessorTreeBuilder(
-                this, mainResultRows,
+                this,
+                mainResultRows,
                 extraResultsByPath).buildTree(tree);
 
         // do a single path for disjoint prefetches, joint subtrees will be processed at
@@ -109,124 +108,69 @@
                 return true;
             }
 
-            List objects;
-
-            // disjoint node that is an instance of DecoratedJointNode is a top
-            // of a local joint prefetch "group"...
-            if (processorNode instanceof PrefetchProcessorJointNode) {
-                JointProcessor subprocessor = new JointProcessor(
-                        (PrefetchProcessorJointNode) processorNode);
-                Iterator it = processorNode.getDataRows().iterator();
-                while (it.hasNext()) {
-                    subprocessor.setCurrentFlatRow((DataRow) it.next());
-                    processorNode.traverse(subprocessor);
-                }
-
-                objects = processorNode.getObjects();
-
-                cache.snapshotsUpdatedForObjects(
-                        objects,
-                        ((PrefetchProcessorJointNode) processorNode).getResolvedRows(),
-                        queryMetadata.isRefreshingObjects());
-            }
-            // disjoint prefetch on flattened relationships still requires manual matching
-            else if (processorNode.getIncoming() != null
-                    && processorNode.getIncoming().getRelationship().isFlattened()) {
+            if (processorNode.isPartitionedByParent()) {
 
-                objects = processorNode.getResolver().relatedObjectsFromDataRows(
+                List objects = processorNode.getResolver().relatedObjectsFromDataRows(
                         processorNode.getDataRows(),
                         processorNode);
                 processorNode.setObjects(objects);
             }
             else {
-                objects = processorNode.getResolver().objectsFromDataRows(
+                List objects = processorNode.getResolver().objectsFromDataRows(
                         processorNode.getDataRows());
                 processorNode.setObjects(objects);
             }
 
-            // ... continue with processing even if the objects list is empty to handle
-            // multi-step prefetches.
-            if (objects.isEmpty()) {
-                return true;
-            }
+            return true;
+        }
 
-            // create temporary relationship mapping if needed..; flattened relationships
-            // are matched with parents during resolving phase, so skip them here.
-            if (processorNode.isPartitionedByParent()
-                    && !processorNode.getIncoming().getRelationship().isFlattened()) {
-
-                ObjEntity sourceObjEntity = null;
-                String relatedIdPrefix = null;
-
-                // determine resolution strategy
-                ArcProperty reverseArc = processorNode
-                        .getIncoming()
-                        .getComplimentaryReverseArc();
-
-                // if null, prepare for manual matching
-                if (reverseArc == null) {
-                    relatedIdPrefix = processorNode
-                            .getIncoming()
-                            .getRelationship()
-                            .getReverseDbRelationshipPath()
-                            + ".";
-
-                    sourceObjEntity = (ObjEntity) processorNode
-                            .getIncoming()
-                            .getRelationship()
-                            .getSourceEntity();
-                }
+        public boolean startJointPrefetch(PrefetchTreeNode node) {
 
-                Iterator it = objects.iterator();
-                while (it.hasNext()) {
-                    Persistent destinationObject = (Persistent) it.next();
-                    Persistent sourceObject = null;
-
-                    if (reverseArc != null) {
-                        sourceObject = (Persistent) reverseArc
-                                .readProperty(destinationObject);
-                    }
-                    else {
-                        ObjectStore objectStore = context.getObjectStore();
+            // delegate processing of the top level joint prefetch to a joint processor,
+            // skip non-top joint nodes
 
-                        // prefetched snapshots contain parent ids prefixed with
-                        // relationship name.
+            if (node.getParent() != null && !node.getParent().isJointPrefetch()) {
 
-                        DataRow snapshot = objectStore.getSnapshot(destinationObject
-                                .getObjectId());
+                PrefetchProcessorJointNode processorNode = (PrefetchProcessorJointNode) node;
 
-                        ObjectId id = processorNode.getResolver().createObjectId(
-                                snapshot,
-                                sourceObjEntity,
-                                relatedIdPrefix);
-
-                        if (id == null) {
-                            throw new CayenneRuntimeException(
-                                    "Can't build ObjectId from row: "
-                                            + snapshot
-                                            + ", entity: "
-                                            + sourceObjEntity.getName()
-                                            + ", prefix: "
-                                            + relatedIdPrefix);
-                        }
+                JointProcessor subprocessor = new JointProcessor(
+                        (PrefetchProcessorJointNode) node);
 
-                        sourceObject = (Persistent) objectStore.getNode(id);
-                    }
+                PrefetchProcessorNode parent = (PrefetchProcessorNode) processorNode
+                        .getParent();
+                
+                while(parent != null && parent.isPhantom()) {
+                    parent = (PrefetchProcessorNode) parent.getParent();
+                }
+                
+                if(parent == null) {
+                    return false;
+                }
+                
+                List parentRows = parent.getDataRows();
 
-                    // don't attach to hollow objects
-                    if (sourceObject != null
-                            && sourceObject.getPersistenceState() != PersistenceState.HOLLOW) {
-                        processorNode.linkToParent(destinationObject, sourceObject);
-                    }
+                // phantom node?
+                if (parentRows == null || parentRows.size() == 0) {
+                    return false;
                 }
-            }
 
-            return true;
-        }
+                List parentObjects = parent.getObjects();
+                int size = parentRows.size();
 
-        public boolean startJointPrefetch(PrefetchTreeNode node) {
-            // allow joint prefetch nodes to process their children, but skip their own
-            // processing.
+                for (int i = 0; i < size; i++) {
+                    subprocessor.setCurrentFlatRow((DataRow) parentRows.get(i));
+                    parent.setLastResolved((Persistent) parentObjects.get(i));
+                    processorNode.traverse(subprocessor);
+                }
+
+                List objects = processorNode.getObjects();
+
+                cache.snapshotsUpdatedForObjects(
+                        objects,
+                        processorNode.getResolvedRows(),
+                        queryMetadata.isRefreshingObjects());
+
+            }
             return true;
         }
 
@@ -239,7 +183,27 @@
         }
 
         public void finishPrefetch(PrefetchTreeNode node) {
-            // noop
+            // now that all the children are processed, we can clear the dupes
+
+            // TODO: see TODO in ObjectResolver.relatedObjectsFromDataRows
+
+            if (node.isDisjointPrefetch()) {
+                PrefetchProcessorNode processorNode = (PrefetchProcessorNode) node;
+                if (processorNode.isJointChildren()) {
+                    List<Persistent> objects = processorNode.getObjects();
+
+                    if (objects != null && objects.size() > 1) {
+
+                        Set<Persistent> seen = new HashSet<Persistent>(objects.size());
+                        Iterator<Persistent> it = objects.iterator();
+                        while (it.hasNext()) {
+                            if (!seen.add(it.next())) {
+                                it.remove();
+                            }
+                        }
+                    }
+                }
+            }
         }
     }
 
@@ -260,7 +224,8 @@
 
         public boolean startDisjointPrefetch(PrefetchTreeNode node) {
             // disjoint prefetch that is not the root terminates the walk...
-            return node == rootNode ? startJointPrefetch(node) : false;
+            // don't process the root node itself..
+            return node == rootNode;
         }
 
         public boolean startJointPrefetch(PrefetchTreeNode node) {
@@ -286,10 +251,9 @@
                 processorNode.addObject(object, row);
             }
 
-            // categorization by parent needed even if an object is already there
+            // linking by parent needed even if an object is already there
             // (many-to-many case)
             if (processorNode.isPartitionedByParent()) {
-
                 PrefetchProcessorNode parent = (PrefetchProcessorNode) processorNode
                         .getParent();
                 processorNode.linkToParent(object, parent.getLastResolved());

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorJointNode.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorJointNode.java?rev=811957&r1=811956&r2=811957&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorJointNode.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorJointNode.java Mon Sep  7 00:33:46 2009
@@ -37,9 +37,16 @@
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbJoin;
 import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.map.EntityInheritanceTree;
 import org.apache.cayenne.map.ObjAttribute;
-import org.apache.cayenne.map.ObjRelationship;
+import org.apache.cayenne.map.ObjEntity;
 import org.apache.cayenne.query.PrefetchTreeNode;
+import org.apache.cayenne.reflect.ArcProperty;
+import org.apache.cayenne.reflect.AttributeProperty;
+import org.apache.cayenne.reflect.ClassDescriptor;
+import org.apache.cayenne.reflect.PropertyVisitor;
+import org.apache.cayenne.reflect.ToManyProperty;
+import org.apache.cayenne.reflect.ToOneProperty;
 
 /**
  * A specialized PrefetchTreeNode used for joint prefetch resolving.
@@ -132,6 +139,23 @@
             row.put(column.getName(), flatRow.get(column.getDataRowKey()));
         }
 
+        // since JDBC row reader won't inject JOINED entity name, we have to
+        // detect it here...
+
+        ObjEntity entity = null;
+        ClassDescriptor descriptor = resolver.getDescriptor();
+        EntityInheritanceTree entityInheritanceTree = descriptor
+                .getEntityInheritanceTree();
+
+        if (entityInheritanceTree != null) {
+            entity = entityInheritanceTree.entityMatchingRow(row);
+        }
+
+        if (entity == null) {
+            entity = descriptor.getEntity();
+        }
+
+        row.setEntityName(entity.getName());
         return row;
     }
 
@@ -142,7 +166,7 @@
      * Configures row columns mapping for this node entity.
      */
     private void buildRowMapping() {
-        Map<String, ColumnDescriptor> targetSource = new TreeMap<String, ColumnDescriptor>();
+        final Map<String, ColumnDescriptor> targetSource = new TreeMap<String, ColumnDescriptor>();
 
         // build a DB path .. find parent node that terminates the joint group...
         PrefetchTreeNode jointRoot = this;
@@ -150,7 +174,7 @@
             jointRoot = jointRoot.getParent();
         }
 
-        String prefix;
+        final String prefix;
         if (jointRoot != this) {
             Expression objectPath = Expression.fromString(getPath(jointRoot));
             ASTPath translated = (ASTPath) ((PrefetchProcessorNode) jointRoot)
@@ -183,26 +207,47 @@
             }
         }
 
-        // add class attributes
-        for (ObjAttribute attribute : getResolver().getEntity().getAttributes()) {
-            String target = attribute.getDbAttributePath();
+        ClassDescriptor descriptor = resolver.getDescriptor();
 
-            appendColumn(targetSource, target, prefix + target);
-        }
-
-        // add relationships
-        for (ObjRelationship rel : getResolver().getEntity().getRelationships()) {
-            DbRelationship dbRel = rel.getDbRelationships().get(0);
-            for (DbAttribute attribute : dbRel.getSourceAttributes()) {
-                String target = attribute.getName();
+        descriptor.visitAllProperties(new PropertyVisitor() {
 
+            public boolean visitAttribute(AttributeProperty property) {
+                String target = property.getAttribute().getDbAttributePath();
                 appendColumn(targetSource, target, prefix + target);
+                return true;
             }
+
+            public boolean visitToMany(ToManyProperty property) {
+                return visitRelationship(property);
+            }
+
+            public boolean visitToOne(ToOneProperty property) {
+                return visitRelationship(property);
+            }
+
+            private boolean visitRelationship(ArcProperty arc) {
+                DbRelationship dbRel = arc.getRelationship().getDbRelationships().get(0);
+                for (DbAttribute attribute : dbRel.getSourceAttributes()) {
+                    String target = attribute.getName();
+
+                    appendColumn(targetSource, target, prefix + target);
+                }
+                return true;
+            }
+        });
+
+        // append id columns ... (some may have been appended already via relationships)
+        for (String pkName : descriptor.getEntity().getPrimaryKeyNames()) {
+            appendColumn(targetSource, pkName, prefix + pkName);
         }
 
-        // add unmapped PK
-        for (DbAttribute pk : getResolver().getEntity().getDbEntity().getPrimaryKeys()) {
-            appendColumn(targetSource, pk.getName(), prefix + pk.getName());
+        // append inheritance discriminator columns...
+        Iterator<ObjAttribute> discriminatorColumns = descriptor
+                .getDiscriminatorColumns();
+        while (discriminatorColumns.hasNext()) {
+            ObjAttribute column = discriminatorColumns.next();
+            String target = column.getDbAttributePath();
+            appendColumn(targetSource, target, prefix + target);
         }
 
         int size = targetSource.size();

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorNode.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorNode.java?rev=811957&r1=811956&r2=811957&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorNode.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorNode.java Mon Sep  7 00:33:46 2009
@@ -32,17 +32,13 @@
 import org.apache.cayenne.query.PrefetchTreeNode;
 import org.apache.cayenne.reflect.ArcProperty;
 import org.apache.cayenne.reflect.ToOneProperty;
+import org.apache.cayenne.util.ToStringBuilder;
 
 /**
  * A specialized PrefetchTreeNode used for disjoint prefetch resolving.
  * 
  * @since 1.2
  */
-// TODO: Andrus 2/9/2006 optional to-one relationships (Painting -> Artist) are not
-// connected by this algorithm. They are being intercepted later when a corresponding
-// fault is being resolved, but this seems like a wasteful approach. Test case that
-// succeeds, but goes through this wasteful route is
-// DataContextPrefetchTst.testPrefetchingToOneNull().
 class PrefetchProcessorNode extends PrefetchTreeNode {
 
     List dataRows;
@@ -53,7 +49,6 @@
 
     Map partitionByParent;
     boolean jointChildren;
-    boolean partitionedByParent;
 
     Persistent lastResolved;
 
@@ -67,11 +62,7 @@
      */
     void afterInit() {
 
-        partitionedByParent = !phantom
-                && incoming != null
-                && incoming.getRelationship().isSourceIndependentFromTargetChange();
-
-        if (partitionedByParent) {
+        if (isPartitionedByParent()) {
             partitionByParent = new HashMap();
         }
     }
@@ -227,7 +218,7 @@
     }
 
     boolean isPartitionedByParent() {
-        return partitionedByParent;
+        return parent != null;
     }
 
     Persistent getLastResolved() {
@@ -237,4 +228,13 @@
     void setLastResolved(Persistent lastResolved) {
         this.lastResolved = lastResolved;
     }
+
+    @Override
+    public String toString() {
+        String label = incoming != null ? incoming.getName() : "<root>";
+
+        return new ToStringBuilder(this).append("incoming", label).append(
+                "phantom",
+                phantom).toString();
+    }
 }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/SelectTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/SelectTranslator.java?rev=811957&r1=811956&r2=811957&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/SelectTranslator.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/trans/SelectTranslator.java Mon Sep  7 00:33:46 2009
@@ -52,6 +52,8 @@
 import org.apache.cayenne.reflect.ToManyProperty;
 import org.apache.cayenne.reflect.ToOneProperty;
 import org.apache.cayenne.util.CayenneMapEntry;
+import org.apache.cayenne.util.EqualsBuilder;
+import org.apache.cayenne.util.HashCodeBuilder;
 
 /**
  * A builder of JDBC PreparedStatements based on Cayenne SelectQueries. Translates
@@ -125,8 +127,8 @@
 
         // build qualifier
         QualifierTranslator qualifierTranslator = adapter.getQualifierTranslator(this);
-        StringBuilder qualifierBuffer = qualifierTranslator.appendPart(
-                new StringBuilder());
+        StringBuilder qualifierBuffer = qualifierTranslator
+                .appendPart(new StringBuilder());
 
         // build ORDER BY
         OrderingTranslator orderingTranslator = new OrderingTranslator(this);
@@ -187,8 +189,8 @@
 
         // append tables and joins
         joins.appendRootWithQuoteSqlIdentifiers(queryBuf, getRootDbEntity());
-        
-        //join parameters will be added to head of query
+
+        // join parameters will be added to head of query
         parameterIndex = 0;
 
         joins.appendJoins(queryBuf);
@@ -295,12 +297,11 @@
     /**
      * Appends columns needed for object SelectQuery to the provided columns list.
      */
-    // TODO: this whole method screams REFACTORING!!!
     List<ColumnDescriptor> appendQueryColumns(
             final List<ColumnDescriptor> columns,
             SelectQuery query) {
 
-        final Set<DbAttribute> attributes = new HashSet<DbAttribute>();
+        final Set<ColumnTracker> attributes = new HashSet<ColumnTracker>();
 
         // fetched attributes include attributes that are either:
         // 
@@ -373,9 +374,6 @@
 
         // special handling of a disjoint query...
 
-        // TODO, Andrus 11/17/2005 - resultPath mechanism is generic and should probably
-        // be moved in the superclass (SelectQuery), replacing customDbAttributes.
-
         if (query instanceof PrefetchSelectQuery) {
 
             // for each relationship path add closest FK or PK, for each attribute path,
@@ -392,8 +390,7 @@
                 for (PathComponent<DbAttribute, DbRelationship> component : table
                         .resolvePath(pathExp, getPathAliases())) {
 
-                    // do not add join for the last DB Rel
-                    if (component.getRelationship() != null && !component.isLast()) {
+                    if (component.getRelationship() != null) {
                         dbRelationshipAdded(component.getRelationship(), component
                                 .getJoinType(), null);
                     }
@@ -401,8 +398,6 @@
                     lastComponent = component;
                 }
 
-                String labelPrefix = pathExp.toString().substring("db:".length());
-
                 // process terminating element
                 if (lastComponent != null) {
 
@@ -410,37 +405,18 @@
 
                     if (relationship != null) {
 
-                        // add last join
-                        if (relationship.isToMany()) {
-                            dbRelationshipAdded(relationship, JoinType.INNER, null);
-                        }
-
-                        for (DbJoin j : relationship.getJoins()) {
+                        String labelPrefix = pathExp.toString().substring("db:".length());
+                        DbEntity targetEntity = (DbEntity) relationship.getTargetEntity();
 
-                            DbAttribute attribute = relationship.isToMany() ? j
-                                    .getTarget() : j.getSource();
+                        for (DbAttribute pk : targetEntity.getPrimaryKeys()) {
 
                             // note that we my select a source attribute, but label it as
                             // target for simplified snapshot processing
-                            appendColumn(
-                                    columns,
-                                    null,
-                                    attribute,
-                                    attributes,
-                                    labelPrefix + '.' + j.getTargetName());
+                            appendColumn(columns, null, pk, attributes, labelPrefix
+                                    + '.'
+                                    + pk.getName());
                         }
                     }
-
-                    else {
-
-                        // label prefix already includes relationship name
-                        appendColumn(
-                                columns,
-                                null,
-                                lastComponent.getAttribute(),
-                                attributes,
-                                labelPrefix);
-                    }
                 }
             }
         }
@@ -526,7 +502,7 @@
             final List<ColumnDescriptor> columns,
             SelectQuery query) {
 
-        Set<DbAttribute> skipSet = new HashSet<DbAttribute>();
+        Set<ColumnTracker> skipSet = new HashSet<ColumnTracker>();
 
         ClassDescriptor descriptor = queryMetadata.getClassDescriptor();
         ObjEntity oe = descriptor.getEntity();
@@ -576,11 +552,12 @@
             List<ColumnDescriptor> columns,
             ObjAttribute objAttribute,
             DbAttribute attribute,
-            Set<DbAttribute> skipSet,
+            Set<ColumnTracker> skipSet,
             String label) {
 
-        if (skipSet.add(attribute)) {
-            String alias = getCurrentAlias();
+        String alias = getCurrentAlias();
+        if (skipSet.add(new ColumnTracker(alias, attribute))) {
+
             ColumnDescriptor column = (objAttribute != null) ? new ColumnDescriptor(
                     objAttribute,
                     attribute,
@@ -650,4 +627,35 @@
     public boolean supportsTableAliases() {
         return true;
     }
+
+    final class ColumnTracker {
+
+        private DbAttribute attribute;
+        private String alias;
+
+        ColumnTracker(String alias, DbAttribute attribute) {
+            this.attribute = attribute;
+            this.alias = alias;
+        }
+
+        @Override
+        public boolean equals(Object object) {
+            if (object instanceof ColumnTracker) {
+                ColumnTracker other = (ColumnTracker) object;
+                return new EqualsBuilder().append(alias, other.alias).append(
+                        attribute,
+                        other.attribute).isEquals();
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return new HashCodeBuilder(31, 5)
+                    .append(alias)
+                    .append(attribute)
+                    .toHashCode();
+        }
+
+    }
 }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/EntityInheritanceTree.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/EntityInheritanceTree.java?rev=811957&r1=811956&r2=811957&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/EntityInheritanceTree.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/EntityInheritanceTree.java Mon Sep  7 00:33:46 2009
@@ -134,6 +134,30 @@
         return entity;
     }
 
+    /**
+     * @since 3.0
+     */
+    public Collection<ObjEntity> allSubEntities() {
+        if (subentities == null) {
+            return Collections.singletonList(entity);
+        }
+
+        Collection<ObjEntity> c = new ArrayList<ObjEntity>();
+        appendSubentities(c);
+        return c;
+    }
+
+    private void appendSubentities(Collection<ObjEntity> c) {
+        c.add(entity);
+        if (subentities == null) {
+            return;
+        }
+
+        for (EntityInheritanceTree subentity : subentities) {
+            subentity.appendSubentities(c);
+        }
+    }
+
     public Collection<ObjAttribute> allAttributes() {
         if (subentities == null) {
             return entity.getAttributes();

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=811957&r1=811956&r2=811957&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 Mon Sep  7 00:33:46 2009
@@ -107,12 +107,7 @@
 
         // setup extra result columns to be able to relate result rows to the parent
         // result objects.
-        if (relationship.isFlattened()
-                || (relationship.isToMany() && relationship.getReverseRelationship() == null)) {
-
-            prefetchQuery.addResultPath("db:"
-                    + relationship.getReverseDbRelationshipPath());
-        }
+        prefetchQuery.addResultPath("db:" + relationship.getReverseDbRelationshipPath());
 
         // pass prefetch subtree to enable joint prefetches...
         prefetchQuery.setPrefetchTree(node);

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextPrefetchMultistepTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextPrefetchMultistepTest.java?rev=811957&r1=811956&r2=811957&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextPrefetchMultistepTest.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextPrefetchMultistepTest.java Mon Sep  7 00:33:46 2009
@@ -17,7 +17,6 @@
  *  under the License.
  ****************************************************************/
 
-
 package org.apache.cayenne.access;
 
 import java.util.Collections;
@@ -31,6 +30,7 @@
 import org.apache.cayenne.Fault;
 import org.apache.cayenne.ObjectId;
 import org.apache.cayenne.PersistenceState;
+import org.apache.cayenne.Persistent;
 import org.apache.cayenne.ValueHolder;
 import org.apache.cayenne.exp.Expression;
 import org.apache.cayenne.query.PrefetchTreeNode;
@@ -38,7 +38,6 @@
 
 /**
  * Testing chained prefetches...
- * 
  */
 public class DataContextPrefetchMultistepTest extends DataContextCase {
 
@@ -53,6 +52,12 @@
 
     public void testToManyToManyFirstStepUnresolved() throws Exception {
 
+        // since objects for the phantom prefetches are not retained explicitly, they may
+        // get garbage collected, and we won't be able to detect them
+        // so ensure ObjectStore uses a regular map just for this test
+
+        context.getObjectStore().objectMap = new HashMap<Object, Persistent>();
+
         // Check the target ArtistExhibit objects do not exist yet
 
         Map id1 = new HashMap();
@@ -85,6 +90,7 @@
         // however the target objects must be resolved
         ArtistExhibit ae1 = (ArtistExhibit) context.getGraphManager().getNode(oid1);
         ArtistExhibit ae2 = (ArtistExhibit) context.getGraphManager().getNode(oid2);
+
         assertNotNull(ae1);
         assertNotNull(ae2);
         assertEquals(PersistenceState.COMMITTED, ae1.getPersistenceState());

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/InheritanceTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/InheritanceTest.java?rev=811957&r1=811956&r2=811957&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/InheritanceTest.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/InheritanceTest.java Mon Sep  7 00:33:46 2009
@@ -19,11 +19,12 @@
 
 package org.apache.cayenne.access;
 
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
 
 import org.apache.cayenne.DataObjectUtils;
-import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.query.PrefetchTreeNode;
 import org.apache.cayenne.query.SQLTemplate;
 import org.apache.cayenne.query.SelectQuery;
 import org.apache.cayenne.testdo.inherit.AbstractPerson;
@@ -67,6 +68,138 @@
         assertNotNull(note);
         assertNotNull(note.getPerson());
         assertTrue(note.getPerson() instanceof Employee);
+    }
+
+    public void testRelationshipAbstractFromSuperPrefetchingJoint() {
+        context
+                .performGenericQuery(new SQLTemplate(
+                        AbstractPerson.class,
+                        "INSERT INTO PERSON (PERSON_ID, NAME, PERSON_TYPE) VALUES (3, 'AA', 'EE')"));
+
+        context.performGenericQuery(new SQLTemplate(
+                PersonNotes.class,
+                "INSERT INTO PERSON_NOTES (ID, NOTES, PERSON_ID) VALUES (3, 'AA', 3)"));
+        context.performGenericQuery(new SQLTemplate(
+                PersonNotes.class,
+                "INSERT INTO PERSON_NOTES (ID, NOTES, PERSON_ID) VALUES (4, 'BB', 3)"));
+
+        SelectQuery query = new SelectQuery(AbstractPerson.class);
+        query.addPrefetch(AbstractPerson.NOTES_PROPERTY).setSemantics(
+                PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
+
+        AbstractPerson person = (AbstractPerson) DataObjectUtils.objectForQuery(
+                createDataContext(),
+                query);
+
+        assertTrue(person instanceof Employee);
+
+        blockQueries();
+        try {
+            assertEquals(2, person.getNotes().size());
+
+            String[] names = new String[2];
+            names[0] = person.getNotes().get(0).getNotes();
+            names[1] = person.getNotes().get(1).getNotes();
+            List<String> nameSet = Arrays.asList(names);
+
+            assertTrue(nameSet.contains("AA"));
+            assertTrue(nameSet.contains("BB"));
+        }
+        finally {
+            unblockQueries();
+        }
+    }
+
+    public void testRelationshipAbstractFromSuperPrefetchingDisjoint() {
+        context
+                .performGenericQuery(new SQLTemplate(
+                        AbstractPerson.class,
+                        "INSERT INTO PERSON (PERSON_ID, NAME, PERSON_TYPE) VALUES (3, 'AA', 'EE')"));
+
+        context.performGenericQuery(new SQLTemplate(
+                PersonNotes.class,
+                "INSERT INTO PERSON_NOTES (ID, NOTES, PERSON_ID) VALUES (3, 'AA', 3)"));
+        context.performGenericQuery(new SQLTemplate(
+                PersonNotes.class,
+                "INSERT INTO PERSON_NOTES (ID, NOTES, PERSON_ID) VALUES (4, 'BB', 3)"));
+
+        SelectQuery query = new SelectQuery(AbstractPerson.class);
+        query.addPrefetch(AbstractPerson.NOTES_PROPERTY);
+
+        AbstractPerson person = (AbstractPerson) DataObjectUtils.objectForQuery(
+                createDataContext(),
+                query);
+
+        assertTrue(person instanceof Employee);
+
+        blockQueries();
+        try {
+            assertEquals(2, person.getNotes().size());
+
+            String[] names = new String[2];
+            names[0] = person.getNotes().get(0).getNotes();
+            names[1] = person.getNotes().get(1).getNotes();
+            List<String> nameSet = Arrays.asList(names);
+
+            assertTrue(nameSet.contains("AA"));
+            assertTrue(nameSet.contains("BB"));
+        }
+        finally {
+            unblockQueries();
+        }
+    }
+
+    public void testRelationshipAbstractToSuperPrefetchingDisjoint() {
+        context
+                .performGenericQuery(new SQLTemplate(
+                        AbstractPerson.class,
+                        "INSERT INTO PERSON (PERSON_ID, NAME, PERSON_TYPE) VALUES (2, 'AA', 'EE')"));
+
+        context.performGenericQuery(new SQLTemplate(
+                PersonNotes.class,
+                "INSERT INTO PERSON_NOTES (ID, NOTES, PERSON_ID) VALUES (2, 'AA', 2)"));
+
+        SelectQuery query = new SelectQuery(PersonNotes.class);
+        query.addPrefetch(PersonNotes.PERSON_PROPERTY);
+
+        PersonNotes note = (PersonNotes) DataObjectUtils.objectForQuery(
+                createDataContext(),
+                query);
+
+        blockQueries();
+        try {
+            assertEquals("AA", note.getPerson().getName());
+        }
+        finally {
+            unblockQueries();
+        }
+    }
+
+    public void testRelationshipAbstractToSuperPrefetchingJoint() {
+        context
+                .performGenericQuery(new SQLTemplate(
+                        AbstractPerson.class,
+                        "INSERT INTO PERSON (PERSON_ID, NAME, PERSON_TYPE) VALUES (3, 'AA', 'EE')"));
+
+        context.performGenericQuery(new SQLTemplate(
+                PersonNotes.class,
+                "INSERT INTO PERSON_NOTES (ID, NOTES, PERSON_ID) VALUES (3, 'AA', 3)"));
+
+        SelectQuery query = new SelectQuery(PersonNotes.class);
+        query.addPrefetch(PersonNotes.PERSON_PROPERTY).setSemantics(
+                PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
+
+        PersonNotes note = (PersonNotes) DataObjectUtils.objectForQuery(
+                createDataContext(),
+                query);
+
+        blockQueries();
+        try {
+            assertEquals("AA", note.getPerson().getName());
+        }
+        finally {
+            unblockQueries();
+        }
 
     }
 

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/JointPrefetchTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/JointPrefetchTest.java?rev=811957&r1=811956&r2=811957&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/JointPrefetchTest.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/JointPrefetchTest.java Mon Sep  7 00:33:46 2009
@@ -21,6 +21,7 @@
 
 import java.sql.Date;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -33,6 +34,7 @@
 import org.apache.cayenne.DataRow;
 import org.apache.cayenne.ObjectId;
 import org.apache.cayenne.PersistenceState;
+import org.apache.cayenne.Persistent;
 import org.apache.cayenne.ValueHolder;
 import org.apache.cayenne.exp.Expression;
 import org.apache.cayenne.exp.ExpressionFactory;
@@ -45,7 +47,6 @@
 
 /**
  * Tests joint prefetch handling by Cayenne access stack.
- * 
  */
 public class JointPrefetchTest extends CayenneCase {
 
@@ -231,7 +232,8 @@
                     Artist a = p.getToArtist();
                     assertNotNull(a);
                     assertNotNull(a.getDateOfBirth());
-                    assertTrue(Date.class.isAssignableFrom(a.getDateOfBirth().getClass()));
+                    assertTrue(a.getDateOfBirth().getClass().getName(), Date.class
+                            .isAssignableFrom(a.getDateOfBirth().getClass()));
                 }
             }
             finally {
@@ -341,6 +343,9 @@
 
         DataContext context = createDataContext();
 
+        // make sure phantomly prefetched objects are not deallocated
+        context.getObjectStore().objectMap = new HashMap<Object, Persistent>();
+
         // sanity check...
         DataObject g1 = (DataObject) context.getGraphManager().getNode(
                 new ObjectId("Gallery", Gallery.GALLERY_ID_PK_COLUMN, 33001));

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/PrefetchProcessorTreeBuilderTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/PrefetchProcessorTreeBuilderTest.java?rev=811957&r1=811956&r2=811957&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/PrefetchProcessorTreeBuilderTest.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/PrefetchProcessorTreeBuilderTest.java Mon Sep  7 00:33:46 2009
@@ -181,7 +181,7 @@
         assertNotNull(n3);
         assertSame(e3, n3.getResolver().getEntity());
         assertTrue(n3.isPhantom());
-        assertFalse(n3.isPartitionedByParent());
+        assertTrue(n3.isPartitionedByParent());
 
         PrefetchProcessorNode n4 = (PrefetchProcessorNode) n1
                 .getNode("paintingArray.toGallery.exhibitArray");

Modified: cayenne/main/trunk/itests/pojo/src/test/java/org/apache/cayenne/itest/pojo/OneToManyObjectTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/itests/pojo/src/test/java/org/apache/cayenne/itest/pojo/OneToManyObjectTest.java?rev=811957&r1=811956&r2=811957&view=diff
==============================================================================
--- cayenne/main/trunk/itests/pojo/src/test/java/org/apache/cayenne/itest/pojo/OneToManyObjectTest.java (original)
+++ cayenne/main/trunk/itests/pojo/src/test/java/org/apache/cayenne/itest/pojo/OneToManyObjectTest.java Mon Sep  7 00:33:46 2009
@@ -21,6 +21,8 @@
 import java.lang.reflect.Field;
 import java.util.List;
 
+import org.apache.cayenne.PersistenceState;
+import org.apache.cayenne.Persistent;
 import org.apache.cayenne.query.SelectQuery;
 
 public class OneToManyObjectTest extends PojoContextCase {
@@ -94,16 +96,18 @@
         Field f = OneToManyEntity1.class.getDeclaredField("$cay_faultResolved_toMany");
         assertEquals(Boolean.TRUE, f.get(o1));
 
-        blockContextQueries();
+        List<ManyToOneEntity1> or;
+        blockDomainQueries();
         try {
-            List<ManyToOneEntity1> or = o1.getToMany();
+            or = o1.getToMany();
             assertNotNull(or);
-
             assertEquals(2, or.size());
+            assertEquals(PersistenceState.COMMITTED, ((Persistent) or.get(0))
+                    .getPersistenceState());
             assertSame(o1, or.get(0).getToOne());
         }
         finally {
-            unblockContextQueries();
+            unblockDomainQueries();
         }
     }