You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2017/01/25 14:19:31 UTC
cayenne git commit: CAY-2209 shortcuts for aggregate functions
Repository: cayenne
Updated Branches:
refs/heads/master 6f76f057a -> 3f8a8f196
CAY-2209 shortcuts for aggregate functions
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/3f8a8f19
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/3f8a8f19
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/3f8a8f19
Branch: refs/heads/master
Commit: 3f8a8f196ed2aca1a07470f33ae51d66251bda38
Parents: 6f76f05
Author: Nikita Timofeev <st...@gmail.com>
Authored: Wed Jan 25 17:17:24 2017 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Wed Jan 25 17:17:24 2017 +0300
----------------------------------------------------------------------
.../java/org/apache/cayenne/exp/Property.java | 22 +++
.../org/apache/cayenne/query/ColumnSelect.java | 125 +++++++++--------
.../org/apache/cayenne/query/FluentSelect.java | 2 +-
.../org/apache/cayenne/query/ObjectSelect.java | 123 +++++++++++++++--
.../cayenne/access/DataContextBinaryPKIT.java | 4 +-
.../apache/cayenne/query/ColumnSelectIT.java | 137 +++++++++++++------
.../apache/cayenne/query/ColumnSelectTest.java | 79 +++++++++--
7 files changed, 364 insertions(+), 128 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cayenne/blob/3f8a8f19/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
index 3ec518a..f1bb1ce 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
@@ -50,6 +50,8 @@ import java.util.List;
*/
public class Property<E> {
+ public static final Property<Long> COUNT = Property.create(FunctionExpressionFactory.countExp(), Long.class);
+
/**
* Name of the property in the object
*/
@@ -633,6 +635,26 @@ public class Property<E> {
}
}
+ public Property<Long> count() {
+ return create(FunctionExpressionFactory.countExp(getExpression()), Long.class);
+ }
+
+ public Property<E> max() {
+ return create(FunctionExpressionFactory.maxExp(getExpression()), getType());
+ }
+
+ public Property<E> min() {
+ return create(FunctionExpressionFactory.minExp(getExpression()), getType());
+ }
+
+ public Property<E> avg() {
+ return create(FunctionExpressionFactory.avgExp(getExpression()), getType());
+ }
+
+ public Property<E> sum() {
+ return create(FunctionExpressionFactory.sumExp(getExpression()), getType());
+ }
+
public Property<E> alias(String alias) {
return new Property<>(alias, this.getExpression(), this.getType());
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/3f8a8f19/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelect.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelect.java b/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelect.java
index 4058c28..c3cfee0 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelect.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelect.java
@@ -29,60 +29,40 @@ import org.apache.cayenne.exp.Property;
import org.apache.cayenne.map.EntityResolver;
/**
- * <p>A selecting query providing individual properties based on the root object.</p>
+ * <p>A helper builder for queries selecting individual properties based on the root object.</p>
* <p>
- * It can be properties of the object itself, properties of related entities
+ * It can be used to select properties of the object itself, properties of related entities
* or some function calls (including aggregate functions).
- * </p><p>
+ * </p>
+ * <p>
* Usage examples: <pre>
+ * {@code
* // select list of names:
- * List<String> names = ColumnSelect.query(Artist.class, Artist.ARTIST_NAME).select(context);
+ * List<String> names = ObjectSelect.columnQuery(Artist.class, Artist.ARTIST_NAME).select(context);
*
* // select count:
- * Property<Long> countProperty = Property.create(FunctionExpressionFactory.countExp(), Long.class);
- * long count = ColumnSelect.query(Artist.class, countProperty).selectOne();
+ * long count = ObjectSelect.columnQuery(Artist.class, Property.COUNT).selectOne();
*
* // select only required properties of an entity:
- * List<Object[]> data = ColumnSelect.query(Artist.class, Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH)
+ * List<Object[]> data = ObjectSelect.columnQuery(Artist.class, Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH)
* .where(Artist.ARTIST_NAME.like("Picasso%))
* .select(context);
- * </pre></p>
+ * }
+ * </pre>
+ * </p>
+ * <p><b>Note: this class can't be instantiated directly. Use {@link ObjectSelect}.</b></p>
+ * @see ObjectSelect#columnQuery(Class, Property)
+ *
* @since 4.0
*/
public class ColumnSelect<T> extends FluentSelect<T, ColumnSelect<T>> {
private Collection<Property<?>> columns;
private boolean havingExpressionIsActive = false;
- private boolean singleColumn = true;
+ // package private for tests
+ boolean singleColumn = true;
private Expression having;
- /**
- *
- * @param entityType base persistent class that will be used as a root for this query
- */
- public static <T> ColumnSelect<T> query(Class<T> entityType) {
- return new ColumnSelect<T>().entityType(entityType);
- }
-
- /**
- *
- * @param entityType base persistent class that will be used as a root for this query
- * @param column single column to select
- */
- public static <E> ColumnSelect<E> query(Class<?> entityType, Property<E> column) {
- return new ColumnSelect<>().entityType(entityType).column(column);
- }
-
- /**
- *
- * @param entityType base persistent class that will be used as a root for this query
- * @param firstColumn column to select
- * @param otherColumns columns to select
- */
- public static ColumnSelect<Object[]> query(Class<?> entityType, Property<?> firstColumn, Property<?>... otherColumns) {
- return new ColumnSelect<Object[]>().entityType(entityType).columns(firstColumn, otherColumns);
- }
-
protected ColumnSelect() {
super();
}
@@ -116,14 +96,16 @@ public class ColumnSelect<T> extends FluentSelect<T, ColumnSelect<T>> {
}
/**
- * <p>Select only specific properties.</p>
+ * <p>Add properties to select.</p>
* <p>Can be any properties that can be resolved against root entity type
* (root entity properties, function call expressions, properties of relationships, etc).</p>
* <p>
* <pre>
- * List<Object[]> columns = ColumnSelect.query(Artist.class)
- * .columns(Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH)
+ * {@code
+ * List<Object[]> columns = ObjectSelect.columnQuery(Artist.class, Artist.ARTIST_NAME)
+ * .columns(Artist.ARTIST_SALARY, Artist.DATE_OF_BIRTH)
* .select(context);
+ * }
* </pre>
*
* @param firstProperty first property
@@ -143,7 +125,7 @@ public class ColumnSelect<T> extends FluentSelect<T, ColumnSelect<T>> {
}
/**
- * <p>Select only specific properties.</p>
+ * <p>Add properties to select.</p>
* <p>Can be any properties that can be resolved against root entity type
* (root entity properties, function call expressions, properties of relationships, etc).</p>
* <p>
@@ -168,21 +150,6 @@ public class ColumnSelect<T> extends FluentSelect<T, ColumnSelect<T>> {
return (ColumnSelect<Object[]>)this;
}
- /**
- * <p>Select one specific property.</p>
- * <p>Can be any property that can be resolved against root entity type
- * (root entity property, function call expression, property of relationships, etc)</p>
- * <p>If you need several columns use {@link ColumnSelect#columns(Property, Property[])} method as subsequent
- * call to this method will override previous columns set via this or
- * {@link ColumnSelect#columns(Property, Property[])} method.</p>
- * <p>
- * <pre>
- * List<String> names = ColumnSelect.query(Artist.class, Artist.ARTIST_NAME).select(context);
- * </pre>
- *
- * @param property single property to select
- * @see ColumnSelect#columns(Property, Property[])
- */
@SuppressWarnings("unchecked")
protected <E> ColumnSelect<E> column(Property<E> property) {
if (this.columns == null) {
@@ -195,6 +162,54 @@ public class ColumnSelect<T> extends FluentSelect<T, ColumnSelect<T>> {
}
/**
+ * <p>Shortcut for {@link #columns(Property, Property[])} columns}(Property.COUNT)</p>
+ */
+ public ColumnSelect<Object[]> count() {
+ return columns(Property.COUNT);
+ }
+
+ /**
+ * <p>Select COUNT(property)</p>
+ * <p>Can return different result than COUNT(*) as it will count only non null values</p>
+ * @see ColumnSelect#count()
+ */
+ public ColumnSelect<Object[]> count(Property<?> property) {
+ return columns(property.count());
+ }
+
+ /**
+ * <p>Select minimum value of property</p>
+ * @see ColumnSelect#columns(Property, Property[])
+ */
+ public ColumnSelect<Object[]> min(Property<?> property) {
+ return columns(property.min());
+ }
+
+ /**
+ * <p>Select maximum value of property</p>
+ * @see ColumnSelect#columns(Property, Property[])
+ */
+ public ColumnSelect<Object[]> max(Property<?> property) {
+ return columns(property.max());
+ }
+
+ /**
+ * <p>Select average value of property</p>
+ * @see ColumnSelect#columns(Property, Property[])
+ */
+ public ColumnSelect<Object[]> avg(Property<?> property) {
+ return columns(property.avg());
+ }
+
+ /**
+ * <p>Select sum of values</p>
+ * @see ColumnSelect#columns(Property, Property[])
+ */
+ public <E extends Number> ColumnSelect<Object[]> sum(Property<E> property) {
+ return columns(property.sum());
+ }
+
+ /**
* Appends a having qualifier expression of this query. An equivalent to
* {@link #and(Expression...)} that can be used a syntactic sugar.
*
http://git-wip-us.apache.org/repos/asf/cayenne/blob/3f8a8f19/cayenne-server/src/main/java/org/apache/cayenne/query/FluentSelect.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/FluentSelect.java b/cayenne-server/src/main/java/org/apache/cayenne/query/FluentSelect.java
index de772bc..1ceeb17 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/FluentSelect.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/FluentSelect.java
@@ -38,7 +38,7 @@ import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.map.ObjEntity;
/**
- * Base class for ObjectSelect and ColumnSelect
+ * Base class for {@link ObjectSelect} and {@link ColumnSelect}
*
* @since 4.0
*/
http://git-wip-us.apache.org/repos/asf/cayenne/blob/3f8a8f19/cayenne-server/src/main/java/org/apache/cayenne/query/ObjectSelect.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/ObjectSelect.java b/cayenne-server/src/main/java/org/apache/cayenne/query/ObjectSelect.java
index f4cbd0c..262f18c 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/ObjectSelect.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/ObjectSelect.java
@@ -19,6 +19,7 @@
package org.apache.cayenne.query;
import org.apache.cayenne.DataRow;
+import org.apache.cayenne.ObjectContext;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.Property;
import org.apache.cayenne.map.DbEntity;
@@ -34,9 +35,9 @@ import java.util.List;
* <pre>
* {@code
* Artist a = ObjectSelect
- * .query(Artist.class)
- * .where(Artist.NAME.eq("Picasso"))
- * .selectOne(context);
+ * .query(Artist.class)
+ * .where(Artist.NAME.eq("Picasso"))
+ * .selectOne(context);
* }
* </pre>
*
@@ -117,6 +118,28 @@ public class ObjectSelect<T> extends FluentSelect<T, ObjectSelect<T>> {
return new ObjectSelect<DataRow>().fetchDataRows().dbEntityName(dbEntityName).where(expression);
}
+ /**
+ * Creates a ColumnSelect that will fetch single property that can be resolved
+ * against a given {@link ObjEntity} class.
+ *
+ * @param entityType base persistent class that will be used as a root for this query
+ * @param column single column to select
+ */
+ protected static <E> ColumnSelect<E> columnQuery(Class<?> entityType, Property<E> column) {
+ return new ColumnSelect<>().entityType(entityType).column(column);
+ }
+
+ /**
+ * Creates a ColumnSelect that will fetch multiple columns of a given {@link ObjEntity}
+ *
+ * @param entityType base persistent class that will be used as a root for this query
+ * @param firstColumn column to select
+ * @param otherColumns columns to select
+ */
+ protected static ColumnSelect<Object[]> columnQuery(Class<?> entityType, Property<?> firstColumn, Property<?>... otherColumns) {
+ return new ColumnSelect<Object[]>().entityType(entityType).columns(firstColumn, otherColumns);
+ }
+
protected ObjectSelect() {
}
@@ -146,18 +169,19 @@ public class ObjectSelect<T> extends FluentSelect<T, ObjectSelect<T>> {
/**
* <p>Select only specific properties.</p>
* <p>Can be any properties that can be resolved against root entity type
- * (root entity properties, function call expressions, properties of relationships, etc).</p>
+ * (root entity's properties, function call expressions, properties of relationships, etc).</p>
* <p>
* <pre>
- * List<Object[]> columns = ColumnSelect.query(Artist.class)
+ * {@code
+ * List<Object[]> columns = ObjectSelect.query(Artist.class)
* .columns(Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH)
* .select(context);
+ * }
* </pre>
*
* @param properties array of properties to select
- * @see ColumnSelect#column(Property)
+ * @see ObjectSelect#column(Property)
*/
- @SuppressWarnings("unchecked")
public ColumnSelect<Object[]> columns(Property<?> firstProperty, Property<?>... properties) {
return new ColumnSelect<>(this).columns(firstProperty, properties);
}
@@ -165,23 +189,92 @@ public class ObjectSelect<T> extends FluentSelect<T, ObjectSelect<T>> {
/**
* <p>Select one specific property.</p>
* <p>Can be any property that can be resolved against root entity type
- * (root entity property, function call expression, property of relationships, etc)</p>
- * <p>If you need several columns use {@link ColumnSelect#columns(Property, Property[])} method as subsequent
- * call to this method will override previous columns set via this or
- * {@link ColumnSelect#columns(Property, Property[])} method.</p>
+ * (root entity's property, function call expression, property of relationships, etc)</p>
+ * <p>If you need several columns use {@link ObjectSelect#columns(Property, Property[])} method.</p>
* <p>
* <pre>
- * List<String> names = ObjectSelect.query(Artist.class).column(Artist.ARTIST_NAME).select(context);
+ * {@code
+ * List<String> names = ObjectSelect.query(Artist.class)
+ * .column(Artist.ARTIST_NAME)
+ * .select(context);
+ * }
* </pre>
- *
+ * </p>
* @param property single property to select
- * @see ColumnSelect#columns(Property, Property[])
+ * @see ObjectSelect#columns(Property, Property[])
*/
- @SuppressWarnings("unchecked")
public <E> ColumnSelect<E> column(Property<E> property) {
return new ColumnSelect<>(this).column(property);
}
+ /**
+ * Select COUNT(*)
+ * @see ObjectSelect#column(Property)
+ */
+ public ColumnSelect<Long> count() {
+ return column(Property.COUNT);
+ }
+
+ /**
+ * <p>Select COUNT(property)</p>
+ * <p>Can return different result than COUNT(*) as it will count only non null values</p>
+ * @see ObjectSelect#count()
+ * @see ObjectSelect#column(Property)
+ */
+ public ColumnSelect<Long> count(Property<?> property) {
+ return column(property.count());
+ }
+
+ /**
+ * <p>Select minimum value of property</p>
+ * @see ObjectSelect#column(Property)
+ */
+ public <E> ColumnSelect<E> min(Property<E> property) {
+ return column(property.min());
+ }
+
+ /**
+ * <p>Select maximum value of property</p>
+ * @see ObjectSelect#column(Property)
+ */
+ public <E> ColumnSelect<E> max(Property<E> property) {
+ return column(property.max());
+ }
+
+ /**
+ * <p>Select average value of property</p>
+ * @see ObjectSelect#column(Property)
+ */
+ public <E> ColumnSelect<E> avg(Property<E> property) {
+ return column(property.avg());
+ }
+
+ /**
+ * <p>Select sum of values</p>
+ * @see ObjectSelect#column(Property)
+ */
+ public <E extends Number> ColumnSelect<E> sum(Property<E> property) {
+ return column(property.sum());
+ }
+
+ /**
+ * <p>Quick way to select count of records</p>
+ * <p>Usage:
+ * <pre>
+ * {@code
+ * long count = ObjectSelect.query(Artist.class)
+ * .where(Artist.ARTIST_NAME.like("a%"))
+ * .selectCount(context);
+ * }
+ * </pre>
+ * </p>
+ * @param context to perform query
+ * @return count of rows
+ */
+ public long selectCount(ObjectContext context) {
+ return count().selectOne(context);
+ }
+
public boolean isFetchingDataRows() {
return fetchingDataRows;
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/3f8a8f19/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextBinaryPKIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextBinaryPKIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextBinaryPKIT.java
index 42e24e8..32b3b85 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextBinaryPKIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextBinaryPKIT.java
@@ -76,9 +76,7 @@ public class DataContextBinaryPKIT extends ServerCase {
context.commitChanges();
context.invalidateObjects(master, detail);
- BinaryPKTest2 fetchedDetail = (BinaryPKTest2) context1.performQuery(
- new SelectQuery(BinaryPKTest2.class)).get(0);
-
+ BinaryPKTest2 fetchedDetail = new SelectQuery<>(BinaryPKTest2.class).select(context1).get(0);
assertNotNull(fetchedDetail.readPropertyDirectly("toBinaryPKMaster"));
BinaryPKTest1 fetchedMaster = fetchedDetail.getToBinaryPKMaster();
http://git-wip-us.apache.org/repos/asf/cayenne/blob/3f8a8f19/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java
index 0e2f404..613b659 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java
@@ -19,6 +19,8 @@
package org.apache.cayenne.query;
+import java.math.BigDecimal;
+import java.math.BigInteger;
import java.sql.Types;
import java.text.DateFormat;
import java.util.Locale;
@@ -30,6 +32,7 @@ import org.apache.cayenne.exp.Property;
import org.apache.cayenne.test.jdbc.DBHelper;
import org.apache.cayenne.test.jdbc.TableHelper;
import org.apache.cayenne.testdo.testmap.Artist;
+import org.apache.cayenne.testdo.testmap.Painting;
import org.apache.cayenne.unit.PostgresUnitDbAdapter;
import org.apache.cayenne.unit.UnitDbAdapter;
import org.apache.cayenne.unit.di.server.CayenneProjects;
@@ -39,7 +42,6 @@ import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
-import static org.apache.cayenne.exp.FunctionExpressionFactory.countExp;
import static org.apache.cayenne.exp.FunctionExpressionFactory.substringExp;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
@@ -60,12 +62,13 @@ public class ColumnSelectIT extends ServerCase {
private UnitDbAdapter unitDbAdapter;
// Format: d/m/YY
- private DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US);
+ private static final DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US);
+ private TableHelper tArtist;
@Before
public void createArtistsDataSet() throws Exception {
- TableHelper tArtist = new TableHelper(dbHelper, "ARTIST");
+ tArtist = new TableHelper(dbHelper, "ARTIST");
tArtist.setColumns("ARTIST_ID", "ARTIST_NAME", "DATE_OF_BIRTH");
tArtist.setColumnTypes(Types.INTEGER, Types.VARCHAR, Types.DATE);
@@ -82,19 +85,17 @@ public class ColumnSelectIT extends ServerCase {
tGallery.insert(1, "tate modern");
TableHelper tPaintings = new TableHelper(dbHelper, "PAINTING");
- tPaintings.setColumns("PAINTING_ID", "PAINTING_TITLE", "ARTIST_ID", "GALLERY_ID");
+ tPaintings.setColumns("PAINTING_ID", "PAINTING_TITLE", "ARTIST_ID", "GALLERY_ID", "ESTIMATED_PRICE");
for (int i = 1; i <= 20; i++) {
- tPaintings.insert(i, "painting" + i, i % 5 + 1, 1);
+ tPaintings.insert(i, "painting" + i, i % 5 + 1, 1, 22 - i);
}
- tPaintings.insert(21, "painting21", 2, 1);
+ tPaintings.insert(21, "painting21", 2, 1, 30);
}
@Test
public void testSelectGroupBy() throws Exception {
- Property<Long> count = Property.create(countExp(), Long.class);
-
- Object[] result = ColumnSelect.query(Artist.class)
- .columns(Artist.DATE_OF_BIRTH, count)
+ Object[] result = ObjectSelect.query(Artist.class)
+ .columns(Artist.DATE_OF_BIRTH, Property.COUNT)
.orderBy(Artist.DATE_OF_BIRTH.asc())
.selectFirst(context);
@@ -104,10 +105,8 @@ public class ColumnSelectIT extends ServerCase {
@Test
public void testSelectSimpleHaving() throws Exception {
- Property<Long> count = Property.create(countExp(), Long.class);
-
- Object[] result = ColumnSelect.query(Artist.class)
- .columns(Artist.DATE_OF_BIRTH, count)
+ Object[] result = ObjectSelect.query(Artist.class)
+ .columns(Artist.DATE_OF_BIRTH, Property.COUNT)
.orderBy(Artist.DATE_OF_BIRTH.asc())
.having(Artist.DATE_OF_BIRTH.eq(dateFormat.parse("1/2/17")))
.selectOne(context);
@@ -119,9 +118,8 @@ public class ColumnSelectIT extends ServerCase {
@Test(expected = Exception.class)
public void testHavingOnNonGroupByColumn() throws Exception {
Property<String> nameSubstr = Property.create(substringExp(Artist.ARTIST_NAME.path(), 1, 6), String.class);
- Property<Long> count = Property.create(countExp(), Long.class);
- Object[] q = ColumnSelect.query(Artist.class, nameSubstr, count)
+ Object[] q = ObjectSelect.columnQuery(Artist.class, nameSubstr, Property.COUNT)
.having(Artist.ARTIST_NAME.like("artist%"))
.selectOne(context);
assertEquals("artist", q[0]);
@@ -130,10 +128,8 @@ public class ColumnSelectIT extends ServerCase {
@Test
public void testSelectRelationshipCount() throws Exception {
- Property<Long> paintingCount = Property.create(countExp(Artist.PAINTING_ARRAY.path()), Long.class);
-
- Object[] result = ColumnSelect.query(Artist.class)
- .columns(Artist.DATE_OF_BIRTH, paintingCount)
+ Object[] result = ObjectSelect.query(Artist.class)
+ .columns(Artist.DATE_OF_BIRTH, Artist.PAINTING_ARRAY.count())
.orderBy(Artist.DATE_OF_BIRTH.asc())
.selectFirst(context);
assertEquals(dateFormat.parse("1/1/17"), result[0]);
@@ -144,12 +140,10 @@ public class ColumnSelectIT extends ServerCase {
public void testSelectHavingWithExpressionAlias() throws Exception {
Property<String> nameSubstr = Property.create("name_substr", substringExp(Artist.ARTIST_NAME.path(), 1, 6), String.class);
- Property<Long> count = Property.create(countExp(), Long.class);
-
Object[] q = null;
try {
- q = ColumnSelect.query(Artist.class, nameSubstr, count)
- .having(count.gt(10L))
+ q = ObjectSelect.columnQuery(Artist.class, nameSubstr, Property.COUNT)
+ .having(Property.COUNT.gt(10L))
.selectOne(context);
} catch (CayenneRuntimeException ex) {
if(unitDbAdapter.supportsExpressionInHaving()) {
@@ -167,12 +161,10 @@ public class ColumnSelectIT extends ServerCase {
public void testSelectHavingWithExpressionNoAlias() throws Exception {
Property<String> nameSubstr = Property.create(substringExp(Artist.ARTIST_NAME.path(), 1, 6), String.class);
- Property<Long> count = Property.create(countExp(), Long.class);
-
Object[] q = null;
try {
- q = ColumnSelect.query(Artist.class, nameSubstr, count)
- .having(count.gt(10L))
+ q = ObjectSelect.columnQuery(Artist.class, nameSubstr, Property.COUNT)
+ .having(Property.COUNT.gt(10L))
.selectOne(context);
} catch (CayenneRuntimeException ex) {
if(unitDbAdapter.supportsExpressionInHaving()) {
@@ -189,13 +181,12 @@ public class ColumnSelectIT extends ServerCase {
public void testSelectWhereAndHaving() throws Exception {
Property<String> nameFirstLetter = Property.create(substringExp(Artist.ARTIST_NAME.path(), 1, 1), String.class);
Property<String> nameSubstr = Property.create("name_substr", substringExp(Artist.ARTIST_NAME.path(), 1, 6), String.class);
- Property<Long> count = Property.create(countExp(), Long.class);
Object[] q = null;
try {
- q = ColumnSelect.query(Artist.class, nameSubstr, count)
+ q = ObjectSelect.columnQuery(Artist.class, nameSubstr, Property.COUNT)
.where(nameFirstLetter.eq("a"))
- .having(count.gt(10L))
+ .having(Property.COUNT.gt(10L))
.selectOne(context);
} catch (CayenneRuntimeException ex) {
if(unitDbAdapter.supportsExpressionInHaving()) {
@@ -210,11 +201,11 @@ public class ColumnSelectIT extends ServerCase {
@Test
public void testSelectRelationshipCountHaving() throws Exception {
- Property<Long> paintingCount = Property.create(countExp(Artist.PAINTING_ARRAY.path()), Long.class);
+ Property<Long> paintingCount = Artist.PAINTING_ARRAY.count();
Object[] result = null;
try {
- result = ColumnSelect.query(Artist.class)
+ result = ObjectSelect.query(Artist.class)
.columns(Artist.ARTIST_NAME, paintingCount)
.having(paintingCount.gt(4L))
.selectOne(context);
@@ -236,12 +227,12 @@ public class ColumnSelectIT extends ServerCase {
return;
}
- Property<Long> paintingCount = Property.create(countExp(Artist.PAINTING_ARRAY.path()), Long.class);
+ Property<Long> paintingCount = Artist.PAINTING_ARRAY.count();
context.getEntityResolver().getDataMap("testmap").setQuotingSQLIdentifiers(true);
Object[] result = null;
try {
- result = ColumnSelect.query(Artist.class)
+ result = ObjectSelect.query(Artist.class)
.columns(Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH, paintingCount)
.having(paintingCount.gt(4L))
.selectOne(context);
@@ -265,12 +256,10 @@ public class ColumnSelectIT extends ServerCase {
return;
}
- Property<Long> count = Property.create(countExp(), Long.class);
context.getEntityResolver().getDataMap("testmap").setQuotingSQLIdentifiers(true);
-
try {
- Object[] result = ColumnSelect.query(Artist.class)
- .columns(Artist.DATE_OF_BIRTH, count)
+ Object[] result = ObjectSelect.query(Artist.class)
+ .columns(Artist.DATE_OF_BIRTH, Property.COUNT)
.orderBy(Artist.DATE_OF_BIRTH.asc())
.selectFirst(context);
@@ -280,4 +269,74 @@ public class ColumnSelectIT extends ServerCase {
context.getEntityResolver().getDataMap("testmap").setQuotingSQLIdentifiers(false);
}
}
+
+ @Test
+ public void testAgregateOnRelation() throws Exception {
+ BigDecimal min = new BigDecimal(3);
+ BigDecimal max = new BigDecimal(30);
+ BigDecimal avg = new BigDecimal(BigInteger.valueOf(1290L), 2);
+ BigDecimal sum = new BigDecimal(258);
+
+ Property<BigDecimal> estimatedPrice = Artist.PAINTING_ARRAY.dot(Painting.ESTIMATED_PRICE);
+ Object[] minMaxAvgPrice = ObjectSelect.query(Artist.class)
+ .where(estimatedPrice.gte(min))
+ .min(estimatedPrice).max(estimatedPrice)
+ .avg(estimatedPrice)
+ .sum(estimatedPrice)
+ .count()
+ .selectOne(context);
+
+ assertEquals(0, min.compareTo((BigDecimal)minMaxAvgPrice[0]));
+ assertEquals(0, max.compareTo((BigDecimal)minMaxAvgPrice[1]));
+ assertEquals(0, avg.compareTo((BigDecimal)minMaxAvgPrice[2]));
+ assertEquals(0, sum.compareTo((BigDecimal)minMaxAvgPrice[3]));
+ assertEquals(20L, minMaxAvgPrice[4]);
+ }
+
+ @Test
+ public void testQueryCount() throws Exception {
+ long count = ObjectSelect
+ .columnQuery(Artist.class, Property.COUNT)
+ .selectOne(context);
+
+ assertEquals(20, count);
+
+ long count2 = ObjectSelect
+ .query(Artist.class)
+ .count()
+ .selectOne(context);
+
+ assertEquals(count, count2);
+
+ long count3 = ObjectSelect
+ .query(Artist.class)
+ .selectCount(context);
+
+ assertEquals(count, count3);
+ }
+
+ @Test
+ public void testQueryCountWithProperty() throws Exception {
+ tArtist.insert(21, "artist_21", null);
+ tArtist.insert(22, "artist_21", null);
+
+ long count = ObjectSelect
+ .columnQuery(Artist.class, Property.COUNT)
+ .selectOne(context);
+ assertEquals(22, count);
+
+ // COUNT(attribute) should return count of non null values of attribute
+ long count2 = ObjectSelect
+ .columnQuery(Artist.class, Artist.DATE_OF_BIRTH.count())
+ .selectOne(context);
+ assertEquals(20, count2);
+
+ long count3 = ObjectSelect
+ .query(Artist.class)
+ .count(Artist.DATE_OF_BIRTH)
+ .selectOne(context);
+ assertEquals(count2, count3);
+
+
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/3f8a8f19/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectTest.java b/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectTest.java
index 9d4d057..188455f 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectTest.java
@@ -19,6 +19,7 @@
package org.apache.cayenne.query;
+import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@@ -27,6 +28,7 @@ import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
import org.apache.cayenne.exp.Property;
import org.apache.cayenne.testdo.testmap.Artist;
+import org.apache.cayenne.testdo.testmap.Painting;
import org.junit.Test;
import static org.junit.Assert.*;
@@ -38,34 +40,81 @@ public class ColumnSelectTest {
@Test
public void query() throws Exception {
- ColumnSelect<Artist> q = ColumnSelect.query(Artist.class);
+ ColumnSelect<Artist> q = new ColumnSelect<>();
assertNull(q.getColumns());
assertNull(q.getHaving());
+ assertNull(q.getWhere());
+ }
+
+ @Test
+ public void queryWithOneColumn() throws Exception {
+ ColumnSelect<String> q = ObjectSelect.columnQuery(Artist.class, Artist.ARTIST_NAME);
+ assertEquals(Collections.singletonList(Artist.ARTIST_NAME), q.getColumns());
+ assertTrue(q.singleColumn);
+ assertNull(q.getHaving());
+ assertNull(q.getWhere());
+ }
+
+ @Test
+ public void queryWithOneColumn2() throws Exception {
+ ColumnSelect<String> q = ObjectSelect.query(Artist.class).column(Artist.ARTIST_NAME);
+ assertEquals(Collections.singletonList(Artist.ARTIST_NAME), q.getColumns());
+ assertTrue(q.singleColumn);
+ assertNull(q.getHaving());
+ assertNull(q.getWhere());
}
@Test
- public void queryWithColumn() throws Exception {
- ColumnSelect<String> q = ColumnSelect.query(Artist.class, Artist.ARTIST_NAME);
- assertEquals(Arrays.asList(Artist.ARTIST_NAME), q.getColumns());
+ public void queryWithOneColumn3() throws Exception {
+ ColumnSelect<Object[]> q = ObjectSelect.query(Artist.class).columns(Artist.ARTIST_NAME);
+ assertEquals(Collections.singletonList(Artist.ARTIST_NAME), q.getColumns());
+ assertFalse(q.singleColumn);
assertNull(q.getHaving());
+ assertNull(q.getWhere());
}
@Test
- public void queryWithColumns() throws Exception {
- ColumnSelect<Object[]> q = ColumnSelect.query(Artist.class, Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH);
+ public void queryWithMultipleColumns() throws Exception {
+ ColumnSelect<Object[]> q = ObjectSelect.columnQuery(Artist.class, Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH);
assertEquals(Arrays.asList(Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH), q.getColumns());
+ assertFalse(q.singleColumn);
+ assertNull(q.getHaving());
+ assertNull(q.getWhere());
+ }
+
+ @Test
+ public void queryCount() throws Exception {
+ ColumnSelect<Long> q = ObjectSelect.query(Artist.class).count();
+ assertEquals(Collections.singletonList(Property.COUNT), q.getColumns());
+ assertNull(q.getHaving());
+ assertNull(q.getWhere());
+ }
+
+ @Test
+ public void queryCountWithProperty() throws Exception {
+ ColumnSelect<Long> q = ObjectSelect.query(Artist.class).count(Artist.ARTIST_NAME);
+ assertEquals(Collections.singletonList(Artist.ARTIST_NAME.count()), q.getColumns());
assertNull(q.getHaving());
+ assertNull(q.getWhere());
+ }
+
+ @Test
+ public void queryMinWithProperty() throws Exception {
+ ColumnSelect<BigDecimal> q = ObjectSelect.query(Artist.class).min(Artist.PAINTING_ARRAY.dot(Painting.ESTIMATED_PRICE));
+ assertEquals(Collections.singletonList(Artist.PAINTING_ARRAY.dot(Painting.ESTIMATED_PRICE).min()), q.getColumns());
+ assertNull(q.getHaving());
+ assertNull(q.getWhere());
}
@SuppressWarnings("unchecked")
@Test
public void columns() throws Exception {
- ColumnSelect q = ColumnSelect.query(Artist.class);
+ ColumnSelect q = new ColumnSelect();
assertNull(q.getColumns());
q.columns(Artist.ARTIST_NAME, Artist.PAINTING_ARRAY);
assertEquals(Arrays.asList(Artist.ARTIST_NAME, Artist.PAINTING_ARRAY), q.getColumns());
- q = ColumnSelect.query(Artist.class, Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH);
+ q = ObjectSelect.columnQuery(Artist.class, Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH);
assertEquals(Arrays.asList(Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH), q.getColumns());
q.columns(Artist.PAINTING_ARRAY);
assertEquals(Arrays.asList(Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH, Artist.PAINTING_ARRAY), q.getColumns());
@@ -74,7 +123,7 @@ public class ColumnSelectTest {
@Test
public void havingExpression() throws Exception {
- ColumnSelect q = ColumnSelect.query(Artist.class);
+ ColumnSelect q = new ColumnSelect();
assertNull(q.getHaving());
assertNull(q.getWhere());
@@ -91,7 +140,7 @@ public class ColumnSelectTest {
@Test
public void havingString() throws Exception {
- ColumnSelect q = ColumnSelect.query(Artist.class);
+ ColumnSelect q = new ColumnSelect();
assertNull(q.getHaving());
assertNull(q.getWhere());
@@ -108,7 +157,7 @@ public class ColumnSelectTest {
@Test
public void and() throws Exception {
- ColumnSelect q = ColumnSelect.query(Artist.class);
+ ColumnSelect q = new ColumnSelect();
assertNull(q.getHaving());
assertNull(q.getWhere());
@@ -127,7 +176,7 @@ public class ColumnSelectTest {
@Test
public void or() throws Exception {
- ColumnSelect q = ColumnSelect.query(Artist.class);
+ ColumnSelect q = new ColumnSelect();
assertNull(q.getHaving());
assertNull(q.getWhere());
@@ -147,7 +196,7 @@ public class ColumnSelectTest {
@Test
public void testColumnsAddByOne() {
- ColumnSelect<Artist> q = ColumnSelect.query(Artist.class);
+ ColumnSelect<Artist> q = new ColumnSelect<>();
assertEquals(null, q.getColumns());
@@ -161,7 +210,7 @@ public class ColumnSelectTest {
@Test
public void testColumnsAddAll() {
- ColumnSelect<Artist> q = ColumnSelect.query(Artist.class);
+ ColumnSelect<Artist> q = new ColumnSelect<>();
assertEquals(null, q.getColumns());
@@ -176,7 +225,7 @@ public class ColumnSelectTest {
@Test
public void testColumnAddByOne() {
- ColumnSelect<Artist> q = ColumnSelect.query(Artist.class);
+ ColumnSelect<Artist> q = new ColumnSelect<>();
assertEquals(null, q.getColumns());