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 2010/01/23 16:12:26 UTC
svn commit: r902416 - in /cayenne/main/branches/STABLE-3.0:
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/jdbc/
fr...
Author: aadamchik
Date: Sat Jan 23 15:12:25 2010
New Revision: 902416
URL: http://svn.apache.org/viewvc?rev=902416&view=rev
Log:
CAY-1366 EJBQL: Support for fetch joins
patch by Ksenia Khailenko - merging multiple changes to a single 3.0 commit
Added:
cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextEJBQLFetchJoinTest.java
Modified:
cayenne/main/branches/STABLE-3.0/docs/doc/src/main/resources/RELEASE-NOTES.txt
cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolver.java
cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorTreeBuilder.java
cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLFromTranslator.java
cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLIdentifierColumnsTranslator.java
cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLSelectColumnsTranslator.java
cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLSelectTranslator.java
cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EntityRowReader.java
cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLCompiledExpression.java
cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/CompiledExpression.java
cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/Compiler.java
cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLInnerFetchJoin.java
cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLOuterFetchJoin.java
cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/DefaultEntityResultSegment.java
cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/EntityResult.java
cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/EJBQLQuery.java
cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/EJBQLQueryMetadata.java
cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/PrefetchTreeNode.java
Modified: cayenne/main/branches/STABLE-3.0/docs/doc/src/main/resources/RELEASE-NOTES.txt
URL: http://svn.apache.org/viewvc/cayenne/main/branches/STABLE-3.0/docs/doc/src/main/resources/RELEASE-NOTES.txt?rev=902416&r1=902415&r2=902416&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/docs/doc/src/main/resources/RELEASE-NOTES.txt (original)
+++ cayenne/main/branches/STABLE-3.0/docs/doc/src/main/resources/RELEASE-NOTES.txt Sat Jan 23 15:12:25 2010
@@ -18,6 +18,7 @@
CAY-1216 Improve the Remote-Object-Persistence-Tutorial for 3.0
CAY-1326 Document Cayenne Embeddables Feature
CAY-1352 in "code generation" dialog add generation embeddables
+CAY-1366 EJBQL: Support for fetch joins
Bug Fixes Since 3.0RC1:
Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
URL: http://svn.apache.org/viewvc/cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java?rev=902416&r1=902415&r2=902416&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java Sat Jan 23 15:12:25 2010
@@ -20,11 +20,15 @@
package org.apache.cayenne.access;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import org.apache.cayenne.CayenneException;
import org.apache.cayenne.CayenneRuntimeException;
@@ -636,15 +640,31 @@
rowsColumn.add((DataRow) rows.get(i)[position]);
}
List<Persistent> objects;
-
- // take a shortcut when no prefetches exist...
+ if (prefetchTree != null) {
+ PrefetchTreeNode prefetchTreeNode = null;
+ for (PrefetchTreeNode prefetch : prefetchTree.getChildren()) {
+ if (descriptor.getEntity().getName().equals(prefetch.getEntityName())) {
+ if (prefetchTreeNode == null) {
+ prefetchTreeNode = new PrefetchTreeNode();
+ }
+ PrefetchTreeNode addPath = prefetchTreeNode.addPath(prefetch
+ .getPath());
+ addPath.setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
+ addPath.setPhantom(false);
+ }
+ }
+ prefetchTree = prefetchTreeNode;
+ }
if (prefetchTree == null) {
objects = new ObjectResolver(context, descriptor, metadata
.isRefreshingObjects())
.synchronizedObjectsFromDataRows(rowsColumn);
}
else {
- HierarchicalObjectResolver resolver = new HierarchicalObjectResolver(context, metadata);
+ HierarchicalObjectResolver resolver = new HierarchicalObjectResolver(
+ context,
+ metadata,
+ descriptor, true);
objects = resolver.synchronizedObjectsFromDataRows(
prefetchTree,
rowsColumn,
@@ -660,7 +680,6 @@
List<Object> rsMapping = metadata.getResultSetMapping();
int width = rsMapping.size();
-
// no conversions needed for scalar positions; reuse Object[]'s to fill them
// with resolved objects
List<List<?>> resultLists = new ArrayList<List<?>>(width);
@@ -670,16 +689,27 @@
EntityResultSegment entitySegment = (EntityResultSegment) rsMapping
.get(i);
List<Persistent> nextResult = toObjects(entitySegment
- .getClassDescriptor(), null, mainRows, i);
+ .getClassDescriptor(), metadata.getPrefetchTree() , mainRows, i);
+
resultLists.add(nextResult);
-
+
for (int j = 0; j < rowsLen; j++) {
Object[] row = mainRows.get(j);
row[i] = nextResult.get(j);
}
}
}
-
+ Set<List<?>> seen = new HashSet(mainRows.size());
+ Iterator<Object[]> it = mainRows.iterator();
+ while (it.hasNext()) {
+
+ if (!seen.add(Arrays.asList(it.next()))) {
+ it.remove();
+
+ }
+
+ }
+
// invoke callbacks now that all objects are resolved...
LifecycleCallbackRegistry callbackRegistry = context
.getEntityResolver()
Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolver.java
URL: http://svn.apache.org/viewvc/cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolver.java?rev=902416&r1=902415&r2=902416&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolver.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolver.java Sat Jan 23 15:12:25 2010
@@ -32,6 +32,7 @@
import org.apache.cayenne.query.PrefetchProcessor;
import org.apache.cayenne.query.PrefetchTreeNode;
import org.apache.cayenne.query.QueryMetadata;
+import org.apache.cayenne.reflect.ClassDescriptor;
/**
* Processes a number of DataRow sets corresponding to a given prefetch tree, resolving
@@ -43,6 +44,8 @@
DataContext context;
QueryMetadata queryMetadata;
DataRowStore cache;
+ ClassDescriptor descriptor;
+ boolean needToSaveDuplicates;
HierarchicalObjectResolver(DataContext context, QueryMetadata queryMetadata) {
this.queryMetadata = queryMetadata;
@@ -50,6 +53,13 @@
this.cache = context.getObjectStore().getDataRowCache();
}
+ HierarchicalObjectResolver(DataContext context, QueryMetadata metadata,
+ ClassDescriptor descriptor, boolean needToSaveDuplicates) {
+ this(context, metadata);
+ this.descriptor = descriptor;
+ this.needToSaveDuplicates = needToSaveDuplicates;
+ }
+
/**
* Properly synchronized version of 'resolveObjectTree'.
*/
@@ -175,7 +185,7 @@
// TODO: see TODO in ObjectResolver.relatedObjectsFromDataRows
- if (node.isDisjointPrefetch()) {
+ if (node.isDisjointPrefetch() && !needToSaveDuplicates) {
PrefetchProcessorNode processorNode = (PrefetchProcessorNode) node;
if (processorNode.isJointChildren()) {
List<Persistent> objects = processorNode.getObjects();
Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorTreeBuilder.java
URL: http://svn.apache.org/viewvc/cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorTreeBuilder.java?rev=902416&r1=902415&r2=902416&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorTreeBuilder.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorTreeBuilder.java Sat Jan 23 15:12:25 2010
@@ -36,6 +36,7 @@
private PrefetchProcessorNode root;
private LinkedList<PrefetchProcessorNode> nodeStack;
+ private ClassDescriptor descriptor;
private List mainResultRows;
private Map extraResultsByPath;
@@ -45,6 +46,7 @@
this.queryMetadata = objectTreeResolver.queryMetadata;
this.mainResultRows = mainResultRows;
this.extraResultsByPath = extraResultsByPath;
+ this.descriptor=objectTreeResolver.descriptor;
}
PrefetchProcessorNode buildTree(PrefetchTreeNode tree) {
@@ -147,7 +149,11 @@
}
else {
arc = null;
- descriptor = queryMetadata.getClassDescriptor();
+ if(this.descriptor!=null){
+ descriptor=this.descriptor;
+ }else{
+ descriptor = queryMetadata.getClassDescriptor();
+ }
rows = mainResultRows;
}
Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLFromTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLFromTranslator.java?rev=902416&r1=902415&r2=902416&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLFromTranslator.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLFromTranslator.java Sat Jan 23 15:12:25 2010
@@ -21,7 +21,9 @@
import org.apache.cayenne.ejbql.EJBQLBaseVisitor;
import org.apache.cayenne.ejbql.EJBQLExpression;
import org.apache.cayenne.ejbql.parser.EJBQLFromItem;
+import org.apache.cayenne.ejbql.parser.EJBQLInnerFetchJoin;
import org.apache.cayenne.ejbql.parser.EJBQLJoin;
+import org.apache.cayenne.ejbql.parser.EJBQLOuterFetchJoin;
/**
* @since 3.0
@@ -66,8 +68,14 @@
@Override
public boolean visitInnerFetchJoin(EJBQLJoin join) {
- // TODO: andrus, 4/9/2007 - support for prefetching
- throw new UnsupportedOperationException("Fetch joins are not yet supported");
+ joinAppender.appendInnerJoin(
+ null,
+ new EJBQLTableId(join.getLeftHandSideId()),
+ new EJBQLTableId(((EJBQLInnerFetchJoin) join).getRightHandSideId()));
+
+ context.markCurrentPosition(EJBQLJoinAppender
+ .makeJoinTailMarker(((EJBQLInnerFetchJoin) join).getRightHandSideId()));
+ return false;
}
@Override
@@ -84,8 +92,14 @@
@Override
public boolean visitOuterFetchJoin(EJBQLJoin join) {
- // TODO: andrus, 4/9/2007 - support for prefetching
- throw new UnsupportedOperationException("Fetch joins are not yet supported");
+ joinAppender.appendOuterJoin(
+ null,
+ new EJBQLTableId(join.getLeftHandSideId()),
+ new EJBQLTableId(((EJBQLOuterFetchJoin) join).getRightHandSideId()));
+
+ context.markCurrentPosition(EJBQLJoinAppender
+ .makeJoinTailMarker(((EJBQLOuterFetchJoin) join).getRightHandSideId()));
+ return false;
}
@Override
Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLIdentifierColumnsTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLIdentifierColumnsTranslator.java?rev=902416&r1=902415&r2=902416&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLIdentifierColumnsTranslator.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLIdentifierColumnsTranslator.java Sat Jan 23 15:12:25 2010
@@ -27,12 +27,16 @@
import org.apache.cayenne.dba.TypesMapping;
import org.apache.cayenne.ejbql.EJBQLBaseVisitor;
import org.apache.cayenne.ejbql.EJBQLExpression;
+import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.DbJoin;
import org.apache.cayenne.map.DbRelationship;
import org.apache.cayenne.map.ObjAttribute;
+import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.map.ObjRelationship;
+import org.apache.cayenne.map.PathComponent;
+import org.apache.cayenne.query.PrefetchTreeNode;
import org.apache.cayenne.reflect.ArcProperty;
import org.apache.cayenne.reflect.AttributeProperty;
import org.apache.cayenne.reflect.ClassDescriptor;
@@ -151,9 +155,58 @@
appendColumn(idVar, attribute, attribute.getDbAttribute(), fields);
}
+ addPrefetchedColumnsIfAny(idVar);
+
return false;
}
+ private void addPrefetchedColumnsIfAny(final String visitedIdentifier) {
+ PrefetchTreeNode prefetchTree = context.getCompiledExpression().getPrefetchTree();
+ if (prefetchTree != null) {
+ for (PrefetchTreeNode prefetch : prefetchTree.adjacentJointNodes()) {
+ ClassDescriptor descriptor = context.getEntityDescriptor(prefetch
+ .getEjbqlPathEntityId());
+ if (visitedIdentifier.equals(prefetch.getEjbqlPathEntityId())) {
+ DbEntity table = descriptor.getRootDbEntities().iterator().next();
+ ObjEntity objectEntity = descriptor.getEntity();
+ prefetch.setEntityName(objectEntity.getName());
+ Expression prefetchExp = Expression.fromString(prefetch.getPath());
+ Expression dbPrefetch = objectEntity.translateToDbPath(prefetchExp);
+
+ DbRelationship r = null;
+ for (PathComponent<DbAttribute, DbRelationship> component : table
+ .resolvePath(dbPrefetch, context
+ .getMetadata()
+ .getPathSplitAliases())) {
+ r = component.getRelationship();
+
+ }
+
+ if (r == null) {
+ throw new CayenneRuntimeException("Invalid joint prefetch '"
+ + prefetch
+ + "' for entity: "
+ + objectEntity.getName());
+ }
+
+ Iterator<DbAttribute> targetAttributes = (Iterator<DbAttribute>) r
+ .getTargetEntity()
+ .getAttributes()
+ .iterator();
+ while (targetAttributes.hasNext()) {
+ DbAttribute attribute = targetAttributes.next();
+ appendColumn(prefetch.getEjbqlPathEntityId()
+ + "."
+ + prefetch.getPath(), attribute, "", prefetch.getPath()
+ + "."
+ + attribute.getName(), null);
+
+ }
+ }
+ }
+ }
+ }
+
public void appendColumn(
String identifier,
ObjAttribute property,
@@ -168,6 +221,20 @@
DbAttribute column,
Map<String, String> fields,
String javaType) {
+ String columnLabel = "";
+ if (context.isAppendingResultColumns()) {
+ columnLabel = fields.get(property != null ? property
+ .getDbAttributePath() : column.getName());
+ }
+ appendColumn(identifier, column, columnLabel, columnLabel, javaType);
+ }
+
+ public void appendColumn(
+ String identifier,
+ DbAttribute column,
+ String columnAlias,
+ String dataRowKey,
+ String javaType) {
DbEntity table = (DbEntity) column.getEntity();
String alias = context.getTableAlias(identifier, table.getFullyQualifiedName());
@@ -190,18 +257,15 @@
javaType = TypesMapping.getJavaBySqlType(column.getType());
}
- String columnLabel = fields.get(property != null ? property
- .getDbAttributePath() : column.getName());
-
// TODO: andrus 6/27/2007 - the last parameter is an unofficial "jdbcType"
// pending CAY-813 implementation, switch to #column directive
context
.append("' '")
.append(javaType)
.append("' '")
- .append(columnLabel)
+ .append(columnAlias)
.append("' '")
- .append(columnLabel)
+ .append(dataRowKey)
.append("' " + column.getType())
.append(")");
}
Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLSelectColumnsTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLSelectColumnsTranslator.java?rev=902416&r1=902415&r2=902416&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLSelectColumnsTranslator.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLSelectColumnsTranslator.java Sat Jan 23 15:12:25 2010
@@ -104,26 +104,11 @@
context.append(!first ? ", " : " ");
DbAttribute dbAttribute = it.next();
-
- if (context.isAppendingResultColumns()) {
- context.append(" #result('");
- }
- else {
- context.append(' ');
- }
-
- context.append(alias).append('.').append(dbAttribute.getName());
-
- if (context.isAppendingResultColumns()) {
-
- String javaType = TypesMapping.getJavaBySqlType(dbAttribute
- .getType());
- String columnLabel = fields.get(dbAttribute.getName());
-
- context.append("' '").append(javaType).append("' '").append(
- columnLabel).append("' '").append(columnLabel).append(
- "' " + dbAttribute.getType()).append(")");
- }
+ appendColumn(
+ TypesMapping.getJavaBySqlType(dbAttribute.getType()),
+ alias,
+ dbAttribute,
+ fields!=null?fields.get(dbAttribute.getName()):"");
first = false;
}
@@ -151,11 +136,13 @@
else if (pathPart instanceof DbAttribute) {
DbAttribute dbAttribute = (DbAttribute) pathPart;
appendColumn(
- attribute,
+ attribute.getType(),
context.getTableAlias(
lhsId.getEntityId(),
dbAttribute.getEntity().getName()),
- dbAttribute);
+ dbAttribute,
+ context.isAppendingResultColumns() ? context
+ .nextColumnAlias() : "");
}
@@ -166,38 +153,8 @@
DbAttribute dbAttribute = attribute.getDbAttribute();
- appendColumn(attribute, alias, dbAttribute);
- }
- }
-
- private void appendColumn(
- ObjAttribute attribute,
- String alias,
- DbAttribute dbAttribute) {
- if (context.isAppendingResultColumns()) {
- context.append(" #result('");
- }
- else {
- context.append(' ');
- }
-
- context.append(alias).append('.').append(dbAttribute.getName());
-
- if (context.isAppendingResultColumns()) {
- String columnAlias = context.nextColumnAlias();
-
- // TODO: andrus 6/27/2007 - the last parameter is an unofficial
- // "jdbcType"
- // pending CAY-813 implementation, switch to #column directive
- context
- .append("' '")
- .append(attribute.getType())
- .append("' '")
- .append(columnAlias)
- .append("' '")
- .append(columnAlias)
- .append("' " + dbAttribute.getType())
- .append(")");
+ appendColumn(attribute.getType(), alias, dbAttribute, context
+ .isAppendingResultColumns() ? context.nextColumnAlias() : "");
}
}
@@ -213,4 +170,37 @@
return false;
}
+ public void appendColumn(
+ String javaType,
+ String alias,
+ DbAttribute dbAttribute,
+ String columnAlias) {
+ if (context.isAppendingResultColumns()) {
+ context.append(" #result('");
+ }
+ else {
+ context.append(' ');
+ }
+
+ context.append(alias).append('.').append(dbAttribute.getName());
+
+ if (context.isAppendingResultColumns()) {
+ // String columnAlias = context.nextColumnAlias();
+
+ // TODO: andrus 6/27/2007 - the last parameter is an unofficial
+ // "jdbcType"
+ // pending CAY-813 implementation, switch to #column directive
+ context
+ .append("' '")
+ .append(javaType)
+ .append("' '")
+ .append(columnAlias)
+ .append("' '")
+ .append(columnAlias)
+ .append("' " + dbAttribute.getType())
+ .append(")");
+ }
+
+ }
+
}
Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLSelectTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLSelectTranslator.java?rev=902416&r1=902415&r2=902416&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLSelectTranslator.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLSelectTranslator.java Sat Jan 23 15:12:25 2010
@@ -116,4 +116,5 @@
expression.visit(context.getTranslatorFactory().getConditionTranslator(context));
return false;
}
+
}
Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EntityRowReader.java
URL: http://svn.apache.org/viewvc/cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EntityRowReader.java?rev=902416&r1=902415&r2=902416&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EntityRowReader.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EntityRowReader.java Sat Jan 23 15:12:25 2010
@@ -65,7 +65,12 @@
// query translator may change the order of fields compare to the entity
// result, so figure out DataRow labels by doing reverse lookup of
// RowDescriptor labels...
- labels[i] = segmentMetadata.getColumnPath(columns[startIndex + i].getDataRowKey());
+ if(columns[startIndex + i].getDataRowKey().contains(".")){
+ // if the dataRowKey contains ".", it is prefetched column and we can use it instead of search the name by alias
+ labels[i]=columns[startIndex + i].getDataRowKey();
+ }else{
+ labels[i] = segmentMetadata.getColumnPath(columns[startIndex + i].getDataRowKey());
+ }
}
}
Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLCompiledExpression.java
URL: http://svn.apache.org/viewvc/cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLCompiledExpression.java?rev=902416&r1=902415&r2=902416&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLCompiledExpression.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/EJBQLCompiledExpression.java Sat Jan 23 15:12:25 2010
@@ -22,6 +22,7 @@
import org.apache.cayenne.map.DbRelationship;
import org.apache.cayenne.map.SQLResult;
+import org.apache.cayenne.query.PrefetchTreeNode;
import org.apache.cayenne.reflect.ClassDescriptor;
/**
@@ -63,4 +64,9 @@
* expression.
*/
SQLResult getResult();
+
+ /**
+ * Returns prefetched columns tree for fetch joins.
+ */
+ PrefetchTreeNode getPrefetchTree();
}
Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/CompiledExpression.java
URL: http://svn.apache.org/viewvc/cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/CompiledExpression.java?rev=902416&r1=902415&r2=902416&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/CompiledExpression.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/CompiledExpression.java Sat Jan 23 15:12:25 2010
@@ -27,6 +27,7 @@
import org.apache.cayenne.map.DbRelationship;
import org.apache.cayenne.map.ObjRelationship;
import org.apache.cayenne.map.SQLResult;
+import org.apache.cayenne.query.PrefetchTreeNode;
import org.apache.cayenne.reflect.ClassDescriptor;
/**
@@ -42,7 +43,9 @@
private Map<String, ObjRelationship> incomingById;
private EJBQLExpression expression;
private SQLResult result;
-
+ private PrefetchTreeNode prefetchTree;
+
+
public ClassDescriptor getEntityDescriptor(String idVariable) {
if (idVariable == null) {
return null;
@@ -99,4 +102,12 @@
void setResult(SQLResult resultSetMapping) {
this.result = resultSetMapping;
}
+
+ public PrefetchTreeNode getPrefetchTree() {
+ return prefetchTree;
+ }
+
+ public void setPrefetchTree(PrefetchTreeNode prefetchTree) {
+ this.prefetchTree = prefetchTree;
+ }
}
Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/Compiler.java
URL: http://svn.apache.org/viewvc/cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/Compiler.java?rev=902416&r1=902415&r2=902416&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/Compiler.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/Compiler.java Sat Jan 23 15:12:25 2010
@@ -40,6 +40,7 @@
import org.apache.cayenne.map.ObjAttribute;
import org.apache.cayenne.map.ObjRelationship;
import org.apache.cayenne.map.SQLResult;
+import org.apache.cayenne.query.PrefetchTreeNode;
import org.apache.cayenne.reflect.ArcProperty;
import org.apache.cayenne.reflect.AttributeProperty;
import org.apache.cayenne.reflect.ClassDescriptor;
@@ -69,7 +70,8 @@
private EJBQLExpressionVisitor pathVisitor;
private EJBQLExpressionVisitor rootDescriptorVisitor;
private List<Object> resultComponents;
-
+ private PrefetchTreeNode prefetchTree = null;
+
Compiler(EntityResolver resolver) {
this.resolver = resolver;
this.descriptorsById = new HashMap<String, ClassDescriptor>();
@@ -85,7 +87,7 @@
parsed.visit(new CompilationVisitor());
Map<EJBQLPath, Integer> pathsInSelect = new HashMap<EJBQLPath, Integer>();
-
+
if (parsed != null) {
for (int i = 0; i < parsed.getChildrenCount(); i++) {
if (parsed.getChild(i) instanceof EJBQLSelectClause) {
@@ -129,10 +131,8 @@
}
}
}
-
// postprocess paths, now that all id vars are resolved
if (paths != null) {
- int elenent = 0;
for (EJBQLPath path : paths) {
String id = normalizeIdPath(path.getId());
ClassDescriptor descriptor = descriptorsById.get(id);
@@ -156,7 +156,7 @@
incoming = ((ArcProperty) property).getRelationship();
descriptor = ((ArcProperty) property).getTargetDescriptor();
pathRelationshipString = buffer.substring(0, buffer.length());
-
+
descriptorsById.put(pathRelationshipString, descriptor);
incomingById.put(pathRelationshipString, incoming);
@@ -178,11 +178,12 @@
resultComponents.add(pathsInSelect.get(path).intValue(), ident);
rootId = pathRelationshipString;
}
- };
- elenent++;
+ }
}
}
-
+
+
+
CompiledExpression compiled = new CompiledExpression();
compiled.setExpression(parsed);
compiled.setSource(source);
@@ -190,7 +191,8 @@
compiled.setRootId(rootId);
compiled.setDescriptorsById(descriptorsById);
compiled.setIncomingById(incomingById);
-
+ compiled.setPrefetchTree(prefetchTree);
+
if (resultComponents != null) {
SQLResult mapping = new SQLResult();
@@ -200,9 +202,27 @@
mapping.addColumnResult((String) nextMapping);
}
else if (nextMapping instanceof EJBQLExpression) {
- mapping.addEntityResult(compileEntityResult(
+ EntityResult compileEntityResult = compileEntityResult(
(EJBQLExpression) nextMapping,
- i));
+ i);
+ if (prefetchTree != null) {
+ for (PrefetchTreeNode prefetch : prefetchTree.getChildren()) {
+ if (((EJBQLExpression) nextMapping).getText().equals(
+ prefetch.getEjbqlPathEntityId())) {
+ EJBQLIdentifier ident = new EJBQLIdentifier(0);
+ ident.text = prefetch.getEjbqlPathEntityId()
+ + "."
+ + prefetch.getPath();
+
+ compileEntityResult = compileEntityResultWithPrefetch(
+ compileEntityResult,
+ ident);
+
+ }
+ }
+ }
+ mapping.addEntityResult(compileEntityResult);
+
}
}
@@ -213,6 +233,72 @@
return compiled;
}
+ private EntityResult compileEntityResultWithPrefetch(EntityResult compiledResult, EJBQLExpression prefetchExpression){
+ final EntityResult result = compiledResult;
+ String id = prefetchExpression.getText().toLowerCase();
+ ClassDescriptor descriptor = descriptorsById.get(id);
+ if (descriptor == null) {
+ descriptor = descriptorsById.get(prefetchExpression.getText());
+ }
+ final String prefix = prefetchExpression.getText().substring(prefetchExpression.getText().indexOf(".")+1);
+
+ final Set<String> visited = new HashSet<String>();
+
+ PropertyVisitor visitor = new PropertyVisitor() {
+
+ public boolean visitAttribute(AttributeProperty property) {
+ ObjAttribute oa = property.getAttribute();
+ if (visited.add(oa.getDbAttributePath())) {
+ result.addObjectField(
+ oa.getEntity().getName(),
+ "fetch."+prefix+"."+oa.getName(),
+ prefix +"."+ oa.getDbAttributeName());
+ }
+ return true;
+ }
+
+ public boolean visitToMany(ToManyProperty property) {
+ return true;
+ }
+
+ public boolean visitToOne(ToOneProperty property) {
+ ObjRelationship rel = property.getRelationship();
+ DbRelationship dbRel = rel.getDbRelationships().get(0);
+
+ for (DbJoin join : dbRel.getJoins()) {
+ DbAttribute src = join.getSource();
+ if (src.isForeignKey() && visited.add(src.getName())) {
+ result.addDbField("fetch."+prefix+"."+src.getName(), prefix +"."+ src.getName());
+ }
+ }
+
+ return true;
+ }
+ };
+
+ descriptor.visitAllProperties(visitor);
+
+ // append id columns ... (some may have been appended already via relationships)
+ for (String pkName : descriptor.getEntity().getPrimaryKeyNames()) {
+ if (visited.add(pkName)) {
+ result.addDbField("fetch."+prefix+"."+pkName, prefix +"."+ pkName);
+ }
+ }
+
+ // append inheritance discriminator columns...
+ Iterator<ObjAttribute> discriminatorColumns = descriptor
+ .getDiscriminatorColumns();
+ while (discriminatorColumns.hasNext()) {
+ ObjAttribute column = discriminatorColumns.next();
+
+ if (visited.add(column.getName())) {
+ result.addDbField("fetch."+prefix+"."+column.getDbAttributePath(), prefix +"."+ column.getDbAttributePath());
+ }
+ }
+
+ return result;
+ }
+
private EntityResult compileEntityResult(EJBQLExpression expression, int position) {
String id = expression.getText().toLowerCase();
ClassDescriptor descriptor = descriptorsById.get(id);
@@ -329,6 +415,7 @@
@Override
public boolean visitInnerFetchJoin(EJBQLJoin join) {
+ prepareFetchJoin(join);
join.visit(joinVisitor);
return false;
}
@@ -341,6 +428,7 @@
@Override
public boolean visitOuterFetchJoin(EJBQLJoin join) {
+ prepareFetchJoin(join);
join.visit(joinVisitor);
return false;
}
@@ -370,6 +458,20 @@
public boolean visitSubselect(EJBQLExpression expression) {
return super.visitSubselect(expression);
}
+
+ private void prepareFetchJoin(EJBQLJoin join) {
+ if (prefetchTree == null) {
+ prefetchTree = new PrefetchTreeNode();
+ }
+ EJBQLPath fetchJoin = (EJBQLPath) join.getChild(0);
+ addPath(fetchJoin);
+
+ PrefetchTreeNode node = prefetchTree.addPath(fetchJoin.getRelativePath());
+ node.setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
+ node.setPhantom(false);
+ node.setEjbqlPathEntityId(fetchJoin.getChild(0).getText());
+ }
+
}
class FromItemVisitor extends EJBQLBaseVisitor {
Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLInnerFetchJoin.java
URL: http://svn.apache.org/viewvc/cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLInnerFetchJoin.java?rev=902416&r1=902415&r2=902416&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLInnerFetchJoin.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLInnerFetchJoin.java Sat Jan 23 15:12:25 2010
@@ -33,4 +33,15 @@
protected boolean visitNode(EJBQLExpressionVisitor visitor) {
return visitor.visitInnerFetchJoin(this);
}
+
+ @Override
+ public String getRightHandSideId() {
+ int len = getChildrenCount();
+ if (len < 1) {
+ return null;
+ }
+
+ EJBQLPath path = (EJBQLPath) getChild(0);
+ return path.getAbsolutePath();
+ }
}
Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLOuterFetchJoin.java
URL: http://svn.apache.org/viewvc/cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLOuterFetchJoin.java?rev=902416&r1=902415&r2=902416&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLOuterFetchJoin.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/EJBQLOuterFetchJoin.java Sat Jan 23 15:12:25 2010
@@ -33,4 +33,15 @@
protected boolean visitNode(EJBQLExpressionVisitor visitor) {
return visitor.visitOuterFetchJoin(this);
}
+
+ @Override
+ public String getRightHandSideId() {
+ int len = getChildrenCount();
+ if (len < 1) {
+ return null;
+ }
+
+ EJBQLPath path = (EJBQLPath) getChild(0);
+ return path.getAbsolutePath();
+ }
}
Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/DefaultEntityResultSegment.java
URL: http://svn.apache.org/viewvc/cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/DefaultEntityResultSegment.java?rev=902416&r1=902415&r2=902416&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/DefaultEntityResultSegment.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/DefaultEntityResultSegment.java Sat Jan 23 15:12:25 2010
@@ -18,6 +18,7 @@
****************************************************************/
package org.apache.cayenne.map;
+import java.util.HashMap;
import java.util.Map;
import org.apache.cayenne.query.EntityResultSegment;
Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/EntityResult.java
URL: http://svn.apache.org/viewvc/cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/EntityResult.java?rev=902416&r1=902415&r2=902416&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/EntityResult.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/EntityResult.java Sat Jan 23 15:12:25 2010
@@ -58,12 +58,11 @@
}
Map<String, String> dbFields = new HashMap<String, String>();
- for (FieldResult field : fields) {
- if (field.isDbAttribute()) {
+ for (FieldResult field : fields) {
+ if(field.isDbAttribute()||field.getAttributeName().startsWith("fetch.")){
dbFields.put(field.getAttributeName(), field.getColumn());
- }
- else {
+ } else {
ObjEntity entity = field.getEntityName() != null ? resolver
.getObjEntity(field.getEntityName()) : getRootEntity(resolver);
Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/EJBQLQuery.java
URL: http://svn.apache.org/viewvc/cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/EJBQLQuery.java?rev=902416&r1=902415&r2=902416&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/EJBQLQuery.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/EJBQLQuery.java Sat Jan 23 15:12:25 2010
@@ -120,7 +120,7 @@
throws EJBQLException {
if (expression == null) {
this.expression = EJBQLParserFactory.getParser().compile(
- ejbqlStatement,
+ ejbqlStatement,
resolver);
}
@@ -265,4 +265,6 @@
public int getStatementFetchSize() {
return metadata.getStatementFetchSize();
}
+
+
}
Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/EJBQLQueryMetadata.java
URL: http://svn.apache.org/viewvc/cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/EJBQLQueryMetadata.java?rev=902416&r1=902415&r2=902416&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/EJBQLQueryMetadata.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/EJBQLQueryMetadata.java Sat Jan 23 15:12:25 2010
@@ -36,7 +36,7 @@
boolean resolve(EntityResolver resolver, EJBQLQuery query) {
EJBQLCompiledExpression expression = query.getExpression(resolver);
-
+ setPrefetchTree(expression.getPrefetchTree());
resultSetMapping = expression.getResult() != null ? expression
.getResult()
.getResolvedComponents(resolver) : null;
Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/PrefetchTreeNode.java
URL: http://svn.apache.org/viewvc/cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/PrefetchTreeNode.java?rev=902416&r1=902415&r2=902416&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/PrefetchTreeNode.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/PrefetchTreeNode.java Sat Jan 23 15:12:25 2010
@@ -45,6 +45,8 @@
protected String name;
protected boolean phantom;
protected int semantics;
+ protected String ejbqlPathEntityId;
+ protected String entityName;
// transient parent allows cloning parts of the tree via serialization
protected transient PrefetchTreeNode parent;
@@ -343,6 +345,23 @@
public boolean isDisjointPrefetch() {
return semantics == DISJOINT_PREFETCH_SEMANTICS;
}
+
+ public String getEjbqlPathEntityId() {
+ return ejbqlPathEntityId;
+ }
+
+ public void setEjbqlPathEntityId(String ejbqlPathEntityId) {
+ this.ejbqlPathEntityId = ejbqlPathEntityId;
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public void setEntityName(String entityName) {
+ this.entityName = entityName;
+ }
+
// **** custom serialization that supports serializing subtrees...
Added: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextEJBQLFetchJoinTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextEJBQLFetchJoinTest.java?rev=902416&view=auto
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextEJBQLFetchJoinTest.java (added)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextEJBQLFetchJoinTest.java Sat Jan 23 15:12:25 2010
@@ -0,0 +1,465 @@
+/*****************************************************************
+ * 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 java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.art.Artist;
+import org.apache.art.ArtistExhibit;
+import org.apache.art.Gallery;
+import org.apache.art.Painting;
+import org.apache.cayenne.PersistenceState;
+import org.apache.cayenne.ValueHolder;
+import org.apache.cayenne.query.EJBQLQuery;
+import org.apache.cayenne.unit.CayenneCase;
+
+public class DataContextEJBQLFetchJoinTest extends CayenneCase {
+ public static final String INSERT_ARTIST = "INSERT INTO ARTIST (ARTIST_ID, ARTIST_NAME) VALUES (?,?)";
+ public static final String INSERT_PAINTING = "INSERT INTO PAINTING (PAINTING_ID, PAINTING_TITLE, ARTIST_ID, ESTIMATED_PRICE) VALUES (?, ?, ?, ?)";
+ public static final String INSERT_GALLERY = "INSERT INTO GALLERY (GALLERY_ID, GALLERY_NAME) VALUES (?,?)";
+ public static final String INSERT_EXIBIT = "INSERT INTO EXHIBIT (EXHIBIT_ID, GALLERY_ID, CLOSING_DATE, OPENING_DATE) VALUES (?, ?, ?, ?)";
+ public static final String INSERT_ARTIST_EXIBIT = "INSERT INTO ARTIST_EXHIBIT (ARTIST_ID, EXHIBIT_ID) VALUES (?, ?)";
+
+ Connection conn;
+ @Override
+ protected void setUp() throws Exception {
+ deleteTestData();
+ }
+
+ private void populateTables(TestData data) throws Exception {
+
+ Object[][] artistsData = {
+ {1, "A1"},
+ {2, "A2"},
+ {3, "A3"}
+ };
+ Object[][] paintingsData = {
+ {1, "P11", 1, new BigDecimal(3000d)},
+ {2, "P2", 2, new BigDecimal(5000d)},
+ {3, "P12", 1, new BigDecimal(3000d)}
+ };
+ Object[][] galleriesData = {
+ {1, "gallery1"},
+ {2, "gallery2"}
+ };
+ Object[][] exibitsData = {
+ {1, 1, new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 1), new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 2)},
+ {2, 1, new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 3), new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 4)}
+ };
+ Object[][] artistExibitsData = {
+ {1, 1},
+ {1, 2}
+ };
+ conn = getConnection();
+ try {
+ conn.setAutoCommit(false);
+
+ switch (data) {
+ case ONE_ENTITY_FETCH_JOIN:
+ insertArtists(artistsData);
+ insertPaintings(paintingsData);
+ break;
+ case SEVERAL_FETCH_JOINS:
+ insertArtists(artistsData);
+ insertPaintings(paintingsData);
+ insertGalleries(galleriesData);
+ insertExibits(exibitsData);
+ insertArtistExibits(artistExibitsData);
+ break;
+ }
+
+ }
+ finally {
+ conn.close();
+ }
+
+ }
+
+
+ public void testFetchJoinForOneEntity() throws Exception {
+ populateTables(TestData.ONE_ENTITY_FETCH_JOIN);
+ String ejbql = "SELECT a FROM Artist a JOIN FETCH a.paintingArray ";
+
+ EJBQLQuery query = new EJBQLQuery(ejbql);
+
+ DataContext context = createDataContext();
+
+ List objects = context.performQuery(query);
+
+ blockQueries();
+ try {
+ assertEquals(2, objects.size());
+
+ Iterator it = objects.iterator();
+ while (it.hasNext()) {
+ Artist a = (Artist) it.next();
+ List list = a.getPaintingArray();
+
+ assertNotNull(list);
+ assertFalse(((ValueHolder) list).isFault());
+
+ Iterator children = list.iterator();
+ while (children.hasNext()) {
+ Painting p = (Painting) children.next();
+ assertEquals(PersistenceState.COMMITTED, p.getPersistenceState());
+ // make sure properties are not null..
+ assertNotNull(p.getPaintingTitle());
+ }
+ }
+ }
+ finally {
+ unblockQueries();
+ }
+ }
+
+ public void testSeveralFetchJoins() throws Exception {
+ populateTables(TestData.SEVERAL_FETCH_JOINS);
+ String ejbql = "SELECT a "
+ + "FROM Artist a JOIN FETCH a.paintingArray JOIN FETCH a.artistExhibitArray "
+ + "WHERE a.artistName = 'A1'";
+
+ EJBQLQuery query = new EJBQLQuery(ejbql);
+
+ DataContext context = createDataContext();
+
+ List objects = context.performQuery(query);
+
+ blockQueries();
+ try {
+ assertEquals(1, objects.size());
+
+ Artist a = (Artist) objects.get(0);
+ assertEquals("A1", a.getArtistName());
+
+ List<Painting> paintings = a.getPaintingArray();
+
+ assertNotNull(paintings);
+ assertFalse(((ValueHolder) paintings).isFault());
+ assertEquals(2, paintings.size());
+
+ List<String> expectedPaintingsNames = new ArrayList<String>();
+ expectedPaintingsNames.add("P11");
+ expectedPaintingsNames.add("P12");
+
+ Iterator<Painting> paintingsIterator = paintings.iterator();
+ while (paintingsIterator.hasNext()) {
+ Painting p = paintingsIterator.next();
+ assertEquals(PersistenceState.COMMITTED, p.getPersistenceState());
+ assertNotNull(p.getPaintingTitle());
+ assertTrue(expectedPaintingsNames.contains(p.getPaintingTitle()));
+ }
+
+ List<ArtistExhibit> exibits = a.getArtistExhibitArray();
+
+ assertNotNull(exibits);
+ assertFalse(((ValueHolder) exibits).isFault());
+ assertEquals(2, exibits.size());
+
+ Iterator<ArtistExhibit> exibitsIterator = exibits.iterator();
+ while (exibitsIterator.hasNext()) {
+ ArtistExhibit ae = exibitsIterator.next();
+ assertEquals(PersistenceState.COMMITTED, ae.getPersistenceState());
+ assertNotNull(ae.getObjectId());
+
+ }
+
+ }
+ finally {
+ unblockQueries();
+ }
+ }
+
+ public void testSeveralEntitiesFetchJoins() throws Exception {
+ populateTables(TestData.SEVERAL_FETCH_JOINS);
+ String ejbql = "SELECT DISTINCT a , g "
+ + "FROM Artist a JOIN FETCH a.paintingArray , Gallery g JOIN FETCH g.exhibitArray "
+ + "WHERE a.artistName='A1' AND g.galleryName='gallery1'";
+
+ EJBQLQuery query = new EJBQLQuery(ejbql);
+
+ DataContext context = createDataContext();
+
+ List objects = context.performQuery(query);
+
+ blockQueries();
+ try {
+ assertNotNull(objects);
+ assertFalse(objects.isEmpty());
+ assertEquals(1, objects.size());
+ }
+ finally {
+ unblockQueries();
+ }
+ }
+
+ public void testSeveralEntitiesAndScalarFetchInnerJoins() throws Exception {
+ populateTables(TestData.SEVERAL_FETCH_JOINS);
+ String ejbql = "SELECT DISTINCT a, a.artistName , g "
+ + "FROM Artist a JOIN FETCH a.paintingArray, Gallery g JOIN FETCH g.exhibitArray "
+ + "ORDER BY a.artistName";
+
+ EJBQLQuery query = new EJBQLQuery(ejbql);
+
+ DataContext context = createDataContext();
+
+ List objects = context.performQuery(query);
+
+ blockQueries();
+ try {
+ assertEquals(2, objects.size());
+
+ Object[] firstRow = (Object[]) objects.get(0);
+ Artist a = (Artist) firstRow[0];
+ assertEquals("A1", a.getArtistName());
+
+ List<Painting> paintings = a.getPaintingArray();
+
+ assertNotNull(paintings);
+ assertFalse(((ValueHolder) paintings).isFault());
+ assertEquals(2, paintings.size());
+
+ List<String> expectedPaintingsNames = new ArrayList<String>();
+ expectedPaintingsNames.add("P11");
+ expectedPaintingsNames.add("P12");
+
+ Iterator<Painting> paintingsIterator = paintings.iterator();
+ while (paintingsIterator.hasNext()) {
+ Painting p = paintingsIterator.next();
+ assertEquals(PersistenceState.COMMITTED, p.getPersistenceState());
+ assertNotNull(p.getPaintingTitle());
+ assertTrue(expectedPaintingsNames.contains(p.getPaintingTitle()));
+ }
+ String artistName = (String) firstRow[1];
+ assertEquals("A1", artistName);
+
+ Gallery g1 = (Gallery) firstRow[2];
+ assertEquals("gallery1", g1.getGalleryName());
+
+ List exibits = g1.getExhibitArray();
+
+ assertNotNull(exibits);
+ assertFalse(((ValueHolder) exibits).isFault());
+ assertEquals(2, exibits.size());
+
+ Object[] secondRow = (Object[]) objects.get(1);
+ a = (Artist) secondRow[0];
+ assertEquals("A2", a.getArtistName());
+
+ paintings = a.getPaintingArray();
+
+ assertNotNull(paintings);
+ assertFalse(((ValueHolder) paintings).isFault());
+ assertEquals(1, paintings.size());
+
+ expectedPaintingsNames = new ArrayList<String>();
+ expectedPaintingsNames.add("P2");
+
+ paintingsIterator = paintings.iterator();
+ while (paintingsIterator.hasNext()) {
+ Painting p = paintingsIterator.next();
+ assertEquals(PersistenceState.COMMITTED, p.getPersistenceState());
+ assertNotNull(p.getPaintingTitle());
+ assertTrue(expectedPaintingsNames.contains(p.getPaintingTitle()));
+ }
+ artistName = (String) secondRow[1];
+ assertEquals("A2", artistName);
+
+ Gallery g2 = (Gallery) secondRow[2];
+ assertEquals(g1, g2);
+ }
+ finally {
+ unblockQueries();
+ }
+ }
+
+ public void testSeveralEntitiesAndScalarFetchOuterJoins() throws Exception {
+ populateTables(TestData.SEVERAL_FETCH_JOINS);
+ String ejbql = "SELECT DISTINCT a, a.artistName , g "
+ + "FROM Artist a LEFT JOIN FETCH a.paintingArray, Gallery g LEFT JOIN FETCH g.exhibitArray "
+ + "ORDER BY a.artistName, g.galleryName";
+
+ EJBQLQuery query = new EJBQLQuery(ejbql);
+
+ DataContext context = createDataContext();
+
+ List objects = context.performQuery(query);
+
+ blockQueries();
+ try {
+ assertEquals(6, objects.size());
+
+ Object[] row = (Object[]) objects.get(0);
+ Artist a1 = (Artist) row[0];
+ assertEquals("A1", a1.getArtistName());
+
+ List<Painting> paintings = a1.getPaintingArray();
+
+ assertNotNull(paintings);
+ assertFalse(((ValueHolder) paintings).isFault());
+ assertEquals(2, paintings.size());
+
+ List<String> expectedPaintingsNames = new ArrayList<String>();
+ expectedPaintingsNames.add("P11");
+ expectedPaintingsNames.add("P12");
+
+ Iterator<Painting> paintingsIterator = paintings.iterator();
+ while (paintingsIterator.hasNext()) {
+ Painting p = paintingsIterator.next();
+ assertEquals(PersistenceState.COMMITTED, p.getPersistenceState());
+ assertNotNull(p.getPaintingTitle());
+ assertTrue(expectedPaintingsNames.contains(p.getPaintingTitle()));
+ }
+ String artistName1 = (String) row[1];
+ assertEquals("A1", artistName1);
+
+ Gallery g1 = (Gallery) row[2];
+ assertEquals("gallery1", g1.getGalleryName());
+
+ List exibits = g1.getExhibitArray();
+
+ assertNotNull(exibits);
+ assertFalse(((ValueHolder) exibits).isFault());
+ assertEquals(2, exibits.size());
+
+ row = (Object[]) objects.get(1);
+
+ assertEquals(a1, (Artist) row[0]);
+ assertEquals(artistName1, row[1]);
+
+ Gallery g2 = (Gallery) row[2];
+ assertEquals("gallery2", g2.getGalleryName());
+
+ exibits = g2.getExhibitArray();
+
+ assertTrue(exibits.isEmpty());
+
+ row = (Object[]) objects.get(2);
+
+ Artist a2 = (Artist) row[0];
+ assertEquals("A2", a2.getArtistName());
+
+ paintings = a2.getPaintingArray();
+
+ assertNotNull(paintings);
+ assertEquals(1, paintings.size());
+
+ Painting p = paintings.get(0);
+ assertEquals(PersistenceState.COMMITTED, p.getPersistenceState());
+ assertNotNull(p.getPaintingTitle());
+ assertEquals("P2", p.getPaintingTitle());
+
+ String artistName2 = (String) row[1];
+ assertEquals("A2", artistName2);
+ assertEquals(g1, row[2]);
+
+ row = (Object[]) objects.get(3);
+
+ assertEquals(a2, (Artist) row[0]);
+ assertEquals(artistName2, row[1]);
+ assertEquals(g2, row[2]);
+
+ row = (Object[]) objects.get(4);
+
+ Artist a3 = (Artist) row[0];
+ assertEquals("A3", a3.getArtistName());
+
+ paintings = a3.getPaintingArray();
+
+ assertTrue(paintings.isEmpty());
+
+ String artistName3 = (String) row[1];
+ assertEquals("A3", artistName3);
+ assertEquals(g1, row[2]);
+
+ row = (Object[]) objects.get(5);
+
+ assertEquals(a3, (Artist) row[0]);
+ assertEquals(artistName3, row[1]);
+ assertEquals(g2, row[2]);
+ }
+ finally {
+ unblockQueries();
+ }
+ }
+
+ private void insertArtists(Object[][] artistsData) throws SQLException {
+ PreparedStatement stmt = conn.prepareStatement(INSERT_ARTIST);
+ for (int i = 0; i < artistsData.length; i++) {
+ stmt.setInt(1, (Integer)artistsData[i][0]);
+ stmt.setString(2, (String) artistsData[i][1]);
+ stmt.executeUpdate();
+ }
+ stmt.close();
+ }
+
+ private void insertPaintings(Object[][] paintingsData) throws SQLException {
+ PreparedStatement stmt = conn.prepareStatement(INSERT_PAINTING);
+ for (int i = 0; i < paintingsData.length; i++) {
+ stmt.setInt(1, (Integer)paintingsData[i][0]);
+ stmt.setString(2, (String) paintingsData[i][1]);
+ stmt.setInt(3, (Integer)paintingsData[i][2]);
+ stmt.setBigDecimal(4, (BigDecimal) paintingsData[i][3]);
+ stmt.executeUpdate();
+ }
+ stmt.close();
+ }
+
+ private void insertGalleries(Object[][] galleriesData) throws SQLException {
+ PreparedStatement stmt = conn.prepareStatement(INSERT_GALLERY);
+ for (int i = 0; i < galleriesData.length; i++) {
+ stmt.setInt(1, (Integer)galleriesData[i][0]);
+ stmt.setString(2, (String) galleriesData[i][1]);
+ stmt.executeUpdate();
+ }
+ stmt.close();
+ }
+
+ private void insertExibits(Object[][] exibitsData) throws SQLException {
+ PreparedStatement stmt = conn.prepareStatement(INSERT_EXIBIT);
+ for (int i = 0; i < exibitsData.length; i++) {
+ stmt.setInt(1, (Integer)exibitsData[i][0]);
+ stmt.setInt(2, (Integer) exibitsData[i][1]);
+ stmt.setDate(3, (Date)exibitsData[i][2]);
+ stmt.setDate(4, (Date)exibitsData[i][3]);
+ stmt.executeUpdate();
+ }
+ stmt.close();
+ }
+
+ private void insertArtistExibits(Object[][] artistExibitsData) throws SQLException {
+ PreparedStatement stmt = conn.prepareStatement(INSERT_ARTIST_EXIBIT);
+ for (int i = 0; i < artistExibitsData.length; i++) {
+ stmt.setInt(1, (Integer)artistExibitsData[i][0]);
+ stmt.setInt(2, (Integer) artistExibitsData[i][1]);
+ stmt.executeUpdate();
+ }
+ stmt.close();
+ }
+
+ enum TestData{
+ ONE_ENTITY_FETCH_JOIN, SEVERAL_FETCH_JOINS
+ }
+}