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/25 20:00:19 UTC

svn commit: r902926 - 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: Mon Jan 25 19:00:19 2010
New Revision: 902926

URL: http://svn.apache.org/viewvc?rev=902926&view=rev
Log:
CAY-1367 EJBQL: Implement support for relationship-ending paths in GROUP BY clause

patch by Ksenia Khailenko
(I changed the code cloning result arrays as this may cause preformance degradation on the large lists)

(cherry picked from commit d30557a96231579b717ca6d757692c1963fe0c41)

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/DataRowStore.java
    cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ObjectResolver.java
    cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLAggregateColumnTranslator.java
    cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLGroupByTranslator.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/test/java/org/apache/cayenne/access/DataContextEJBQLGroupByHavingTest.java
    cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/test/resources/dml/access.DataContextEJBQLGroupByHavingTest.xml

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=902926&r1=902925&r2=902926&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 Mon Jan 25 19:00:19 2010
@@ -31,6 +31,7 @@
 CAY-1355 Warning if rename Embeddable.
 CAY-1361 SelectQuery.aliasPathSplits does nothing
 CAY-1365 "= NULL" being used instead of "IS NULL" on an EJBQL expression..
+CAY-1367 EJBQL: Implement support for relationship-ending paths in GROUP BY clause
 CAY-1368 Left Join and Prefetches do not work together
 CAY-1369 EJBQL: Fix likeIgnoreCase issues
 

Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataRowStore.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/DataRowStore.java?rev=902926&r1=902925&r2=902926&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataRowStore.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataRowStore.java Mon Jan 25 19:00:19 2010
@@ -233,6 +233,11 @@
         synchronized (this) {
             for (int i = 0; i < size; i++) {
                 Persistent object = (Persistent) objects.get(i);
+                
+                // skip null objects... possible since 3.0 in some EJBQL results
+                if (object == null) {
+                    continue;
+                }
 
                 // skip HOLLOW objects as they likely were created from partial snapshots
                 if (object.getPersistenceState() == PersistenceState.HOLLOW) {

Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ObjectResolver.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/ObjectResolver.java?rev=902926&r1=902925&r2=902926&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ObjectResolver.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ObjectResolver.java Mon Jan 25 19:00:19 2010
@@ -108,21 +108,13 @@
         }
 
         List<Persistent> results = new ArrayList<Persistent>(rows.size());
-
         for (DataRow row : rows) {
-
-            Persistent object = objectFromDataRow(row);
-
-            if (object == null) {
-                throw new CayenneRuntimeException("Can't build Object from row: " + row);
-            }
-
-            results.add(object);
+            // nulls are possible here since 3.0 for soem varieties of EJBQL
+            results.add(objectFromDataRow(row));
         }
 
         // now deal with snapshots
         cache.snapshotsUpdatedForObjects(results, rows, refreshObjects);
-
         return results;
     }
 

Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLAggregateColumnTranslator.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/EJBQLAggregateColumnTranslator.java?rev=902926&r1=902925&r2=902926&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLAggregateColumnTranslator.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLAggregateColumnTranslator.java Mon Jan 25 19:00:19 2010
@@ -18,13 +18,21 @@
  ****************************************************************/
 package org.apache.cayenne.access.jdbc;
 
+import java.util.Collection;
+import java.util.Iterator;
+
 import org.apache.cayenne.ejbql.EJBQLBaseVisitor;
 import org.apache.cayenne.ejbql.EJBQLException;
 import org.apache.cayenne.ejbql.EJBQLExpression;
 import org.apache.cayenne.ejbql.EJBQLExpressionVisitor;
 import org.apache.cayenne.ejbql.parser.EJBQLAggregateColumn;
+import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
+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.reflect.ClassDescriptor;
 
 /**
  * @since 3.0
@@ -116,13 +124,45 @@
         protected void processTerminatingAttribute(ObjAttribute attribute) {
 
             EJBQLAggregateColumnTranslator.this.attributeType = attribute.getType();
-
+            
             DbEntity table = currentEntity.getDbEntity();
             String alias = this.lastAlias != null ? lastAlias : context.getTableAlias(
                     idPath,
                     table.getFullyQualifiedName());
             context.append(alias).append('.').append(attribute.getDbAttributeName());
         }
+        @Override
+        public boolean visitIdentificationVariable(EJBQLExpression expression) {
+
+            String idVariableAbsolutePath = idPath+"."+expression.getText();
+            ClassDescriptor descriptor = context.getEntityDescriptor(idVariableAbsolutePath);
+            if (descriptor != null) {
+                this.lastAlias = context.getTableAlias(idVariableAbsolutePath, descriptor.getEntity().getDbEntityName());
+            }
+
+            this.lastPathComponent = expression.getText();
+            
+            return true;
+        }
+        
+        @Override
+        protected void processTerminatingRelationship(ObjRelationship relationship) {
+
+            Collection<DbAttribute> dbAttr = ((ObjEntity) relationship
+                    .getTargetEntity()).getDbEntity().getAttributes();
+
+            DbRelationship dbRelationship = relationship.getDbRelationships().get(0);
+            DbEntity table = (DbEntity) dbRelationship.getTargetEntity();
+
+            if (dbAttr.size() > 0) {
+                this.resolveJoin(false);
+            }
+
+            String alias = this.lastAlias != null ? lastAlias : context
+                    .getTableAlias(idPath, table.getFullyQualifiedName());
+
+            context.append(alias).append(".*");
+        }
     }
 
     class CountColumnVisitor extends EJBQLBaseVisitor {

Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLGroupByTranslator.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/EJBQLGroupByTranslator.java?rev=902926&r1=902925&r2=902926&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLGroupByTranslator.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLGroupByTranslator.java Mon Jan 25 19:00:19 2010
@@ -18,10 +18,19 @@
  ****************************************************************/
 package org.apache.cayenne.access.jdbc;
 
+import java.util.Collection;
+import java.util.Iterator;
+
 import org.apache.cayenne.ejbql.EJBQLBaseVisitor;
 import org.apache.cayenne.ejbql.EJBQLException;
 import org.apache.cayenne.ejbql.EJBQLExpression;
 import org.apache.cayenne.ejbql.EJBQLExpressionVisitor;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.ObjRelationship;
+import org.apache.cayenne.reflect.ClassDescriptor;
 
 /**
  * @since 3.0
@@ -60,8 +69,54 @@
                 throw new EJBQLException(
                         "Can't GROUP BY on multi-column paths or objects");
             }
+            
+            @Override
+            public boolean visitIdentificationVariable(EJBQLExpression expression) {
+
+                String idVariableAbsolutePath = idPath+"."+expression.getText();
+                ClassDescriptor descriptor = context.getEntityDescriptor(idVariableAbsolutePath);
+                if (descriptor != null) {
+                    this.lastAlias = context.getTableAlias(idVariableAbsolutePath, descriptor.getEntity().getDbEntityName());
+                }
+
+                this.lastPathComponent = expression.getText();
+                
+                return true;
+            }
+            
+            
+            @Override
+            protected void processTerminatingRelationship(ObjRelationship relationship) {
+
+                Collection<DbAttribute> dbAttr = ((ObjEntity) relationship
+                        .getTargetEntity()).getDbEntity().getAttributes();
+
+                DbRelationship dbRelationship = relationship.getDbRelationships().get(0);
+                DbEntity table = (DbEntity) dbRelationship.getTargetEntity();
+
+                Iterator<DbAttribute> it = dbAttr.iterator();
+                
+
+                String alias = this.lastAlias != null ? lastAlias : context
+                        .getTableAlias(idPath, table.getFullyQualifiedName());
+
+                boolean first = true;
+                while (it.hasNext()) {
+
+                    context.append(!first ? ", " : " ");
+
+                    DbAttribute dbAttribute = it.next();
+                    context.append(alias).append('.').append(dbAttribute.getName());
+
+                    first = false;
+                }
+
+            }
+            
         };
+       
         expression.visit(childVisitor);
+        
         return false;
     }
 }

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=902926&r1=902925&r2=902926&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 Mon Jan 25 19:00:19 2010
@@ -606,9 +606,10 @@
         @Override
         public boolean visitAggregate(EJBQLExpression expression) {
             addResultSetColumn();
+            expression.getChild(0).getChild(0).visit(pathVisitor);
             return false;
         }
-
+        
         @Override
         public boolean visitPath(EJBQLExpression expression, int finishedChildIndex) {
             addPath((EJBQLPath) expression);

Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextEJBQLGroupByHavingTest.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/DataContextEJBQLGroupByHavingTest.java?rev=902926&r1=902925&r2=902926&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextEJBQLGroupByHavingTest.java (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextEJBQLGroupByHavingTest.java Mon Jan 25 19:00:19 2010
@@ -19,9 +19,14 @@
 package org.apache.cayenne.access;
 
 import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.art.Artist;
+import org.apache.art.Gallery;
 import org.apache.cayenne.query.EJBQLQuery;
 import org.apache.cayenne.unit.CayenneCase;
 
@@ -171,4 +176,64 @@
         assertEquals(new BigDecimal(1d), row0[0], 0.001d);
         assertEquals(new Long(3l), row0[1]);
     }
+    
+    public void testGroupByJoinedRelatedEntities() throws Exception {
+        createTestData("testGroupByRelatedEntity");
+        EJBQLQuery query = new EJBQLQuery(
+                "SELECT COUNT(p), p.toArtist FROM Painting p GROUP BY p.toArtist ");
+        List<Object[]> data = createDataContext().performQuery(query);
+        assertNotNull(data);
+        assertEquals(2, data.size());
+        
+        List<String> expectedArtists=new ArrayList<String>();
+        expectedArtists.add("AA1");
+        expectedArtists.add("AA2");
+        
+        Object[]row = data.get(0);
+        String artistName = ((Artist)row[1]).getArtistName();
+        assertEquals(1L, row[0]);
+        assertTrue("error artistName:"+artistName, expectedArtists.contains(artistName));
+        
+        row = data.get(1);
+        artistName = ((Artist)row[1]).getArtistName();
+        assertEquals(1L, row[0]);
+        assertTrue("error artistName:"+artistName, expectedArtists.contains(artistName));
+    }
+
+    public void testGroupByJoinedEntities() throws Exception {
+        createTestData("testGroupByEntities");
+        EJBQLQuery query = new EJBQLQuery(
+                "SELECT COUNT(p), p.toArtist, p.toGallery FROM Painting p " +
+                "GROUP BY p.toGallery, p.toArtist ");
+        List<Object[]> data = createDataContext().performQuery(query);
+        assertNotNull(data);
+        assertEquals(5, data.size());
+        
+        HashSet<List> expectedResults=new HashSet<List>();
+        expectedResults.add(Arrays.asList(3L, null,null));
+        expectedResults.add(Arrays.asList(1L, "AA1",null));
+        expectedResults.add(Arrays.asList(1L, "AA2",null));
+        expectedResults.add(Arrays.asList(1L, "AA2","gallery1"));
+        expectedResults.add(Arrays.asList(1L, "AA1","gallery2"));
+        
+        for(Object[] row:data){
+            assertFalse(expectedResults.add(Arrays.asList(
+                    row[0], 
+                    row[1]==null?null:((Artist)row[1]).getArtistName(),
+                    row[2]==null?null:((Gallery)row[2]).getGalleryName())));
+        }
+    }
+
+    public void testGroupByJoinedEntityInCount() throws Exception {
+        createTestData("testGroupByEntities");
+        EJBQLQuery query = new EJBQLQuery(
+                "SELECT COUNT(p.toArtist) FROM Painting p GROUP BY p.toArtist");
+        List<Long> data = createDataContext().performQuery(query);
+        assertNotNull(data);
+        assertEquals(3, data.size());
+        for(Long result:data){
+            assertTrue(result==3L||result==2L);
+        }
+    }
+    
 }

Modified: cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/test/resources/dml/access.DataContextEJBQLGroupByHavingTest.xml
URL: http://svn.apache.org/viewvc/cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/test/resources/dml/access.DataContextEJBQLGroupByHavingTest.xml?rev=902926&r1=902925&r2=902926&view=diff
==============================================================================
--- cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/test/resources/dml/access.DataContextEJBQLGroupByHavingTest.xml (original)
+++ cayenne/main/branches/STABLE-3.0/framework/cayenne-jdk1.5-unpublished/src/test/resources/dml/access.DataContextEJBQLGroupByHavingTest.xml Mon Jan 25 19:00:19 2010
@@ -83,6 +83,44 @@
 		</value></constructor-arg>
 	</bean>
 
+
+	<bean id="P111" class="org.apache.cayenne.unit.util.UpdatingSQLTemplate">
+		<constructor-arg type="java.lang.Class">
+			<value>org.apache.art.Painting</value>
+		</constructor-arg>
+		<constructor-arg>
+			<value>
+				INSERT INTO PAINTING (PAINTING_ID, GALLERY_ID, PAINTING_TITLE, ARTIST_ID, ESTIMATED_PRICE)
+				VALUES (33009, 33001, 'P111', 33002, 5000)
+			</value>
+		</constructor-arg>
+	</bean>
+	
+	<bean id="P112" class="org.apache.cayenne.unit.util.UpdatingSQLTemplate">
+		<constructor-arg type="java.lang.Class">
+			<value>org.apache.art.Painting</value>
+		</constructor-arg>
+		<constructor-arg>
+			<value>
+				INSERT INTO PAINTING (PAINTING_ID, GALLERY_ID, PAINTING_TITLE, ARTIST_ID, ESTIMATED_PRICE)
+				VALUES (33010, 33002, 'P112', 33001, 5000)
+			</value>
+		</constructor-arg>
+	</bean>
+	
+	<bean id="G1" class="org.apache.cayenne.unit.util.UpdatingSQLTemplate">
+		<constructor-arg type="java.lang.Class"><value>org.apache.art.Gallery</value></constructor-arg>
+		<constructor-arg><value>
+		INSERT INTO GALLERY (GALLERY_ID, GALLERY_NAME) VALUES (33001, 'gallery1')
+		</value></constructor-arg>
+	</bean>
+	
+	<bean id="G2" class="org.apache.cayenne.unit.util.UpdatingSQLTemplate">
+		<constructor-arg type="java.lang.Class"><value>org.apache.art.Gallery</value></constructor-arg>
+		<constructor-arg><value>
+		INSERT INTO GALLERY (GALLERY_ID, GALLERY_NAME) VALUES (33002, 'gallery2')
+		</value></constructor-arg>
+	</bean>
 	<!-- ======================================= -->
 	<!-- Data Sets -->
 	<!-- ======================================= -->	
@@ -111,4 +149,24 @@
 			</list>
 		</constructor-arg>
 	</bean>
+	
+	<bean id="testGroupByEntities" class="java.util.ArrayList">
+		<constructor-arg>
+			<list>
+				<ref bean="A1"/>
+				<ref bean="A2"/>
+				<ref bean="A3"/>
+				<ref bean="A4"/>
+				<ref bean="P1"/>
+				<ref bean="P2"/>
+				<ref bean="P3"/>
+				<ref bean="P11"/>
+				<ref bean="P12"/>
+				<ref bean="G1"/>
+				<ref bean="G2"/>
+				<ref bean="P111"/>
+				<ref bean="P112"/>
+			</list>
+		</constructor-arg>
+	</bean>
 </beans>
\ No newline at end of file