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();
}
}