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/12 13:41:54 UTC
[1/2] cayenne git commit: CAY-2193 HAVING clause - extracted abstract
class FluentSelect from ObjectSelect - created new class ColumnSelect
(extends FluentSelect)
Repository: cayenne
Updated Branches:
refs/heads/master ba64168d3 -> 4f860180d
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelectTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelectTest.java b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelectTest.java
index 35749c0..94a7292 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelectTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelectTest.java
@@ -33,7 +33,6 @@ import java.util.Collections;
import org.apache.cayenne.DataRow;
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.junit.Test;
@@ -430,52 +429,4 @@ public class ObjectSelectTest {
assertNull(q.getCacheGroups());
}
- @Test
- public void testColumnsAddByOne() {
- ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
-
- assertEquals(null, q.getColumns());
-
- q.columns(Artist.ARTIST_NAME);
- q.columns();
- q.columns(Artist.DATE_OF_BIRTH);
- q.columns();
- q.columns(Artist.PAINTING_ARRAY);
- q.columns();
-
- Collection<Property<?>> properties = Arrays.asList(Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH, Artist.PAINTING_ARRAY);
- assertEquals(properties, q.getColumns());
- }
-
- @Test
- public void testColumnsAddAll() {
- ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
-
- assertEquals(null, q.getColumns());
-
- q.columns(Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH, Artist.PAINTING_ARRAY);
- q.columns(Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH, Artist.PAINTING_ARRAY);
-
- Collection<Property<?>> properties = Arrays.asList(
- Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH, Artist.PAINTING_ARRAY,
- Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH, Artist.PAINTING_ARRAY); // should it be Set instead of List?
- assertEquals(properties, q.getColumns());
- }
-
- @Test
- public void testColumnAddByOne() {
- ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
-
- assertEquals(null, q.getColumns());
-
- q.column(Artist.ARTIST_NAME);
- q.columns();
- q.column(Artist.DATE_OF_BIRTH);
- q.columns();
- q.column(Artist.PAINTING_ARRAY);
- q.columns();
-
- Collection<Property<?>> properties = Collections.<Property<?>>singletonList(Artist.PAINTING_ARRAY);
- assertEquals(properties, q.getColumns());
- }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_AggregateIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_AggregateIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_AggregateIT.java
index 6034d6b..0152470 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_AggregateIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_AggregateIT.java
@@ -21,6 +21,7 @@ package org.apache.cayenne.query;
import java.sql.Types;
import java.text.DateFormat;
+import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
@@ -54,6 +55,8 @@ public class ObjectSelect_AggregateIT extends ServerCase {
@Inject
private DBHelper dbHelper;
+ // Format: d/m/YY
+ DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US);
@Before
public void createArtistsDataSet() throws Exception {
@@ -62,8 +65,6 @@ public class ObjectSelect_AggregateIT extends ServerCase {
tArtist.setColumnTypes(Types.INTEGER, Types.VARCHAR, Types.DATE);
java.sql.Date[] dates = new java.sql.Date[5];
- // Format: d/m/YY
- DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US);
for(int i=1; i<=5; i++) {
dates[i-1] = new java.sql.Date(dateFormat.parse("1/" + i + "/17").getTime());
}
@@ -80,18 +81,15 @@ public class ObjectSelect_AggregateIT extends ServerCase {
for (int i = 1; i <= 20; i++) {
tPaintings.insert(i, "painting" + i, i % 5 + 1, 1);
}
+ tPaintings.insert(21, "painting21", 2, 1);
}
@After
public void clearArtistsDataSet() throws Exception {
- TableHelper tPaintings = new TableHelper(dbHelper, "PAINTING");
- tPaintings.deleteAll();
-
- TableHelper tArtist = new TableHelper(dbHelper, "ARTIST");
- tArtist.deleteAll();
-
- TableHelper tGallery = new TableHelper(dbHelper, "GALLERY");
- tGallery.deleteAll();
+ for(String table : Arrays.asList("PAINTING", "ARTIST", "GALLERY")) {
+ TableHelper tHelper = new TableHelper(dbHelper, table);
+ tHelper.deleteAll();
+ }
}
@Test
@@ -112,7 +110,6 @@ public class ObjectSelect_AggregateIT extends ServerCase {
Date avg = ObjectSelect.query(Artist.class)
.column(avgProp)
.selectOne(context);
- DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US);
Date date = dateFormat.parse("1/3/17");
assertEquals(date, avg);
}
@@ -124,7 +121,6 @@ public class ObjectSelect_AggregateIT extends ServerCase {
Date avg = ObjectSelect.query(Artist.class)
.column(minProp)
.selectOne(context);
- DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US);
Date date = dateFormat.parse("1/1/17");
assertEquals(date, avg);
}
@@ -136,7 +132,6 @@ public class ObjectSelect_AggregateIT extends ServerCase {
Date avg = ObjectSelect.query(Artist.class)
.column(maxProp)
.selectOne(context);
- DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US);
Date date = dateFormat.parse("1/5/17");
assertEquals(date, avg);
}
@@ -149,7 +144,6 @@ public class ObjectSelect_AggregateIT extends ServerCase {
.columns(countProp, Artist.DATE_OF_BIRTH)
.orderBy(Artist.DATE_OF_BIRTH.asc())
.select(context);
- DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US);
Date date = dateFormat.parse("1/2/17");
assertEquals(5L, count.size());
assertEquals(4L, count.get(1)[0]);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_CompileIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_CompileIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_CompileIT.java
index 84a8077..b0aecb0 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_CompileIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_CompileIT.java
@@ -178,9 +178,9 @@ public class ObjectSelect_CompileIT extends ServerCase {
SelectQuery selectQuery1 = (SelectQuery) q.createReplacementQuery(resolver);
assertNull(selectQuery1.getColumns());
- q.columns(Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH);
+ ColumnSelect<Object[]> newQ = q.columns(Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH);
- SelectQuery selectQuery2 = (SelectQuery) q.createReplacementQuery(resolver);
+ SelectQuery selectQuery2 = (SelectQuery) newQ.createReplacementQuery(resolver);
assertNotNull(selectQuery2.getColumns());
Collection<Property<?>> properties = Arrays.<Property<?>>asList(Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_RunIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_RunIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_RunIT.java
index 3881135..ca4a7ce 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_RunIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_RunIT.java
@@ -25,6 +25,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import java.util.Arrays;
import java.util.List;
import org.apache.cayenne.CayenneRuntimeException;
@@ -81,14 +82,10 @@ public class ObjectSelect_RunIT extends ServerCase {
@After
public void clearArtistsDataSet() throws Exception {
- TableHelper tPaintings = new TableHelper(dbHelper, "PAINTING");
- tPaintings.deleteAll();
-
- TableHelper tArtist = new TableHelper(dbHelper, "ARTIST");
- tArtist.deleteAll();
-
- TableHelper tGallery = new TableHelper(dbHelper, "GALLERY");
- tGallery.deleteAll();
+ for(String table : Arrays.asList("PAINTING", "ARTIST", "GALLERY")) {
+ TableHelper tHelper = new TableHelper(dbHelper, table);
+ tHelper.deleteAll();
+ }
}
@Test
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/cayenne-server/src/test/java/org/apache/cayenne/unit/DB2UnitDbAdapter.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/unit/DB2UnitDbAdapter.java b/cayenne-server/src/test/java/org/apache/cayenne/unit/DB2UnitDbAdapter.java
index f4fc4f1..ac46910 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/unit/DB2UnitDbAdapter.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/unit/DB2UnitDbAdapter.java
@@ -63,4 +63,9 @@ public class DB2UnitDbAdapter extends UnitDbAdapter {
public boolean supportsGeneratedKeysAdd() {
return true;
}
+
+ @Override
+ public boolean supportsExpressionInHaving() {
+ return false;
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/cayenne-server/src/test/java/org/apache/cayenne/unit/DerbyUnitDbAdapter.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/unit/DerbyUnitDbAdapter.java b/cayenne-server/src/test/java/org/apache/cayenne/unit/DerbyUnitDbAdapter.java
index bc3f584..c64f1b2 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/unit/DerbyUnitDbAdapter.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/unit/DerbyUnitDbAdapter.java
@@ -50,4 +50,9 @@ public class DerbyUnitDbAdapter extends UnitDbAdapter {
public boolean supportsLobs() {
return true;
}
+
+ @Override
+ public boolean supportsExpressionInHaving() {
+ return false;
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/cayenne-server/src/test/java/org/apache/cayenne/unit/OracleUnitDbAdapter.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/unit/OracleUnitDbAdapter.java b/cayenne-server/src/test/java/org/apache/cayenne/unit/OracleUnitDbAdapter.java
index 9cb93fb..1d9c99b 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/unit/OracleUnitDbAdapter.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/unit/OracleUnitDbAdapter.java
@@ -130,4 +130,9 @@ public class OracleUnitDbAdapter extends UnitDbAdapter {
public boolean onlyGenericNumberType() {
return true;
}
+
+ @Override
+ public boolean supportsExpressionInHaving() {
+ return false;
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/cayenne-server/src/test/java/org/apache/cayenne/unit/SQLServerUnitDbAdapter.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/unit/SQLServerUnitDbAdapter.java b/cayenne-server/src/test/java/org/apache/cayenne/unit/SQLServerUnitDbAdapter.java
index 6c03130..b91f09b 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/unit/SQLServerUnitDbAdapter.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/unit/SQLServerUnitDbAdapter.java
@@ -81,4 +81,9 @@ public class SQLServerUnitDbAdapter extends SybaseUnitDbAdapter {
// ((JdbcAdapter) getAdapter()).setSupportsGeneratedKeys(false);
// }
// }
+
+ @Override
+ public boolean supportsExpressionInHaving() {
+ return false;
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/cayenne-server/src/test/java/org/apache/cayenne/unit/UnitDbAdapter.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/unit/UnitDbAdapter.java b/cayenne-server/src/test/java/org/apache/cayenne/unit/UnitDbAdapter.java
index 1297d74..57a2336 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/unit/UnitDbAdapter.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/unit/UnitDbAdapter.java
@@ -382,4 +382,8 @@ public class UnitDbAdapter {
public boolean supportsEscapeInLike() {
return true;
}
+
+ public boolean supportsExpressionInHaving() {
+ return true;
+ }
}
[2/2] cayenne git commit: CAY-2193 HAVING clause - extracted abstract
class FluentSelect from ObjectSelect - created new class ColumnSelect
(extends FluentSelect)
Posted by nt...@apache.org.
CAY-2193 HAVING clause
- extracted abstract class FluentSelect from ObjectSelect
- created new class ColumnSelect (extends FluentSelect)
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/4f860180
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/4f860180
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/4f860180
Branch: refs/heads/master
Commit: 4f860180dbcea9ea9cf04edd3da1e4166568c97e
Parents: ba64168
Author: Nikita Timofeev <st...@gmail.com>
Authored: Thu Jan 12 15:46:21 2017 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Thu Jan 12 15:46:21 2017 +0300
----------------------------------------------------------------------
.../select/DefaultSelectTranslator.java | 109 +++-
.../translator/select/QualifierTranslator.java | 64 +++
.../translator/select/QueryAssembler.java | 27 +
.../dba/oracle/OracleSelectTranslator.java | 10 +-
.../java/org/apache/cayenne/exp/Property.java | 23 +-
.../org/apache/cayenne/query/ColumnSelect.java | 276 ++++++++++
.../org/apache/cayenne/query/FluentSelect.java | 543 +++++++++++++++++++
.../org/apache/cayenne/query/ObjectSelect.java | 527 +-----------------
.../org/apache/cayenne/query/SelectQuery.java | 39 +-
.../cayenne/query/SelectQueryMetadata.java | 3 +-
.../cayenne/access/ReturnTypesMappingIT.java | 2 +-
.../translator/select/TstQueryAssembler.java | 6 +
.../apache/cayenne/query/ColumnSelectIT.java | 242 +++++++++
.../apache/cayenne/query/ColumnSelectTest.java | 199 +++++++
.../apache/cayenne/query/ObjectSelectTest.java | 49 --
.../cayenne/query/ObjectSelect_AggregateIT.java | 22 +-
.../cayenne/query/ObjectSelect_CompileIT.java | 4 +-
.../cayenne/query/ObjectSelect_RunIT.java | 13 +-
.../apache/cayenne/unit/DB2UnitDbAdapter.java | 5 +
.../apache/cayenne/unit/DerbyUnitDbAdapter.java | 5 +
.../cayenne/unit/OracleUnitDbAdapter.java | 5 +
.../cayenne/unit/SQLServerUnitDbAdapter.java | 5 +
.../org/apache/cayenne/unit/UnitDbAdapter.java | 4 +
23 files changed, 1568 insertions(+), 614 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
index 1ea302d..d21e643 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
@@ -20,6 +20,7 @@ package org.apache.cayenne.access.translator.select;
import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.access.jdbc.ColumnDescriptor;
+import org.apache.cayenne.access.translator.DbAttributeBinding;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dba.QuotingStrategy;
import org.apache.cayenne.dba.TypesMapping;
@@ -56,6 +57,7 @@ import org.apache.cayenne.util.HashCodeBuilder;
import java.sql.Types;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -100,7 +102,7 @@ public class DefaultSelectTranslator extends QueryAssembler implements SelectTra
* Does this SQL have any aggregate function
*/
boolean haveAggregate;
- List<ColumnDescriptor> groupByColumns;
+ Map<ColumnDescriptor, List<DbAttributeBinding>> groupByColumns;
public DefaultSelectTranslator(Query query, DbAdapter adapter, EntityResolver entityResolver) {
@@ -132,7 +134,7 @@ public class DefaultSelectTranslator extends QueryAssembler implements SelectTra
// build qualifier
QualifierTranslator qualifierTranslator = adapter.getQualifierTranslator(this);
- StringBuilder qualifierBuffer = qualifierTranslator.appendPart(new StringBuilder());
+ StringBuilder whereQualifierBuffer = qualifierTranslator.appendPart(new StringBuilder());
// build ORDER BY
OrderingTranslator orderingTranslator = new OrderingTranslator(this);
@@ -187,12 +189,12 @@ public class DefaultSelectTranslator extends QueryAssembler implements SelectTra
joins.appendRootWithQuoteSqlIdentifiers(queryBuf, getQueryMetadata().getDbEntity());
joins.appendJoins(queryBuf);
- joins.appendQualifier(qualifierBuffer, qualifierBuffer.length() == 0);
+ joins.appendQualifier(whereQualifierBuffer, whereQualifierBuffer.length() == 0);
// append qualifier
- if (qualifierBuffer.length() > 0) {
+ if (whereQualifierBuffer.length() > 0) {
queryBuf.append(" WHERE ");
- queryBuf.append(qualifierBuffer);
+ queryBuf.append(whereQualifierBuffer);
}
if(haveAggregate && !groupByColumns.isEmpty()) {
@@ -200,6 +202,18 @@ public class DefaultSelectTranslator extends QueryAssembler implements SelectTra
appendGroupByColumns(queryBuf, groupByColumns);
}
+ // append HAVING qualifier
+ QualifierTranslator havingQualifierTranslator = adapter.getQualifierTranslator(this);
+ Expression havingQualifier = ((SelectQuery)query).getHavingQualifier();
+ if(havingQualifier != null) {
+ havingQualifierTranslator.setQualifier(havingQualifier);
+ StringBuilder havingQualifierBuffer = havingQualifierTranslator.appendPart(new StringBuilder());
+ if(havingQualifierBuffer.length() > 0) {
+ queryBuf.append(" HAVING ");
+ queryBuf.append(havingQualifierBuffer);
+ }
+ }
+
// append prebuilt ordering
if (orderingBuffer.length() > 0) {
queryBuf.append(" ORDER BY ").append(orderingBuffer);
@@ -241,22 +255,35 @@ public class DefaultSelectTranslator extends QueryAssembler implements SelectTra
/**
* Append columns to GROUP BY clause
- * use distinct from appendSelectColumns() method
- * as it has some incompatible overridden versions (i.e. in Oracle translator)
- *
* @since 4.0
*/
- protected void appendGroupByColumns(StringBuilder buffer, List<ColumnDescriptor> groupByColumns) {
- int columnCount = groupByColumns.size();
- buffer.append(groupByColumns.get(0).getName());
-
- for (int i = 1; i < columnCount; i++) {
+ protected void appendGroupByColumns(StringBuilder buffer, Map<ColumnDescriptor, List<DbAttributeBinding>> groupByColumns) {
+ Iterator<Map.Entry<ColumnDescriptor, List<DbAttributeBinding>>> it = groupByColumns.entrySet().iterator();
+ Map.Entry<ColumnDescriptor, List<DbAttributeBinding>> entry = it.next();
+ appendGroupByColumn(buffer, entry);
+ while(it.hasNext()) {
+ entry = it.next();
buffer.append(", ");
- buffer.append(groupByColumns.get(i).getName());
+ appendGroupByColumn(buffer, entry);
}
}
/**
+ * Append single column to GROUP BY clause
+ * @since 4.0
+ */
+ protected void appendGroupByColumn(StringBuilder buffer, Map.Entry<ColumnDescriptor, List<DbAttributeBinding>> entry) {
+ if(entry.getKey().getDataRowKey().equals(entry.getKey().getName())) {
+ buffer.append(entry.getKey().getName());
+ for (DbAttributeBinding binding : entry.getValue()) {
+ addToParamList(binding.getAttribute(), binding.getValue());
+ }
+ } else {
+ buffer.append(entry.getKey().getDataRowKey());
+ }
+ }
+
+ /**
* Handles appending optional limit and offset clauses. This implementation
* does nothing, deferring to subclasses to define the LIMIT/OFFSET clause
* syntax.
@@ -340,25 +367,36 @@ public class DefaultSelectTranslator extends QueryAssembler implements SelectTra
* If query contains explicit column list, use only them
*/
<T> List<ColumnDescriptor> appendOverridedColumns(List<ColumnDescriptor> columns, SelectQuery<T> query) {
+ groupByColumns = new HashMap<>();
+
QualifierTranslator qualifierTranslator = adapter.getQualifierTranslator(this);
- groupByColumns = new ArrayList<>();
+ AccumulatingBindingListener bindingListener = new AccumulatingBindingListener();
+ setAddBindingListener(bindingListener);
+
for(Property<?> property : query.getColumns()) {
- StringBuilder builder = new StringBuilder();
- qualifierTranslator.setOut(builder);
- qualifierTranslator.doAppendPart(property.getExpression());
+ qualifierTranslator.setQualifier(property.getExpression());
+ StringBuilder builder = qualifierTranslator.appendPart(new StringBuilder());
int type = TypesMapping.getSqlTypeByJava(property.getType());
+
+ String alias = property.getAlias();
+ if(alias != null) {
+ builder.append(" AS ").append(alias);
+ }
ColumnDescriptor descriptor = new ColumnDescriptor(builder.toString(), type);
- descriptor.setDataRowKey(property.getName());
+ descriptor.setDataRowKey(alias);
columns.add(descriptor);
if(isAggregate(property)) {
haveAggregate = true;
} else {
- groupByColumns.add(descriptor);
+ groupByColumns.put(descriptor, bindingListener.getBindings());
}
+ bindingListener.reset();
}
+ setAddBindingListener(null);
+
if(!haveAggregate) {
// if no expression with aggregation function found, we don't need this information
groupByColumns.clear();
@@ -678,6 +716,21 @@ public class DefaultSelectTranslator extends QueryAssembler implements SelectTra
return true;
}
+ @Override
+ public String getAliasForExpression(Expression exp) {
+ Collection<Property<?>> columns = ((SelectQuery<?>)query).getColumns();
+ if(columns == null) {
+ return null;
+ }
+ for(Property<?> property : columns) {
+ if(property.getExpression().equals(exp)) {
+ return property.getAlias();
+ }
+ }
+
+ return null;
+ }
+
static final class ColumnTracker {
private DbAttribute attribute;
@@ -705,4 +758,20 @@ public class DefaultSelectTranslator extends QueryAssembler implements SelectTra
}
+ static final class AccumulatingBindingListener implements AddBindingListener {
+ private List<DbAttributeBinding> bindings = new ArrayList<>();
+
+ @Override
+ public void onAdd(DbAttributeBinding binding) {
+ bindings.add(binding);
+ }
+
+ public void reset() {
+ bindings.clear();
+ }
+
+ public List<DbAttributeBinding> getBindings() {
+ return new ArrayList<>(bindings);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java
index 3998c00..b758b23 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java
@@ -54,6 +54,21 @@ public class QualifierTranslator extends QueryAssemblerHelper implements Travers
protected boolean matchingObject;
protected boolean caseInsensitive;
+ /**
+ * @since 4.0
+ */
+ protected boolean useAliasForExpressions;
+
+ /**
+ * @since 4.0
+ */
+ protected Expression waitingForEndNode;
+
+ /**
+ * @since 4.0
+ */
+ protected Expression qualifier;
+
public QualifierTranslator(QueryAssembler queryAssembler) {
super(queryAssembler);
@@ -76,6 +91,22 @@ public class QualifierTranslator extends QueryAssemblerHelper implements Travers
}
/**
+ * Explicitly set qualifier.
+ * It will be used instead of extracting qualifier from the query itself.
+ * @since 4.0
+ */
+ public void setQualifier(Expression qualifier) {
+ this.qualifier = qualifier;
+ }
+
+ /**
+ * @since 4.0
+ */
+ public void setUseAliasForExpressions(boolean useAliasForExpressions) {
+ this.useAliasForExpressions = useAliasForExpressions;
+ }
+
+ /**
* Translates query qualifier to SQL WHERE clause. Qualifier is a method
* parameter.
*
@@ -89,6 +120,11 @@ public class QualifierTranslator extends QueryAssemblerHelper implements Travers
}
protected Expression extractQualifier() {
+ // if additional qualifier is set, use it
+ if(this.qualifier != null) {
+ return this.qualifier;
+ }
+
Query q = queryAssembler.getQuery();
Expression qualifier = ((SelectQuery<?>) q).getQualifier();
@@ -194,6 +230,10 @@ public class QualifierTranslator extends QueryAssemblerHelper implements Travers
@Override
public void finishedChild(Expression node, int childIndex, boolean hasMoreChildren) {
+ if(waitingForEndNode != null) {
+ return;
+ }
+
if (!hasMoreChildren) {
return;
}
@@ -356,6 +396,20 @@ public class QualifierTranslator extends QueryAssemblerHelper implements Travers
@Override
public void startNode(Expression node, Expression parentNode) {
+
+ if(waitingForEndNode != null) {
+ return;
+ }
+
+ if(useAliasForExpressions) {
+ String alias = queryAssembler.getAliasForExpression(node);
+ if(alias != null) {
+ out.append(alias);
+ waitingForEndNode = node;
+ return;
+ }
+ }
+
boolean parenthesisNeeded = parenthesisNeeded(node, parentNode);
if(node.getType() == Expression.FUNCTION_CALL) {
@@ -409,6 +463,13 @@ public class QualifierTranslator extends QueryAssemblerHelper implements Travers
@Override
public void endNode(Expression node, Expression parentNode) {
+ if(waitingForEndNode != null) {
+ if(node == waitingForEndNode) {
+ waitingForEndNode = null;
+ }
+ return;
+ }
+
try {
// check if we need to use objectMatchTranslator to finish building the expression
if (node.getOperandCount() == 2 && matchingObject) {
@@ -449,6 +510,9 @@ public class QualifierTranslator extends QueryAssemblerHelper implements Travers
@Override
public void objectNode(Object leaf, Expression parentNode) {
+ if(waitingForEndNode != null) {
+ return;
+ }
try {
switch (parentNode.getType()) {
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QueryAssembler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QueryAssembler.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QueryAssembler.java
index f026823..1128e22 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QueryAssembler.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QueryAssembler.java
@@ -23,6 +23,7 @@ import org.apache.cayenne.access.translator.DbAttributeBinding;
import org.apache.cayenne.access.types.ExtendedType;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dba.TypesMapping;
+import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbRelationship;
import org.apache.cayenne.map.EntityResolver;
@@ -46,6 +47,10 @@ public abstract class QueryAssembler {
protected DbAdapter adapter;
protected EntityResolver entityResolver;
protected List<DbAttributeBinding> bindings;
+ /**
+ * @since 4.0
+ */
+ protected AddBindingListener addBindingListener;
/**
* @since 4.0
@@ -158,6 +163,9 @@ public abstract class QueryAssembler {
binding.setValue(anObject);
binding.setStatementPosition(bindings.size() + 1);
bindings.add(binding);
+ if(addBindingListener != null) {
+ addBindingListener.onAdd(binding);
+ }
}
/**
@@ -166,4 +174,23 @@ public abstract class QueryAssembler {
public DbAttributeBinding[] getBindings() {
return bindings.toArray(new DbAttributeBinding[bindings.size()]);
}
+
+ /**
+ * @since 4.0
+ */
+ public abstract String getAliasForExpression(Expression exp);
+
+ /**
+ * @since 4.0
+ */
+ public void setAddBindingListener(AddBindingListener addBindingListener) {
+ this.addBindingListener = addBindingListener;
+ }
+
+ /**
+ * @since 4.0
+ */
+ protected interface AddBindingListener {
+ void onAdd(DbAttributeBinding binding);
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleSelectTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleSelectTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleSelectTranslator.java
index fa6e95b..c98aad9 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleSelectTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleSelectTranslator.java
@@ -61,12 +61,18 @@ class OracleSelectTranslator extends DefaultSelectTranslator {
// append columns (unroll the loop's first element)
int columnCount = selectColumnExpList.size();
- buffer.append(selectColumnExpList.get(0)).append(" AS c0");
+ buffer.append(selectColumnExpList.get(0));
+ if(!selectColumnExpList.get(0).contains(" AS ")) {
+ buffer.append(" AS c0");
+ }
// assume there is at least 1 element
for (int i = 1; i < columnCount; i++) {
buffer.append(", ");
- buffer.append(selectColumnExpList.get(i)).append(" AS c" + i);
+ buffer.append(selectColumnExpList.get(i));
+ if(!selectColumnExpList.get(i).contains(" AS ")) {
+ buffer.append(" AS c").append(i);
+ }
}
}
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/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 9c13244..47ac8f0 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
@@ -18,6 +18,7 @@
****************************************************************/
package org.apache.cayenne.exp;
+import org.apache.cayenne.exp.parser.ASTPath;
import org.apache.cayenne.query.Ordering;
import org.apache.cayenne.query.PrefetchTreeNode;
import org.apache.cayenne.query.SortOrder;
@@ -127,6 +128,26 @@ public class Property<E> {
/**
* @since 4.0
+ * @return alias for this property
+ */
+ public String getAlias() {
+ if(getName() == null) {
+ return null;
+ }
+
+ // check if default name for Path expression is overridden
+ Expression exp = getExpression();
+ if(exp instanceof ASTPath) {
+ if(((ASTPath) exp).getPath().equals(getName())) {
+ return null;
+ }
+ }
+
+ return getName();
+ }
+
+ /**
+ * @since 4.0
*/
public Expression getExpression() {
return expressionProvider.get();
@@ -615,7 +636,7 @@ public class Property<E> {
}
public static <T> Property<T> create(Expression expression, Class<? super T> type) {
- return new Property<>(expression.expName().toLowerCase(), expression, type);
+ return new Property<>(null, expression, type);
}
public static <T> Property<T> create(String name, Expression expression, Class<? super T> type) {
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/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
new file mode 100644
index 0000000..9aa2667
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/ColumnSelect.java
@@ -0,0 +1,276 @@
+/*****************************************************************
+ * 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.query;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.exp.ExpressionFactory;
+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>
+ * It can be properties of the object itself or some function calls (including aggregate functions)
+ * </p>
+ * <p>
+ * Usage examples:
+ * <pre>
+ * // selecting list of names:
+ * List<String> names = ColumnSelect.query(Artist.class, Artist.ARTIST_NAME).select(context);
+ *
+ * // selecting count:
+ * long count = ColumnSelect.query(Artist.class, Property.COUNT).selectOne();
+ * </pre>
+ * </p>
+ * @since 4.0
+ */
+public class ColumnSelect<T> extends FluentSelect<T, ColumnSelect<T>> {
+
+ private Collection<Property<?>> columns;
+ private boolean havingExpressionIsActive = false;
+ 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 columns columns to select
+ */
+ public static ColumnSelect<Object[]> query(Class<?> entityType, Property<?>... columns) {
+ return new ColumnSelect<Object[]>().entityType(entityType).columns(columns);
+ }
+
+ protected ColumnSelect() {
+ super();
+ }
+
+ protected ColumnSelect(ObjectSelect<T> select) {
+ super();
+ this.entityType = select.entityType;
+ this.entityName = select.entityName;
+ this.dbEntityName = select.dbEntityName;
+ this.where = select.where;
+ this.orderings = select.orderings;
+ this.prefetches = select.prefetches;
+ this.limit = select.limit;
+ this.offset = select.offset;
+ this.pageSize = select.pageSize;
+ this.statementFetchSize = select.statementFetchSize;
+ this.cacheStrategy = select.cacheStrategy;
+ this.cacheGroups = select.cacheGroups;
+ }
+
+ @Override
+ protected Query createReplacementQuery(EntityResolver resolver) {
+ SelectQuery<?> replacement = (SelectQuery)super.createReplacementQuery(resolver);
+ replacement.setColumns(columns);
+ replacement.setHavingQualifier(having);
+ return replacement;
+ }
+
+ /**
+ * <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>
+ * <p>
+ * <pre>
+ * List<Object[]> columns = ColumnSelect.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)
+ */
+ @SuppressWarnings("unchecked")
+ public ColumnSelect<Object[]> columns(Property<?>... properties) {
+ if (properties == null || properties.length == 0) {
+ return (ColumnSelect<Object[]>)this;
+ }
+
+ return columns(Arrays.asList(properties));
+ }
+
+ @SuppressWarnings("unchecked")
+ public ColumnSelect<Object[]> columns(Collection<Property<?>> properties) {
+ if (properties == null || properties.isEmpty()) {
+ return (ColumnSelect<Object[]>)this;
+ }
+
+ if (this.columns == null) {
+ this.columns = new ArrayList<>(properties.size());
+ }
+
+ columns.addAll(properties);
+ 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[])} method as subsequent
+ * call to this method will override previous columns set via this or
+ * {@link ColumnSelect#columns(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[])
+ */
+ @SuppressWarnings("unchecked")
+ protected <E> ColumnSelect<E> column(Property<E> property) {
+ if (this.columns == null) {
+ this.columns = new ArrayList<>(1);
+ } else {
+ this.columns.clear(); // if we don't clear then return type will be incorrect
+ }
+ this.columns.add(property);
+ return (ColumnSelect<E>) this;
+ }
+
+ /**
+ * Appends a having qualifier expression of this query. An equivalent to
+ * {@link #and(Expression...)} that can be used a syntactic sugar.
+ *
+ * @return this object
+ */
+ public ColumnSelect<T> having(Expression expression) {
+ havingExpressionIsActive = true;
+ return and(expression);
+ }
+
+ /**
+ * Appends a having qualifier expression of this query, using provided expression
+ * String and an array of position parameters. This is an equivalent to
+ * calling "and".
+ *
+ * @return this object
+ */
+ public ColumnSelect<T> having(String expressionString, Object... parameters) {
+ havingExpressionIsActive = true;
+ return and(ExpressionFactory.exp(expressionString, parameters));
+ }
+
+ /**
+ * AND's provided expressions to the existing WHERE or HAVING clause expression.
+ *
+ * @return this object
+ */
+ @SuppressWarnings("unchecked")
+ public ColumnSelect<T> and(Collection<Expression> expressions) {
+
+ if (expressions == null || expressions.isEmpty()) {
+ return this;
+ }
+
+ Collection<Expression> all;
+ Expression activeExpression = getActiveExpression();
+
+ if (activeExpression != null) {
+ all = new ArrayList<>(expressions.size() + 1);
+ all.add(activeExpression);
+ all.addAll(expressions);
+ } else {
+ all = expressions;
+ }
+
+ setActiveExpression(ExpressionFactory.and(all));
+ return this;
+ }
+
+ /**
+ * OR's provided expressions to the existing WHERE or HAVING clause expression.
+ *
+ * @return this object
+ */
+ @SuppressWarnings("unchecked")
+ public ColumnSelect<T> or(Collection<Expression> expressions) {
+ if (expressions == null || expressions.isEmpty()) {
+ return this;
+ }
+
+ Collection<Expression> all;
+ Expression activeExpression = getActiveExpression();
+
+ if (activeExpression != null) {
+ all = new ArrayList<>(expressions.size() + 1);
+ all.add(activeExpression);
+ all.addAll(expressions);
+ } else {
+ all = expressions;
+ }
+
+ setActiveExpression(ExpressionFactory.or(all));
+ return this;
+ }
+
+ private void setActiveExpression(Expression exp) {
+ if(havingExpressionIsActive) {
+ having = exp;
+ } else {
+ where = exp;
+ }
+ }
+
+ private Expression getActiveExpression() {
+ if(havingExpressionIsActive) {
+ return having;
+ } else {
+ return where;
+ }
+ }
+
+ public Collection<Property<?>> getColumns() {
+ return columns;
+ }
+
+ /**
+ * Returns a HAVING clause Expression of this query.
+ */
+ public Expression getHaving() {
+ return having;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/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
new file mode 100644
index 0000000..de772bc
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/FluentSelect.java
@@ -0,0 +1,543 @@
+/*****************************************************************
+ * 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.query;
+
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.ResultBatchIterator;
+import org.apache.cayenne.ResultIterator;
+import org.apache.cayenne.ResultIteratorCallback;
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.map.ObjEntity;
+
+/**
+ * Base class for ObjectSelect and ColumnSelect
+ *
+ * @since 4.0
+ */
+public abstract class FluentSelect<T, S extends FluentSelect<T, S>> extends IndirectQuery implements Select<T> {
+
+ protected Class<?> entityType;
+ protected String entityName;
+ protected String dbEntityName;
+ protected Expression where;
+ protected Collection<Ordering> orderings;
+ protected PrefetchTreeNode prefetches;
+ protected int limit;
+ protected int offset;
+ protected int pageSize;
+ protected int statementFetchSize;
+ protected QueryCacheStrategy cacheStrategy;
+ protected String[] cacheGroups;
+
+ protected FluentSelect() {
+ }
+
+ /**
+ * Translates self to a SelectQuery.
+ */
+ @SuppressWarnings({"deprecation", "unchecked"})
+ @Override
+ protected Query createReplacementQuery(EntityResolver resolver) {
+
+ @SuppressWarnings("rawtypes")
+ SelectQuery replacement = new SelectQuery();
+
+ if (entityType != null) {
+ replacement.setRoot(entityType);
+ } else if (entityName != null) {
+
+ ObjEntity entity = resolver.getObjEntity(entityName);
+ if (entity == null) {
+ throw new CayenneRuntimeException("Unrecognized ObjEntity name: " + entityName);
+ }
+
+ replacement.setRoot(entity);
+ } else if (dbEntityName != null) {
+
+ DbEntity entity = resolver.getDbEntity(dbEntityName);
+ if (entity == null) {
+ throw new CayenneRuntimeException("Unrecognized DbEntity name: " + dbEntityName);
+ }
+
+ replacement.setRoot(entity);
+ } else {
+ throw new CayenneRuntimeException("Undefined root entity of the query");
+ }
+
+ replacement.setQualifier(where);
+ replacement.addOrderings(orderings);
+ replacement.setPrefetchTree(prefetches);
+ replacement.setCacheStrategy(cacheStrategy);
+ replacement.setCacheGroups(cacheGroups);
+ replacement.setFetchLimit(limit);
+ replacement.setFetchOffset(offset);
+ replacement.setPageSize(pageSize);
+ replacement.setStatementFetchSize(statementFetchSize);
+
+ return replacement;
+ }
+
+ /**
+ * Sets the type of the entity to fetch without changing the return type of
+ * the query.
+ *
+ * @return this object
+ */
+ public S entityType(Class<?> entityType) {
+ return resetEntity(entityType, null, null);
+ }
+
+ /**
+ * Sets the {@link ObjEntity} name to fetch without changing the return type
+ * of the query. This form is most often used for generic entities that
+ * don't map to a distinct class.
+ *
+ * @return this object
+ */
+ public S entityName(String entityName) {
+ return resetEntity(null, entityName, null);
+ }
+
+ /**
+ * Sets the {@link DbEntity} name to fetch without changing the return type
+ * of the query. This form is most often used for generic entities that
+ * don't map to a distinct class.
+ *
+ * @return this object
+ */
+ public S dbEntityName(String dbEntityName) {
+ return resetEntity(null, null, dbEntityName);
+ }
+
+ @SuppressWarnings("unchecked")
+ private S resetEntity(Class<?> entityType, String entityName, String dbEntityName) {
+ this.entityType = entityType;
+ this.entityName = entityName;
+ this.dbEntityName = dbEntityName;
+ return (S)this;
+ }
+
+ /**
+ * Appends a qualifier expression of this query. An equivalent to
+ * {@link #and(Expression...)} that can be used a syntactic sugar.
+ *
+ * @return this object
+ */
+ public S where(Expression expression) {
+ return and(expression);
+ }
+
+ /**
+ * Appends a qualifier expression of this query, using provided expression
+ * String and an array of position parameters. This is an equivalent to
+ * calling "and".
+ *
+ * @return this object
+ */
+ public S where(String expressionString, Object... parameters) {
+ return and(ExpressionFactory.exp(expressionString, parameters));
+ }
+
+ /**
+ * AND's provided expressions to the existing WHERE clause expression.
+ *
+ * @return this object
+ */
+ @SuppressWarnings("unchecked")
+ public S and(Expression... expressions) {
+ if (expressions == null || expressions.length == 0) {
+ return (S)this;
+ }
+
+ return and(Arrays.asList(expressions));
+ }
+
+ /**
+ * AND's provided expressions to the existing WHERE clause expression.
+ *
+ * @return this object
+ */
+ @SuppressWarnings("unchecked")
+ public S and(Collection<Expression> expressions) {
+
+ if (expressions == null || expressions.isEmpty()) {
+ return (S)this;
+ }
+
+ Collection<Expression> all;
+
+ if (where != null) {
+ all = new ArrayList<>(expressions.size() + 1);
+ all.add(where);
+ all.addAll(expressions);
+ } else {
+ all = expressions;
+ }
+
+ where = ExpressionFactory.and(all);
+ return (S)this;
+ }
+
+ /**
+ * OR's provided expressions to the existing WHERE clause expression.
+ *
+ * @return this object
+ */
+ @SuppressWarnings("unchecked")
+ public S or(Expression... expressions) {
+ if (expressions == null || expressions.length == 0) {
+ return (S)this;
+ }
+
+ return or(Arrays.asList(expressions));
+ }
+
+ /**
+ * OR's provided expressions to the existing WHERE clause expression.
+ *
+ * @return this object
+ */
+ @SuppressWarnings("unchecked")
+ public S or(Collection<Expression> expressions) {
+ if (expressions == null || expressions.isEmpty()) {
+ return (S)this;
+ }
+
+ Collection<Expression> all;
+
+ if (where != null) {
+ all = new ArrayList<>(expressions.size() + 1);
+ all.add(where);
+ all.addAll(expressions);
+ } else {
+ all = expressions;
+ }
+
+ where = ExpressionFactory.or(all);
+ return (S)this;
+ }
+
+ /**
+ * Add an ascending ordering on the given property. If there is already an ordering
+ * on this query then add this ordering with a lower priority.
+ *
+ * @param property the property to sort on
+ * @return this object
+ */
+ public S orderBy(String property) {
+ return orderBy(new Ordering(property));
+ }
+
+ /**
+ * Add an ordering on the given property. If there is already an ordering
+ * on this query then add this ordering with a lower priority.
+ *
+ * @param property the property to sort on
+ * @param sortOrder the direction of the ordering
+ * @return this object
+ */
+ public S orderBy(String property, SortOrder sortOrder) {
+ return orderBy(new Ordering(property, sortOrder));
+ }
+
+ /**
+ * Add one or more orderings to this query.
+ *
+ * @return this object
+ */
+ @SuppressWarnings("unchecked")
+ public S orderBy(Ordering... orderings) {
+
+ if (orderings == null) {
+ return (S)this;
+ }
+
+ if (this.orderings == null) {
+ this.orderings = new ArrayList<>(orderings.length);
+ }
+
+ Collections.addAll(this.orderings, orderings);
+
+ return (S)this;
+ }
+
+ /**
+ * Adds a list of orderings to this query.
+ *
+ * @return this object
+ */
+ @SuppressWarnings("unchecked")
+ public S orderBy(Collection<Ordering> orderings) {
+
+ if (orderings == null) {
+ return (S)this;
+ }
+
+ if (this.orderings == null) {
+ this.orderings = new ArrayList<>(orderings.size());
+ }
+
+ this.orderings.addAll(orderings);
+
+ return (S)this;
+ }
+
+ /**
+ * Merges prefetch into the query prefetch tree.
+ *
+ * @return this object
+ */
+ @SuppressWarnings("unchecked")
+ public S prefetch(PrefetchTreeNode prefetch) {
+
+ if (prefetch == null) {
+ return (S)this;
+ }
+
+ if (prefetches == null) {
+ prefetches = new PrefetchTreeNode();
+ }
+
+ prefetches.merge(prefetch);
+ return (S)this;
+ }
+
+ /**
+ * Merges a prefetch path with specified semantics into the query prefetch
+ * tree.
+ *
+ * @return this object
+ */
+ @SuppressWarnings("unchecked")
+ public S prefetch(String path, int semantics) {
+
+ if (path == null) {
+ return (S)this;
+ }
+
+ if (prefetches == null) {
+ prefetches = new PrefetchTreeNode();
+ }
+
+ prefetches.addPath(path).setSemantics(semantics);
+ return (S)this;
+ }
+
+ /**
+ * Resets query fetch limit - a parameter that defines max number of objects
+ * that should be ever be fetched from the database.
+ */
+ @SuppressWarnings("unchecked")
+ public S limit(int fetchLimit) {
+ if (this.limit != fetchLimit) {
+ this.limit = fetchLimit;
+ this.replacementQuery = null;
+ }
+
+ return (S)this;
+ }
+
+ /**
+ * Resets query fetch offset - a parameter that defines how many objects
+ * should be skipped when reading data from the database.
+ */
+ @SuppressWarnings("unchecked")
+ public S offset(int fetchOffset) {
+ if (this.offset != fetchOffset) {
+ this.offset = fetchOffset;
+ this.replacementQuery = null;
+ }
+
+ return (S)this;
+ }
+
+ /**
+ * Resets query page size. A non-negative page size enables query result
+ * pagination that saves memory and processing time for large lists if only
+ * parts of the result are ever going to be accessed.
+ */
+ @SuppressWarnings("unchecked")
+ public S pageSize(int pageSize) {
+ if (this.pageSize != pageSize) {
+ this.pageSize = pageSize;
+ this.replacementQuery = null;
+ }
+
+ return (S)this;
+ }
+
+ /**
+ * Sets fetch size of the PreparedStatement generated for this query. Only
+ * non-negative values would change the default size.
+ *
+ * @see Statement#setFetchSize(int)
+ */
+ @SuppressWarnings("unchecked")
+ public S statementFetchSize(int size) {
+ if (this.statementFetchSize != size) {
+ this.statementFetchSize = size;
+ this.replacementQuery = null;
+ }
+
+ return (S)this;
+ }
+
+ public S cacheStrategy(QueryCacheStrategy strategy, String... cacheGroups) {
+ if (this.cacheStrategy != strategy) {
+ this.cacheStrategy = strategy;
+ this.replacementQuery = null;
+ }
+
+ return cacheGroups(cacheGroups);
+ }
+
+ @SuppressWarnings("unchecked")
+ public S cacheGroups(String... cacheGroups) {
+ this.cacheGroups = cacheGroups != null && cacheGroups.length > 0 ? cacheGroups : null;
+ this.replacementQuery = null;
+ return (S)this;
+ }
+
+ public S cacheGroups(Collection<String> cacheGroups) {
+
+ if (cacheGroups == null) {
+ return cacheGroups((String) null);
+ }
+
+ String[] array = new String[cacheGroups.size()];
+ return cacheGroups(cacheGroups.toArray(array));
+ }
+
+ /**
+ * Instructs Cayenne to look for query results in the "local" cache when
+ * running the query. This is a short-hand notation for:
+ * <p>
+ * <pre>
+ * query.cacheStrategy(QueryCacheStrategy.LOCAL_CACHE, cacheGroups);
+ * </pre>
+ */
+ public S localCache(String... cacheGroups) {
+ return cacheStrategy(QueryCacheStrategy.LOCAL_CACHE, cacheGroups);
+ }
+
+ /**
+ * Instructs Cayenne to look for query results in the "shared" cache when
+ * running the query. This is a short-hand notation for:
+ * <p>
+ * <pre>
+ * query.cacheStrategy(QueryCacheStrategy.SHARED_CACHE, cacheGroups);
+ * </pre>
+ */
+ public S sharedCache(String... cacheGroups) {
+ return cacheStrategy(QueryCacheStrategy.SHARED_CACHE, cacheGroups);
+ }
+
+ public String[] getCacheGroups() {
+ return cacheGroups;
+ }
+
+ public QueryCacheStrategy getCacheStrategy() {
+ return cacheStrategy;
+ }
+
+ public int getStatementFetchSize() {
+ return statementFetchSize;
+ }
+
+ public int getPageSize() {
+ return pageSize;
+ }
+
+ public int getLimit() {
+ return limit;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public Class<?> getEntityType() {
+ return entityType;
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public String getDbEntityName() {
+ return dbEntityName;
+ }
+
+ /**
+ * Returns a WHERE clause Expression of this query.
+ */
+ public Expression getWhere() {
+ return where;
+ }
+
+ public Collection<Ordering> getOrderings() {
+ return orderings;
+ }
+
+ public PrefetchTreeNode getPrefetches() {
+ return prefetches;
+ }
+
+ @Override
+ public List<T> select(ObjectContext context) {
+ return context.select(this);
+ }
+
+ @Override
+ public T selectOne(ObjectContext context) {
+ return context.selectOne(this);
+ }
+
+ @Override
+ public T selectFirst(ObjectContext context) {
+ return context.selectFirst(limit(1));
+ }
+
+ @Override
+ public void iterate(ObjectContext context, ResultIteratorCallback<T> callback) {
+ context.iterate(this, callback);
+ }
+
+ @Override
+ public ResultIterator<T> iterator(ObjectContext context) {
+ return context.iterator(this);
+ }
+
+ @Override
+ public ResultBatchIterator<T> batchIterator(ObjectContext context, int size) {
+ return context.batchIterator(this, size);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/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 e11589c..7f6066e 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
@@ -18,24 +18,13 @@
****************************************************************/
package org.apache.cayenne.query;
-import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.DataRow;
-import org.apache.cayenne.ObjectContext;
-import org.apache.cayenne.ResultBatchIterator;
-import org.apache.cayenne.ResultIterator;
-import org.apache.cayenne.ResultIteratorCallback;
import org.apache.cayenne.exp.Expression;
-import org.apache.cayenne.exp.ExpressionFactory;
import org.apache.cayenne.exp.Property;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.map.ObjEntity;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
import java.util.List;
/**
@@ -53,25 +42,11 @@ import java.util.List;
*
* @since 4.0
*/
-public class ObjectSelect<T> extends IndirectQuery implements Select<T> {
+public class ObjectSelect<T> extends FluentSelect<T, ObjectSelect<T>> {
private static final long serialVersionUID = -156124021150949227L;
- private boolean fetchingDataRows;
-
- private Class<?> entityType;
- private String entityName;
- private String dbEntityName;
- private Collection<Property<?>> columns;
- private Expression where;
- private Collection<Ordering> orderings;
- private PrefetchTreeNode prefetches;
- private int limit;
- private int offset;
- private int pageSize;
- private int statementFetchSize;
- private QueryCacheStrategy cacheStrategy;
- private String[] cacheGroups;
+ protected boolean fetchingDataRows;
/**
* Creates a ObjectSelect that selects objects of a given persistent class.
@@ -151,87 +126,12 @@ public class ObjectSelect<T> extends IndirectQuery implements Select<T> {
@SuppressWarnings({"deprecation", "unchecked"})
@Override
protected Query createReplacementQuery(EntityResolver resolver) {
-
- @SuppressWarnings("rawtypes")
- SelectQuery replacement = new SelectQuery();
-
- if (entityType != null) {
- replacement.setRoot(entityType);
- } else if (entityName != null) {
-
- ObjEntity entity = resolver.getObjEntity(entityName);
- if (entity == null) {
- throw new CayenneRuntimeException("Unrecognized ObjEntity name: " + entityName);
- }
-
- replacement.setRoot(entity);
- } else if (dbEntityName != null) {
-
- DbEntity entity = resolver.getDbEntity(dbEntityName);
- if (entity == null) {
- throw new CayenneRuntimeException("Unrecognized DbEntity name: " + dbEntityName);
- }
-
- replacement.setRoot(entity);
- } else {
- throw new CayenneRuntimeException("Undefined root entity of the query");
- }
-
+ SelectQuery<?> replacement = (SelectQuery<?>) super.createReplacementQuery(resolver);
replacement.setFetchingDataRows(fetchingDataRows);
- replacement.setQualifier(where);
- replacement.addOrderings(orderings);
- replacement.setPrefetchTree(prefetches);
- replacement.setCacheStrategy(cacheStrategy);
- replacement.setCacheGroups(cacheGroups);
- replacement.setFetchLimit(limit);
- replacement.setFetchOffset(offset);
- replacement.setPageSize(pageSize);
- replacement.setStatementFetchSize(statementFetchSize);
- replacement.setColumns(columns);
-
return replacement;
}
/**
- * Sets the type of the entity to fetch without changing the return type of
- * the query.
- *
- * @return this object
- */
- public ObjectSelect<T> entityType(Class<?> entityType) {
- return resetEntity(entityType, null, null);
- }
-
- /**
- * Sets the {@link ObjEntity} name to fetch without changing the return type
- * of the query. This form is most often used for generic entities that
- * don't map to a distinct class.
- *
- * @return this object
- */
- public ObjectSelect<T> entityName(String entityName) {
- return resetEntity(null, entityName, null);
- }
-
- /**
- * Sets the {@link DbEntity} name to fetch without changing the return type
- * of the query. This form is most often used for generic entities that
- * don't map to a distinct class.
- *
- * @return this object
- */
- public ObjectSelect<T> dbEntityName(String dbEntityName) {
- return resetEntity(null, null, dbEntityName);
- }
-
- private ObjectSelect<T> resetEntity(Class<?> entityType, String entityName, String dbEntityName) {
- this.entityType = entityType;
- this.entityName = entityName;
- this.dbEntityName = dbEntityName;
- return this;
- }
-
- /**
* Forces query to fetch DataRows. This automatically changes whatever
* result type was set previously to "DataRow".
*
@@ -244,448 +144,45 @@ public class ObjectSelect<T> extends IndirectQuery implements Select<T> {
}
/**
- * Appends a qualifier expression of this query. An equivalent to
- * {@link #and(Expression...)} that can be used a syntactic sugar.
- *
- * @return this object
- */
- public ObjectSelect<T> where(Expression expression) {
- and(expression);
- return this;
- }
-
- /**
- * Appends a qualifier expression of this query, using provided expression
- * String and an array of position parameters. This is an equivalent to
- * calling "and".
- *
- * @return this object
- */
- public ObjectSelect<T> where(String expressionString, Object... parameters) {
- and(ExpressionFactory.exp(expressionString, parameters));
- return this;
- }
-
- /**
- * AND's provided expressions to the existing WHERE clause expression.
- *
- * @return this object
- */
- public ObjectSelect<T> and(Expression... expressions) {
- if (expressions == null || expressions.length == 0) {
- return this;
- }
-
- return and(Arrays.asList(expressions));
- }
-
- /**
- * AND's provided expressions to the existing WHERE clause expression.
- *
- * @return this object
- */
- public ObjectSelect<T> and(Collection<Expression> expressions) {
-
- if (expressions == null || expressions.isEmpty()) {
- return this;
- }
-
- Collection<Expression> all;
-
- if (where != null) {
- all = new ArrayList<>(expressions.size() + 1);
- all.add(where);
- all.addAll(expressions);
- } else {
- all = expressions;
- }
-
- where = ExpressionFactory.and(all);
- return this;
- }
-
- /**
- * OR's provided expressions to the existing WHERE clause expression.
- *
- * @return this object
- */
- public ObjectSelect<T> or(Expression... expressions) {
- if (expressions == null || expressions.length == 0) {
- return this;
- }
-
- return or(Arrays.asList(expressions));
- }
-
- /**
- * OR's provided expressions to the existing WHERE clause expression.
- *
- * @return this object
- */
- public ObjectSelect<T> or(Collection<Expression> expressions) {
- if (expressions == null || expressions.isEmpty()) {
- return this;
- }
-
- Collection<Expression> all;
-
- if (where != null) {
- all = new ArrayList<>(expressions.size() + 1);
- all.add(where);
- all.addAll(expressions);
- } else {
- all = expressions;
- }
-
- where = ExpressionFactory.or(all);
- return this;
- }
-
- /**
- * Add an ascending ordering on the given property. If there is already an ordering
- * on this query then add this ordering with a lower priority.
- *
- * @param property the property to sort on
- * @return this object
- */
- public ObjectSelect<T> orderBy(String property) {
- return orderBy(new Ordering(property));
- }
-
- /**
- * Add an ordering on the given property. If there is already an ordering
- * on this query then add this ordering with a lower priority.
- *
- * @param property the property to sort on
- * @param sortOrder the direction of the ordering
- * @return this object
- */
- public ObjectSelect<T> orderBy(String property, SortOrder sortOrder) {
- return orderBy(new Ordering(property, sortOrder));
- }
-
- /**
- * Add one or more orderings to this query.
- *
- * @return this object
- */
- public ObjectSelect<T> orderBy(Ordering... orderings) {
-
- if (orderings == null) {
- return this;
- }
-
- if (this.orderings == null) {
- this.orderings = new ArrayList<>(orderings.length);
- }
-
- Collections.addAll(this.orderings, orderings);
-
- return this;
- }
-
- /**
- * Adds a list of orderings to this query.
- *
- * @return this object
- */
- public ObjectSelect<T> orderBy(Collection<Ordering> orderings) {
-
- if (orderings == null) {
- return this;
- }
-
- if (this.orderings == null) {
- this.orderings = new ArrayList<>(orderings.size());
- }
-
- this.orderings.addAll(orderings);
-
- return this;
- }
-
- /**
- * Merges prefetch into the query prefetch tree.
- *
- * @return this object
- */
- public ObjectSelect<T> prefetch(PrefetchTreeNode prefetch) {
-
- if (prefetch == null) {
- return this;
- }
-
- if (prefetches == null) {
- prefetches = new PrefetchTreeNode();
- }
-
- prefetches.merge(prefetch);
- return this;
- }
-
- /**
- * Merges a prefetch path with specified semantics into the query prefetch
- * tree.
- *
- * @return this object
- */
- public ObjectSelect<T> prefetch(String path, int semantics) {
-
- if (path == null) {
- return this;
- }
-
- if (prefetches == null) {
- prefetches = new PrefetchTreeNode();
- }
-
- prefetches.addPath(path).setSemantics(semantics);
- return this;
- }
-
- /**
- * Resets query fetch limit - a parameter that defines max number of objects
- * that should be ever be fetched from the database.
- */
- public ObjectSelect<T> limit(int fetchLimit) {
- if (this.limit != fetchLimit) {
- this.limit = fetchLimit;
- this.replacementQuery = null;
- }
-
- return this;
- }
-
- /**
- * Resets query fetch offset - a parameter that defines how many objects
- * should be skipped when reading data from the database.
- */
- public ObjectSelect<T> offset(int fetchOffset) {
- if (this.offset != fetchOffset) {
- this.offset = fetchOffset;
- this.replacementQuery = null;
- }
-
- return this;
- }
-
- /**
- * Resets query page size. A non-negative page size enables query result
- * pagination that saves memory and processing time for large lists if only
- * parts of the result are ever going to be accessed.
- */
- public ObjectSelect<T> pageSize(int pageSize) {
- if (this.pageSize != pageSize) {
- this.pageSize = pageSize;
- this.replacementQuery = null;
- }
-
- return this;
- }
-
- /**
- * Sets fetch size of the PreparedStatement generated for this query. Only
- * non-negative values would change the default size.
- *
- * @see Statement#setFetchSize(int)
- */
- public ObjectSelect<T> statementFetchSize(int size) {
- if (this.statementFetchSize != size) {
- this.statementFetchSize = size;
- this.replacementQuery = null;
- }
-
- return this;
- }
-
- public ObjectSelect<T> cacheStrategy(QueryCacheStrategy strategy, String... cacheGroups) {
- if (this.cacheStrategy != strategy) {
- this.cacheStrategy = strategy;
- this.replacementQuery = null;
- }
-
- return cacheGroups(cacheGroups);
- }
-
- public ObjectSelect<T> cacheGroups(String... cacheGroups) {
- this.cacheGroups = cacheGroups != null && cacheGroups.length > 0 ? cacheGroups : null;
- this.replacementQuery = null;
- return this;
- }
-
- public ObjectSelect<T> cacheGroups(Collection<String> cacheGroups) {
-
- if (cacheGroups == null) {
- return cacheGroups((String) null);
- }
-
- String[] array = new String[cacheGroups.size()];
- return cacheGroups(cacheGroups.toArray(array));
- }
-
- /**
- * Instructs Cayenne to look for query results in the "local" cache when
- * running the query. This is a short-hand notation for:
- * <p>
- * <pre>
- * query.cacheStrategy(QueryCacheStrategy.LOCAL_CACHE, cacheGroups);
- * </pre>
- */
- public ObjectSelect<T> localCache(String... cacheGroups) {
- return cacheStrategy(QueryCacheStrategy.LOCAL_CACHE, cacheGroups);
- }
-
- /**
- * Instructs Cayenne to look for query results in the "shared" cache when
- * running the query. This is a short-hand notation for:
- * <p>
- * <pre>
- * query.cacheStrategy(QueryCacheStrategy.SHARED_CACHE, cacheGroups);
- * </pre>
- */
- public ObjectSelect<T> sharedCache(String... cacheGroups) {
- return cacheStrategy(QueryCacheStrategy.SHARED_CACHE, cacheGroups);
- }
-
- /**
* <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>
* <p>
* <pre>
- * List<Object[]> columns = ObjectSelect.query(Artist.class)
+ * List<Object[]> columns = ColumnSelect.query(Artist.class)
* .columns(Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH)
* .select(context);
* </pre>
*
* @param properties array of properties to select
- * @see ObjectSelect#column(Property)
+ * @see ColumnSelect#column(Property)
*/
@SuppressWarnings("unchecked")
- public ObjectSelect<Object[]> columns(Property<?>... properties) {
- if (properties == null) {
- return (ObjectSelect<Object[]>) this;
- }
-
- if (this.columns == null) {
- this.columns = new ArrayList<>(properties.length);
- }
- Collections.addAll(this.columns, properties);
- return (ObjectSelect<Object[]>) this;
+ public ColumnSelect<Object[]> columns(Property<?>... properties) {
+ return new ColumnSelect<>(this).columns(properties);
}
/**
* <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 ObjectSelect#columns(Property[])} method as subsequent
+ * <p>If you need several columns use {@link ColumnSelect#columns(Property[])} method as subsequent
* call to this method will override previous columns set via this or
- * {@link ObjectSelect#columns(Property[])} method.</p>
+ * {@link ColumnSelect#columns(Property[])} method.</p>
* <p>
* <pre>
* List<String> names = ObjectSelect.query(Artist.class).column(Artist.ARTIST_NAME).select(context);
* </pre>
*
* @param property single property to select
- * @see ObjectSelect#columns(Property[])
+ * @see ColumnSelect#columns(Property[])
*/
@SuppressWarnings("unchecked")
- public <E> ObjectSelect<E> column(Property<E> property) {
- if (this.columns == null) {
- this.columns = new ArrayList<>(1);
- } else {
- this.columns.clear(); // if we don't clear then return type will be incorrect
- }
- this.columns.add(property);
- return (ObjectSelect<E>) this;
- }
-
- public String[] getCacheGroups() {
- return cacheGroups;
- }
-
- public QueryCacheStrategy getCacheStrategy() {
- return cacheStrategy;
- }
-
- public int getStatementFetchSize() {
- return statementFetchSize;
- }
-
- public int getPageSize() {
- return pageSize;
- }
-
- public int getLimit() {
- return limit;
- }
-
- public int getOffset() {
- return offset;
+ protected <E> ColumnSelect<E> column(Property<E> property) {
+ return new ColumnSelect<>(this).column(property);
}
public boolean isFetchingDataRows() {
return fetchingDataRows;
}
-
- public Class<?> getEntityType() {
- return entityType;
- }
-
- public String getEntityName() {
- return entityName;
- }
-
- public String getDbEntityName() {
- return dbEntityName;
- }
-
- /**
- * Returns a WHERE clause Expression of this query.
- */
- public Expression getWhere() {
- return where;
- }
-
- public Collection<Ordering> getOrderings() {
- return orderings;
- }
-
- public PrefetchTreeNode getPrefetches() {
- return prefetches;
- }
-
- public Collection<Property<?>> getColumns() {
- return columns;
- }
-
- @Override
- public List<T> select(ObjectContext context) {
- return context.select(this);
- }
-
- @Override
- public T selectOne(ObjectContext context) {
- return context.selectOne(this);
- }
-
- @Override
- public T selectFirst(ObjectContext context) {
- return context.selectFirst(limit(1));
- }
-
- @Override
- public void iterate(ObjectContext context, ResultIteratorCallback<T> callback) {
- context.iterate(this, callback);
- }
-
- @Override
- public ResultIterator<T> iterator(ObjectContext context) {
- return context.iterator(this);
- }
-
- @Override
- public ResultBatchIterator<T> batchIterator(ObjectContext context, int size) {
- return context.batchIterator(this, size);
- }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java
index 773d97d..9c06d05 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java
@@ -64,6 +64,11 @@ public class SelectQuery<T> extends AbstractQuery implements ParameterizedQuery,
*/
protected Collection<Property<?>> columns;
+ /**
+ * @since 4.0
+ */
+ protected Expression havingQualifier;
+
SelectQueryMetadata metaData = new SelectQueryMetadata();
/**
@@ -875,7 +880,7 @@ public class SelectQuery<T> extends AbstractQuery implements ParameterizedQuery,
if(columns == null || columns.length == 0) {
return;
}
- this.columns = Arrays.asList(columns);
+ setColumns(Arrays.asList(columns));
}
/**
@@ -884,4 +889,36 @@ public class SelectQuery<T> extends AbstractQuery implements ParameterizedQuery,
public Collection<Property<?>> getColumns() {
return columns;
}
+
+ /**
+ * Sets new query HAVING qualifier.
+ * @since 4.0
+ */
+ public void setHavingQualifier(Expression qualifier) {
+ this.havingQualifier = qualifier;
+ }
+
+ /**
+ * Returns query HAVING qualifier.
+ * @since 4.0
+ */
+ public Expression getHavingQualifier() {
+ return havingQualifier;
+ }
+
+ /**
+ * Adds specified HAVING qualifier to the existing HAVING qualifier joining it using "AND".
+ * @since 4.0
+ */
+ public void andHavingQualifier(Expression e) {
+ havingQualifier = (havingQualifier != null) ? havingQualifier.andExp(e) : e;
+ }
+
+ /**
+ * Adds specified HAVING qualifier to the existing HAVING qualifier joining it using "OR".
+ * @since 4.0
+ */
+ public void orHavingQualifier(Expression e) {
+ havingQualifier = (havingQualifier != null) ? havingQualifier.orExp(e) : e;
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java
index 95562b1..741190e 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java
@@ -181,7 +181,8 @@ class SelectQueryMetadata extends BaseQueryMetadata {
SQLResult result = new SQLResult();
for(Property<?> column : query.getColumns()) {
- result.addColumnResult(column.getName());
+ String name = column.getName() == null ? column.getExpression().expName() : column.getName();
+ result.addColumnResult(name);
}
resultSetMapping = result.getResolvedComponents(resolver);
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/cayenne-server/src/test/java/org/apache/cayenne/access/ReturnTypesMappingIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/ReturnTypesMappingIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/ReturnTypesMappingIT.java
index 9bb40ff..f06d146 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/ReturnTypesMappingIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/ReturnTypesMappingIT.java
@@ -821,7 +821,7 @@ public class ReturnTypesMappingIT extends ServerCase {
DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap1")).get(0);
Object columnValue = testRead.get(columnName);
assertNotNull(columnValue);
- assertTrue(Short.class.equals(columnValue.getClass()));
+ assertEquals(Short.class, columnValue.getClass());
assertEquals(tinyintValue.intValue(), ((Number)columnValue).intValue());
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/TstQueryAssembler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/TstQueryAssembler.java b/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/TstQueryAssembler.java
index dfc7495..72a3803 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/TstQueryAssembler.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/translator/select/TstQueryAssembler.java
@@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.List;
import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.map.DbRelationship;
import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.map.JoinType;
@@ -58,6 +59,11 @@ public class TstQueryAssembler extends QueryAssembler {
}
@Override
+ public String getAliasForExpression(Expression exp) {
+ return null;
+ }
+
+ @Override
protected void doTranslate() {
this.sql = "SELECT * FROM ARTIST";
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/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
new file mode 100644
index 0000000..a4dcada
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java
@@ -0,0 +1,242 @@
+/*****************************************************************
+ * 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.query;
+
+import java.sql.Types;
+import java.text.DateFormat;
+import java.util.Arrays;
+import java.util.Locale;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.access.DataContext;
+import org.apache.cayenne.di.Inject;
+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.unit.UnitDbAdapter;
+import org.apache.cayenne.unit.di.server.CayenneProjects;
+import org.apache.cayenne.unit.di.server.ServerCase;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+import org.junit.After;
+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;
+
+/**
+ * @since 4.0
+ */
+@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
+public class ColumnSelectIT extends ServerCase {
+
+ @Inject
+ private DataContext context;
+
+ @Inject
+ private DBHelper dbHelper;
+
+ @Inject
+ private UnitDbAdapter unitDbAdapter;
+
+ // Format: d/m/YY
+ private DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US);
+
+
+ @Before
+ public void createArtistsDataSet() throws Exception {
+ TableHelper tArtist = new TableHelper(dbHelper, "ARTIST");
+ tArtist.setColumns("ARTIST_ID", "ARTIST_NAME", "DATE_OF_BIRTH");
+ tArtist.setColumnTypes(Types.INTEGER, Types.VARCHAR, Types.DATE);
+
+ java.sql.Date[] dates = new java.sql.Date[5];
+ for(int i=1; i<=5; i++) {
+ dates[i-1] = new java.sql.Date(dateFormat.parse("1/" + i + "/17").getTime());
+ }
+ for (int i = 1; i <= 20; i++) {
+ tArtist.insert(i, "artist" + i, dates[i % 5]);
+ }
+
+ TableHelper tGallery = new TableHelper(dbHelper, "GALLERY");
+ tGallery.setColumns("GALLERY_ID", "GALLERY_NAME");
+ tGallery.insert(1, "tate modern");
+
+ TableHelper tPaintings = new TableHelper(dbHelper, "PAINTING");
+ tPaintings.setColumns("PAINTING_ID", "PAINTING_TITLE", "ARTIST_ID", "GALLERY_ID");
+ for (int i = 1; i <= 20; i++) {
+ tPaintings.insert(i, "painting" + i, i % 5 + 1, 1);
+ }
+ tPaintings.insert(21, "painting21", 2, 1);
+ }
+
+ @After
+ public void clearArtistsDataSet() throws Exception {
+ for(String table : Arrays.asList("PAINTING", "ARTIST", "GALLERY")) {
+ TableHelper tHelper = new TableHelper(dbHelper, table);
+ tHelper.deleteAll();
+ }
+ }
+
+ @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)
+ .orderBy(Artist.DATE_OF_BIRTH.asc())
+ .selectFirst(context);
+
+ assertEquals(dateFormat.parse("1/1/17"), result[0]);
+ assertEquals(4L, result[1]);
+ }
+
+ @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)
+ .orderBy(Artist.DATE_OF_BIRTH.asc())
+ .having(Artist.DATE_OF_BIRTH.eq(dateFormat.parse("1/2/17")))
+ .selectOne(context);
+
+ assertEquals(dateFormat.parse("1/2/17"), result[0]);
+ assertEquals(4L, result[1]);
+ }
+
+ @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)
+ .having(Artist.ARTIST_NAME.like("artist%"))
+ .selectOne(context);
+ assertEquals("artist", q[0]);
+ assertEquals(20L, q[1]);
+ }
+
+ @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)
+ .orderBy(Artist.DATE_OF_BIRTH.asc())
+ .selectFirst(context);
+ assertEquals(dateFormat.parse("1/1/17"), result[0]);
+ assertEquals(4L, result[1]);
+ }
+
+ @Test
+ 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))
+ .selectOne(context);
+ } catch (CayenneRuntimeException ex) {
+ if(unitDbAdapter.supportsExpressionInHaving()) {
+ fail();
+ } else {
+ return;
+ }
+ }
+ assertEquals("artist", q[0]);
+ assertEquals(20L, q[1]);
+ }
+
+ @Ignore("Need to figure out a better way to handle alias / no alias case for expression in having")
+ @Test
+ 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))
+ .selectOne(context);
+ } catch (CayenneRuntimeException ex) {
+ if(unitDbAdapter.supportsExpressionInHaving()) {
+ fail();
+ } else {
+ return;
+ }
+ }
+ assertEquals("artist", q[0]);
+ assertEquals(20L, q[1]);
+ }
+
+ @Test
+ 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)
+ .where(nameFirstLetter.eq("a"))
+ .having(count.gt(10L))
+ .selectOne(context);
+ } catch (CayenneRuntimeException ex) {
+ if(unitDbAdapter.supportsExpressionInHaving()) {
+ fail();
+ } else {
+ return;
+ }
+ }
+ assertEquals("artist", q[0]);
+ assertEquals(20L, q[1]);
+ }
+
+ @Test
+ public void testSelectRelationshipCountHaving() throws Exception {
+ Property<Long> paintingCount = Property.create(countExp(Artist.PAINTING_ARRAY.path()), Long.class);
+
+ Object[] result = null;
+ try {
+ result = ColumnSelect.query(Artist.class)
+ .columns(Artist.ARTIST_NAME, paintingCount)
+ .having(paintingCount.gt(4L))
+ .selectOne(context);
+ } catch (CayenneRuntimeException ex) {
+ if(unitDbAdapter.supportsExpressionInHaving()) {
+ fail();
+ } else {
+ return;
+ }
+ }
+ assertEquals("artist2", result[0]);
+ assertEquals(5L, result[1]);
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/4f860180/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
new file mode 100644
index 0000000..c058916
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectTest.java
@@ -0,0 +1,199 @@
+/*****************************************************************
+ * 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.query;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+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.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @since 4.0
+ */
+public class ColumnSelectTest {
+
+ @Test
+ public void query() throws Exception {
+ ColumnSelect<Artist> q = ColumnSelect.query(Artist.class);
+ assertNull(q.getColumns());
+ assertNull(q.getHaving());
+ }
+
+ @Test
+ public void queryWithColumn() throws Exception {
+ ColumnSelect<String> q = ColumnSelect.query(Artist.class, Artist.ARTIST_NAME);
+ assertEquals(Arrays.asList(Artist.ARTIST_NAME), q.getColumns());
+ assertNull(q.getHaving());
+ }
+
+ @Test
+ public void queryWithColumns() throws Exception {
+ ColumnSelect<Object[]> q = ColumnSelect.query(Artist.class, Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH);
+ assertEquals(Arrays.asList(Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH), q.getColumns());
+ assertNull(q.getHaving());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void columns() throws Exception {
+ ColumnSelect q = ColumnSelect.query(Artist.class);
+ assertNull(q.getColumns());
+ q.columns();
+ 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);
+ 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());
+ }
+
+
+ @Test
+ public void havingExpression() throws Exception {
+ ColumnSelect q = ColumnSelect.query(Artist.class);
+ assertNull(q.getHaving());
+ assertNull(q.getWhere());
+
+ Expression expTrue = ExpressionFactory.expTrue();
+ q.where(expTrue);
+ assertNull(q.getHaving());
+ assertEquals(expTrue, q.getWhere());
+
+ Expression expFalse = ExpressionFactory.expFalse();
+ q.having(expFalse);
+ assertEquals(expFalse, q.getHaving());
+ assertEquals(expTrue, q.getWhere());
+ }
+
+ @Test
+ public void havingString() throws Exception {
+ ColumnSelect q = ColumnSelect.query(Artist.class);
+ assertNull(q.getHaving());
+ assertNull(q.getWhere());
+
+ Expression expTrue = ExpressionFactory.expTrue();
+ q.where(expTrue);
+ assertNull(q.getHaving());
+ assertEquals(expTrue, q.getWhere());
+
+ Expression expFalse = ExpressionFactory.expFalse();
+ q.having("false");
+ assertEquals(expFalse, q.getHaving());
+ assertEquals(expTrue, q.getWhere());
+ }
+
+ @Test
+ public void and() throws Exception {
+ ColumnSelect q = ColumnSelect.query(Artist.class);
+ assertNull(q.getHaving());
+ assertNull(q.getWhere());
+
+ Expression expTrue = ExpressionFactory.expTrue();
+ q.where(expTrue);
+ q.and(expTrue);
+ assertNull(q.getHaving());
+ assertEquals(ExpressionFactory.exp("true and true"), q.getWhere());
+
+ Expression expFalse = ExpressionFactory.expFalse();
+ q.having("false");
+ q.and(expFalse);
+ assertEquals(ExpressionFactory.exp("false and false"), q.getHaving());
+ assertEquals(ExpressionFactory.exp("true and true"), q.getWhere());
+ }
+
+ @Test
+ public void or() throws Exception {
+ ColumnSelect q = ColumnSelect.query(Artist.class);
+ assertNull(q.getHaving());
+ assertNull(q.getWhere());
+
+ Expression expTrue = ExpressionFactory.expTrue();
+ q.where(expTrue);
+ q.or(expTrue);
+ assertNull(q.getHaving());
+ assertEquals(ExpressionFactory.exp("true or true"), q.getWhere());
+
+ Expression expFalse = ExpressionFactory.expFalse();
+ q.having("false");
+ q.or(expFalse);
+ assertEquals(ExpressionFactory.exp("false or false"), q.getHaving());
+ assertEquals(ExpressionFactory.exp("true or true"), q.getWhere());
+ }
+
+
+ @Test
+ public void testColumnsAddByOne() {
+ ColumnSelect<Artist> q = ColumnSelect.query(Artist.class);
+
+ assertEquals(null, q.getColumns());
+
+ q.columns(Artist.ARTIST_NAME);
+ q.columns();
+ q.columns(Artist.DATE_OF_BIRTH);
+ q.columns();
+ q.columns(Artist.PAINTING_ARRAY);
+ q.columns();
+
+ Collection<Property<?>> properties = Arrays.asList(Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH, Artist.PAINTING_ARRAY);
+ assertEquals(properties, q.getColumns());
+ }
+
+ @Test
+ public void testColumnsAddAll() {
+ ColumnSelect<Artist> q = ColumnSelect.query(Artist.class);
+
+ assertEquals(null, q.getColumns());
+
+ q.columns(Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH, Artist.PAINTING_ARRAY);
+ q.columns(Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH, Artist.PAINTING_ARRAY);
+
+ Collection<Property<?>> properties = Arrays.asList(
+ Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH, Artist.PAINTING_ARRAY,
+ Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH, Artist.PAINTING_ARRAY); // should it be Set instead of List?
+ assertEquals(properties, q.getColumns());
+ }
+
+ @Test
+ public void testColumnAddByOne() {
+ ColumnSelect<Artist> q = ColumnSelect.query(Artist.class);
+
+ assertEquals(null, q.getColumns());
+
+ q.column(Artist.ARTIST_NAME);
+ q.columns();
+ q.column(Artist.DATE_OF_BIRTH);
+ q.columns();
+ q.column(Artist.PAINTING_ARRAY);
+ q.columns();
+
+ Collection<Property<?>> properties = Collections.<Property<?>>singletonList(Artist.PAINTING_ARRAY);
+ assertEquals(properties, q.getColumns());
+ }
+
+}
\ No newline at end of file