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 17:39:31 UTC
svn commit: r1501800 - in
/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access:
HierarchicalObjectResolverNode.java PrefetchProcessorTreeBuilder.java
Author: aadamchik
Date: Wed Jul 10 15:39:30 2013
New Revision: 1501800
URL: http://svn.apache.org/r1501800
Log:
CAY-1695 Unexpected null value in bidirectional one-to-one prefetch
inferring seen objects based on row version number
TODO: is this a good universal strategy?
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
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=1501800&r1=1501799&r2=1501800&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 15:39:30 2013
@@ -19,24 +19,26 @@
package org.apache.cayenne.access;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
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 {
private PrefetchProcessorNode node;
+ private long txStartRowVersion;
HierarchicalObjectResolverNode(PrefetchProcessorNode node, DataContext context, ClassDescriptor descriptor,
- boolean refresh) {
+ boolean refresh, long txStartRowVersion) {
super(context, descriptor, refresh);
this.node = node;
+ this.txStartRowVersion = txStartRowVersion;
}
@Override
@@ -48,15 +50,7 @@ class HierarchicalObjectResolverNode ext
List<Persistent> results = new ArrayList<Persistent>(rows.size());
- // here we can get the same object repeated multiple times in case of
- // many-to-many between prefetched and main entity... this is needed to
- // connect prefetched objects to the main objects. To avoid needlessly
- // refreshing
- // the same object multiple times, track which objectids area already
- // loaded in
- // this pass
- Map<ObjectId, Persistent> seen = new HashMap<ObjectId, Persistent>();
-
+ GraphManager graphManager = context.getGraphManager();
for (DataRow row : rows) {
// determine entity to use
@@ -67,15 +61,24 @@ class HierarchicalObjectResolverNode ext
// has all needed metadata already cached.
ObjectId anId = createObjectId(row, classDescriptor.getEntity(), null);
- Persistent object = seen.get(anId);
+ // 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.
- if (object == null) {
+ 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);
}
- seen.put(anId, object);
}
// keep the dupe objects (and data rows) around, as there maybe an
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=1501800&r1=1501799&r2=1501800&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 15:39:30 2013
@@ -23,6 +23,8 @@ import java.util.List;
import java.util.Map;
import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.DataObject;
+import org.apache.cayenne.DataRow;
import org.apache.cayenne.query.PrefetchProcessor;
import org.apache.cayenne.query.PrefetchTreeNode;
import org.apache.cayenne.query.QueryMetadata;
@@ -172,11 +174,21 @@ final class PrefetchProcessorTreeBuilder
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();
+ }
+
node.setResolver(new HierarchicalObjectResolverNode(
node,
context,
descriptor,
- queryMetadata.isRefreshingObjects()));
+ queryMetadata.isRefreshingObjects(),
+ txStartRowVersion));
}
else {
node.setResolver(new ObjectResolver(context, descriptor, queryMetadata