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 19:46:48 UTC
svn commit: r902920 - 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/jdbc/
framework/cayenn...
Author: aadamchik
Date: Mon Jan 25 18:46:47 2010
New Revision: 902920
URL: http://svn.apache.org/viewvc?rev=902920&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)
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/DataRowStore.java
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/jdbc/EJBQLAggregateColumnTranslator.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLGroupByTranslator.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/Compiler.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextEJBQLGroupByHavingTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/resources/dml/access.DataContextEJBQLGroupByHavingTest.xml
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=902920&r1=902919&r2=902920&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 Jan 25 18:46:47 2010
@@ -57,6 +57,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
CAY-1371 EJBQL query doesn't preserve grouping by brackets
Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataRowStore.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataRowStore.java?rev=902920&r1=902919&r2=902920&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataRowStore.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataRowStore.java Mon Jan 25 18:46:47 2010
@@ -209,6 +209,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/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=902920&r1=902919&r2=902920&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 Jan 25 18:46:47 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/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLAggregateColumnTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLAggregateColumnTranslator.java?rev=902920&r1=902919&r2=902920&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLAggregateColumnTranslator.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLAggregateColumnTranslator.java Mon Jan 25 18:46:47 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/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLGroupByTranslator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLGroupByTranslator.java?rev=902920&r1=902919&r2=902920&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLGroupByTranslator.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLGroupByTranslator.java Mon Jan 25 18:46:47 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/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/Compiler.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/Compiler.java?rev=902920&r1=902919&r2=902920&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/Compiler.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/ejbql/parser/Compiler.java Mon Jan 25 18:46:47 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/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextEJBQLGroupByHavingTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextEJBQLGroupByHavingTest.java?rev=902920&r1=902919&r2=902920&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextEJBQLGroupByHavingTest.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextEJBQLGroupByHavingTest.java Mon Jan 25 18:46:47 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/trunk/framework/cayenne-jdk1.5-unpublished/src/test/resources/dml/access.DataContextEJBQLGroupByHavingTest.xml
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/resources/dml/access.DataContextEJBQLGroupByHavingTest.xml?rev=902920&r1=902919&r2=902920&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/resources/dml/access.DataContextEJBQLGroupByHavingTest.xml (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/resources/dml/access.DataContextEJBQLGroupByHavingTest.xml Mon Jan 25 18:46:47 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