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 2013/07/10 18:12:58 UTC

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

Author: aadamchik
Date: Wed Jul 10 16:12:57 2013
New Revision: 1501820

URL: http://svn.apache.org/r1501820
Log:
CAY-1695  Unexpected null value in bidirectional one-to-one prefetch

more unit tests
refactoring to support joint prefetches

Added:
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchObjectResolver.java
Modified:
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolverNode.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/test/java/org/apache/cayenne/access/DataContextPrefetchTest.java

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolverNode.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolverNode.java?rev=1501820&r1=1501819&r2=1501820&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolverNode.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolverNode.java Wed Jul 10 16:12:57 2013
@@ -22,23 +22,19 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.cayenne.CayenneRuntimeException;
-import org.apache.cayenne.DataObject;
 import org.apache.cayenne.DataRow;
 import org.apache.cayenne.ObjectId;
 import org.apache.cayenne.Persistent;
-import org.apache.cayenne.graph.GraphManager;
 import org.apache.cayenne.reflect.ClassDescriptor;
 
-class HierarchicalObjectResolverNode extends ObjectResolver {
+class HierarchicalObjectResolverNode extends PrefetchObjectResolver {
 
     private PrefetchProcessorNode node;
-    private long txStartRowVersion;
 
     HierarchicalObjectResolverNode(PrefetchProcessorNode node, DataContext context, ClassDescriptor descriptor,
             boolean refresh, long txStartRowVersion) {
-        super(context, descriptor, refresh);
+        super(context, descriptor, refresh, txStartRowVersion);
         this.node = node;
-        this.txStartRowVersion = txStartRowVersion;
     }
 
     @Override
@@ -50,7 +46,6 @@ class HierarchicalObjectResolverNode ext
 
         List<Persistent> results = new ArrayList<Persistent>(rows.size());
 
-        GraphManager graphManager = context.getGraphManager();
         for (DataRow row : rows) {
 
             // determine entity to use
@@ -61,24 +56,9 @@ class HierarchicalObjectResolverNode ext
             // has all needed metadata already cached.
             ObjectId anId = createObjectId(row, classDescriptor.getEntity(), null);
 
-            // skip processing of objects that were already processed in this
-            // transaction, either by this node or by some other node...
-            // added per CAY-1695 ..
-
-            // TODO: is it going to have any side effects? It is run from the
-            // synchronized block, so I guess other threads can't stick their
-            // versions of this object in here?
-            // TODO: also this logic implies that main rows are always fetched
-            // first... I guess this has to stay true if prefetching is
-            // involved.
-
-            Persistent object = (Persistent) graphManager.getNode(anId);
-            if (object == null || ((DataObject) object).getSnapshotVersion() < txStartRowVersion) {
-                object = objectFromDataRow(row, anId, classDescriptor);
-
-                if (object == null) {
-                    throw new CayenneRuntimeException("Can't build Object from row: " + row);
-                }
+            Persistent object = objectFromDataRow(row, anId, classDescriptor);
+            if (object == null) {
+                throw new CayenneRuntimeException("Can't build Object from row: " + row);
             }
 
             // keep the dupe objects (and data rows) around, as there maybe an

Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchObjectResolver.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchObjectResolver.java?rev=1501820&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchObjectResolver.java (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchObjectResolver.java Wed Jul 10 16:12:57 2013
@@ -0,0 +1,57 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.access;
+
+import org.apache.cayenne.DataObject;
+import org.apache.cayenne.DataRow;
+import org.apache.cayenne.ObjectId;
+import org.apache.cayenne.Persistent;
+import org.apache.cayenne.reflect.ClassDescriptor;
+
+/**
+ * @since 3.2
+ */
+class PrefetchObjectResolver extends ObjectResolver {
+
+    long txStartRowVersion;
+
+    public PrefetchObjectResolver(DataContext context, ClassDescriptor descriptor, boolean refresh,
+            long txStartRowVersion) {
+        super(context, descriptor, refresh);
+        this.txStartRowVersion = txStartRowVersion;
+    }
+
+    @Override
+    Persistent objectFromDataRow(DataRow row, ObjectId anId, ClassDescriptor classDescriptor) {
+        // skip processing of objects that were already processed in this
+        // transaction, either by this node or by some other node...
+        // added per CAY-1695 ..
+
+        // TODO: is it going to have any side effects? It is run from the
+        // synchronized block, so I guess other threads can't stick their
+        // versions of this object in here?
+        Persistent object = (Persistent) context.getGraphManager().getNode(anId);
+        if (object != null && ((DataObject) object).getSnapshotVersion() >= txStartRowVersion) {
+            return object;
+        }
+
+        return super.objectFromDataRow(row, anId, classDescriptor);
+    }
+
+}

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=1501820&r1=1501819&r2=1501820&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 Wed Jul 10 16:12:57 2013
@@ -173,16 +173,20 @@ final class PrefetchProcessorTreeBuilder
         node.setDataRows(rows);
 
         node.setIncoming(arc);
-        if (node.getParent() != null && !node.isJointPrefetch()) {
-            
-            long txStartRowVersion;
-            if (mainResultRows.isEmpty()) {
-                txStartRowVersion = DataObject.DEFAULT_VERSION;
-            } else {
-                DataRow firstRow = (DataRow) mainResultRows.get(0);
-                txStartRowVersion = firstRow.getVersion();
-            }
-            
+        
+        // TODO: is txStartRowVersion guessed
+        // correctly? i.e. the main rows are always fetched
+        // first? I guess this has to stay true if prefetching is
+        // involved.
+        long txStartRowVersion;
+        if (mainResultRows.isEmpty()) {
+            txStartRowVersion = DataObject.DEFAULT_VERSION;
+        } else {
+            DataRow firstRow = (DataRow) mainResultRows.get(0);
+            txStartRowVersion = firstRow.getVersion();
+        }
+        
+        if (node.getParent() != null && !node.isJointPrefetch()) { 
             node.setResolver(new HierarchicalObjectResolverNode(
                     node,
                     context,
@@ -191,8 +195,8 @@ final class PrefetchProcessorTreeBuilder
                     txStartRowVersion));
         }
         else {
-            node.setResolver(new ObjectResolver(context, descriptor, queryMetadata
-                    .isRefreshingObjects()));
+            node.setResolver(new PrefetchObjectResolver(context, descriptor, queryMetadata
+                    .isRefreshingObjects(), txStartRowVersion));
         }
 
         if (node.getParent() == null || node.getParent().isPhantom()) {

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextPrefetchTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextPrefetchTest.java?rev=1501820&r1=1501819&r2=1501820&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextPrefetchTest.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextPrefetchTest.java Wed Jul 10 16:12:57 2013
@@ -702,9 +702,54 @@ public class DataContextPrefetchTest ext
                 assertEquals(1, results.size());
 
                 Painting p0 = results.get(0);
-                PaintingInfo pi0 = (PaintingInfo) p0.readProperty(Painting.TO_PAINTING_INFO.getName());
+                PaintingInfo pi0 = (PaintingInfo) p0.readPropertyDirectly(Painting.TO_PAINTING_INFO.getName());
                 assertNotNull(pi0);
-                assertNotNull(pi0.readProperty(PaintingInfo.PAINTING.getName()));
+                assertNotNull(pi0.readPropertyDirectly(PaintingInfo.PAINTING.getName()));
+            }
+        });
+    }
+
+    public void testPrefetchPaintingOverToOneAndToMany() throws Exception {
+        createArtistWithTwoPaintingsAndTwoInfosDataSet();
+
+        SelectQuery<Painting> query = new SelectQuery<Painting>(Painting.class);
+        query.andQualifier(Painting.PAINTING_TITLE.eq("p_artist2"));
+        query.addPrefetch(Painting.TO_ARTIST.disjoint());
+        query.addPrefetch(Painting.TO_ARTIST.dot(Artist.PAINTING_ARRAY).disjoint());
+        final List<Painting> results = context.select(query);
+
+        queryInterceptor.runWithQueriesBlocked(new UnitTestClosure() {
+
+            public void execute() {
+                assertEquals(1, results.size());
+
+                Painting p0 = results.get(0);
+                Artist a0 = (Artist) p0.readPropertyDirectly(Painting.TO_ARTIST.getName());
+                assertNotNull(a0);
+                List<?> paintings = (List<?>) a0.readPropertyDirectly(Artist.PAINTING_ARRAY.getName());
+                assertEquals(2, paintings.size());
+            }
+        });
+    }
+    
+    public void testPrefetchToOneWithBackRelationship_Joint() throws Exception {
+        createArtistWithTwoPaintingsAndTwoInfosDataSet();
+
+        SelectQuery<Painting> query = new SelectQuery<Painting>(Painting.class);
+        query.andQualifier(Painting.PAINTING_TITLE.eq("p_artist2"));
+        query.addPrefetch(Painting.TO_PAINTING_INFO.joint());
+        query.addPrefetch(Painting.TO_PAINTING_INFO.dot(PaintingInfo.PAINTING).joint());
+        final List<Painting> results = context.select(query);
+
+        queryInterceptor.runWithQueriesBlocked(new UnitTestClosure() {
+
+            public void execute() {
+                assertEquals(1, results.size());
+
+                Painting p0 = results.get(0);
+                PaintingInfo pi0 = (PaintingInfo) p0.readPropertyDirectly(Painting.TO_PAINTING_INFO.getName());
+                assertNotNull(pi0);
+                assertNotNull(pi0.readPropertyDirectly(PaintingInfo.PAINTING.getName()));
             }
         });
     }