You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2014/11/16 13:10:00 UTC
[1/8] cayenne git commit: CAY-1968 SQLSelect cleanup and omissions
Repository: cayenne
Updated Branches:
refs/heads/CAY-1946 29899e48d -> 7c1f69a1a
CAY-1968 SQLSelect cleanup and omissions
* also a unit test for SQLSelect
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/f9c7d647
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/f9c7d647
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/f9c7d647
Branch: refs/heads/CAY-1946
Commit: f9c7d647c1f2b4add5132c78aa3fdb69ac79349f
Parents: cfbfb68
Author: aadamchik <aa...@apache.org>
Authored: Sat Nov 15 11:10:47 2014 +0300
Committer: aadamchik <aa...@apache.org>
Committed: Sat Nov 15 11:16:55 2014 +0300
----------------------------------------------------------------------
.../apache/cayenne/query/BaseQueryMetadata.java | 4 +-
.../org/apache/cayenne/query/SQLSelect.java | 31 ++++---
.../org/apache/cayenne/query/SQLSelectTest.java | 91 ++++++++++++++++++++
docs/doc/src/main/resources/RELEASE-NOTES.txt | 1 +
4 files changed, 115 insertions(+), 12 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cayenne/blob/f9c7d647/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java b/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java
index 7f7c6dd..af402b4 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java
@@ -43,6 +43,8 @@ import org.apache.cayenne.util.XMLSerializable;
*/
class BaseQueryMetadata implements QueryMetadata, XMLSerializable, Serializable {
+ private static final long serialVersionUID = 5129792493303459115L;
+
int fetchLimit = QueryMetadata.FETCH_LIMIT_DEFAULT;
int fetchOffset = QueryMetadata.FETCH_OFFSET_DEFAULT;
@@ -156,7 +158,7 @@ class BaseQueryMetadata implements QueryMetadata, XMLSerializable, Serializable
void initWithProperties(Map<String, ?> properties) {
// must init defaults even if properties are empty
if (properties == null) {
- properties = Collections.EMPTY_MAP;
+ properties = Collections.emptyMap();
}
Object fetchOffset = properties.get(QueryMetadata.FETCH_OFFSET_PROPERTY);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/f9c7d647/cayenne-server/src/main/java/org/apache/cayenne/query/SQLSelect.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/SQLSelect.java b/cayenne-server/src/main/java/org/apache/cayenne/query/SQLSelect.java
index 5509cb4..b4504f4 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/SQLSelect.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/SQLSelect.java
@@ -19,6 +19,7 @@
package org.apache.cayenne.query;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -246,15 +247,14 @@ public class SQLSelect<T> extends IndirectQuery implements Select<T> {
* running the query. This is a short-hand notation for:
*
* <pre>
- * query.setCacheStrategy(QueryCacheStrategy.LOCAL_CACHE);
- * query.setCacheGroups("group1", "group2");
+ * query.cacheStrategy(QueryCacheStrategy.LOCAL_CACHE, cacheGroups);
* </pre>
- *
- * @since 4.0
*/
- public void useLocalCache(String... cacheGroups) {
+ public SQLSelect<T> localCache(String... cacheGroups) {
cacheStrategy(QueryCacheStrategy.LOCAL_CACHE);
cacheGroups(cacheGroups);
+
+ return this;
}
/**
@@ -262,11 +262,10 @@ public class SQLSelect<T> extends IndirectQuery implements Select<T> {
* running the query. This is a short-hand notation for:
*
* <pre>
- * query.setCacheStrategy(QueryCacheStrategy.SHARED_CACHE);
- * query.setCacheGroups("group1", "group2");
+ * query.cacheStrategy(QueryCacheStrategy.SHARED_CACHE, cacheGroups);
* </pre>
*/
- public SQLSelect<T> useSharedCache(String... cacheGroups) {
+ public SQLSelect<T> sharedCache(String... cacheGroups) {
return cacheStrategy(QueryCacheStrategy.SHARED_CACHE).cacheGroups(cacheGroups);
}
@@ -274,13 +273,13 @@ public class SQLSelect<T> extends IndirectQuery implements Select<T> {
return cacheStrategy;
}
- public SQLSelect<T> cacheStrategy(QueryCacheStrategy strategy) {
+ public SQLSelect<T> cacheStrategy(QueryCacheStrategy strategy, String... cacheGroups) {
if (this.cacheStrategy != strategy) {
this.cacheStrategy = strategy;
this.replacementQuery = null;
}
- return this;
+ return cacheGroups(cacheGroups);
}
public String[] getCacheGroups() {
@@ -288,11 +287,21 @@ public class SQLSelect<T> extends IndirectQuery implements Select<T> {
}
public SQLSelect<T> cacheGroups(String... cacheGroups) {
- this.cacheGroups = cacheGroups;
+ this.cacheGroups = cacheGroups != null && cacheGroups.length > 0 ? cacheGroups : null;
this.replacementQuery = null;
return this;
}
+ public SQLSelect<T> cacheGroups(Collection<String> cacheGroups) {
+
+ if (cacheGroups == null) {
+ return cacheGroups((String) null);
+ }
+
+ String[] array = new String[cacheGroups.size()];
+ return cacheGroups(cacheGroups.toArray(array));
+ }
+
/**
* Returns a column name capitalization policy applied to selecting queries.
* This is used to simplify mapping of the queries like "SELECT * FROM ...",
http://git-wip-us.apache.org/repos/asf/cayenne/blob/f9c7d647/cayenne-server/src/test/java/org/apache/cayenne/query/SQLSelectTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/SQLSelectTest.java b/cayenne-server/src/test/java/org/apache/cayenne/query/SQLSelectTest.java
new file mode 100644
index 0000000..88e1b6a
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/SQLSelectTest.java
@@ -0,0 +1,91 @@
+/*****************************************************************
+ * 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 static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import java.util.Arrays;
+
+import org.apache.cayenne.DataRow;
+import org.junit.Test;
+
+public class SQLSelectTest {
+
+ @Test
+ public void testCacheGroups_Collection() {
+ SQLSelect<DataRow> q = SQLSelect.dataRowQuery("bla");
+
+ assertNull(q.getCacheStrategy());
+ assertNull(q.getCacheGroups());
+
+ q.cacheGroups(Arrays.asList("a", "b"));
+ assertNull(q.getCacheStrategy());
+ assertArrayEquals(new String[] { "a", "b" }, q.getCacheGroups());
+ }
+
+ @Test
+ public void testCacheStrategy() {
+ SQLSelect<DataRow> q = SQLSelect.dataRowQuery("bla");
+
+ assertNull(q.getCacheStrategy());
+ assertNull(q.getCacheGroups());
+
+ q.cacheStrategy(QueryCacheStrategy.LOCAL_CACHE, "a", "b");
+ assertSame(QueryCacheStrategy.LOCAL_CACHE, q.getCacheStrategy());
+ assertArrayEquals(new String[] { "a", "b" }, q.getCacheGroups());
+
+ q.cacheStrategy(QueryCacheStrategy.SHARED_CACHE);
+ assertSame(QueryCacheStrategy.SHARED_CACHE, q.getCacheStrategy());
+ assertNull(q.getCacheGroups());
+ }
+
+ @Test
+ public void testLocalCache() {
+ SQLSelect<DataRow> q = SQLSelect.dataRowQuery("bla");
+
+ assertNull(q.getCacheStrategy());
+ assertNull(q.getCacheGroups());
+
+ q.localCache("a", "b");
+ assertSame(QueryCacheStrategy.LOCAL_CACHE, q.getCacheStrategy());
+ assertArrayEquals(new String[] { "a", "b" }, q.getCacheGroups());
+
+ q.localCache();
+ assertSame(QueryCacheStrategy.LOCAL_CACHE, q.getCacheStrategy());
+ assertNull(q.getCacheGroups());
+ }
+
+ @Test
+ public void testSharedCache() {
+ SQLSelect<DataRow> q = SQLSelect.dataRowQuery("bla");
+
+ assertNull(q.getCacheStrategy());
+ assertNull(q.getCacheGroups());
+
+ q.sharedCache("a", "b");
+ assertSame(QueryCacheStrategy.SHARED_CACHE, q.getCacheStrategy());
+ assertArrayEquals(new String[] { "a", "b" }, q.getCacheGroups());
+
+ q.sharedCache();
+ assertSame(QueryCacheStrategy.SHARED_CACHE, q.getCacheStrategy());
+ assertNull(q.getCacheGroups());
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/f9c7d647/docs/doc/src/main/resources/RELEASE-NOTES.txt
----------------------------------------------------------------------
diff --git a/docs/doc/src/main/resources/RELEASE-NOTES.txt b/docs/doc/src/main/resources/RELEASE-NOTES.txt
index ea1e1fd..86ec1c3 100644
--- a/docs/doc/src/main/resources/RELEASE-NOTES.txt
+++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt
@@ -71,6 +71,7 @@ CAY-1962 Implement CayenneTable column resize on double-click on the header sepa
CAY-1965 Change version from 3.2 to 4.0
CAY-1966 SQLTemplate/SQLSelect positional parameter binding
CAY-1967 Deprecate SQLTemplate parameter batches
+CAY-1968 SQLSelect cleanup and omissions
Bug Fixes:
[2/8] cayenne git commit: undeprecating 'isFetchingDataRows' - at
least the getter is still valid
Posted by aa...@apache.org.
undeprecating 'isFetchingDataRows' - at least the getter is still valid
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/e787265f
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/e787265f
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/e787265f
Branch: refs/heads/CAY-1946
Commit: e787265f8964235a9b78ea75acd62d0c6ff59311
Parents: f9c7d64
Author: aadamchik <aa...@apache.org>
Authored: Sat Nov 15 12:07:21 2014 +0300
Committer: aadamchik <aa...@apache.org>
Committed: Sat Nov 15 12:07:21 2014 +0300
----------------------------------------------------------------------
.../src/main/java/org/apache/cayenne/query/SelectQuery.java | 3 ---
1 file changed, 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cayenne/blob/e787265f/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 b7a4fdf..80dba3f 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
@@ -621,9 +621,6 @@ public class SelectQuery<T> extends AbstractQuery implements ParameterizedQuery,
* Returns <code>true</code> if this query should produce a list of data
* rows as opposed to DataObjects, <code>false</code> for DataObjects. This
* is a hint to QueryEngine executing this query.
- *
- * @deprecated since 4.0, use {@link #dataRowQuery(Class, Expression)} to
- * create DataRow query instead.
*/
public boolean isFetchingDataRows() {
return (root instanceof DbEntity) || metaData.isFetchingDataRows();
[5/8] cayenne git commit: ensuring non-null cache strategy
Posted by aa...@apache.org.
ensuring non-null cache strategy
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/5f7b6ddf
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/5f7b6ddf
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/5f7b6ddf
Branch: refs/heads/CAY-1946
Commit: 5f7b6ddf4abcd284d33251c6eabecbfd796445f5
Parents: 7c852a5
Author: aadamchik <aa...@apache.org>
Authored: Sat Nov 15 13:05:58 2014 +0300
Committer: aadamchik <aa...@apache.org>
Committed: Sat Nov 15 13:05:58 2014 +0300
----------------------------------------------------------------------
.../src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cayenne/blob/5f7b6ddf/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java b/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java
index af402b4..1f17aac 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java
@@ -333,7 +333,7 @@ class BaseQueryMetadata implements QueryMetadata, XMLSerializable, Serializable
* @since 3.0
*/
void setCacheStrategy(QueryCacheStrategy cacheStrategy) {
- this.cacheStrategy = cacheStrategy;
+ this.cacheStrategy = cacheStrategy != null ? cacheStrategy : QueryCacheStrategy.getDefaultStrategy();
}
/**
[7/8] cayenne git commit: Merge branch 'master' into CAY-1946
Posted by aa...@apache.org.
Merge branch 'master' into CAY-1946
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/28f5793f
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/28f5793f
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/28f5793f
Branch: refs/heads/CAY-1946
Commit: 28f5793fbfd3890b55e638ca67ace936eb66ebc1
Parents: 29899e4 a0f941a
Author: aadamchik <aa...@apache.org>
Authored: Sun Nov 16 15:02:16 2014 +0300
Committer: aadamchik <aa...@apache.org>
Committed: Sun Nov 16 15:02:16 2014 +0300
----------------------------------------------------------------------
.../java/org/apache/cayenne/exp/Property.java | 19 +-
.../apache/cayenne/query/BaseQueryMetadata.java | 6 +-
.../org/apache/cayenne/query/ObjectSelect.java | 668 ++++++++++++++++++
.../java/org/apache/cayenne/query/Ordering.java | 696 ++++++++++---------
.../apache/cayenne/query/PrefetchTreeNode.java | 15 +
.../org/apache/cayenne/query/SQLSelect.java | 31 +-
.../org/apache/cayenne/query/SelectQuery.java | 13 +-
.../cayenne/query/SelectQueryMetadata.java | 261 +++----
.../cayenne/remote/IncrementalSelectQuery.java | 5 +-
.../apache/cayenne/query/ObjectSelectTest.java | 482 +++++++++++++
.../cayenne/query/ObjectSelect_CompileIT.java | 168 +++++
.../cayenne/query/ObjectSelect_RunIT.java | 94 +++
.../org/apache/cayenne/query/SQLSelectTest.java | 91 +++
.../org/apache/cayenne/query/SelectByIdIT.java | 39 +-
docs/doc/src/main/resources/RELEASE-NOTES.txt | 2 +
15 files changed, 2076 insertions(+), 514 deletions(-)
----------------------------------------------------------------------
[4/8] cayenne git commit: unit test cleanup
Posted by aa...@apache.org.
unit test cleanup
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/7c852a5a
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/7c852a5a
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/7c852a5a
Branch: refs/heads/CAY-1946
Commit: 7c852a5af9abce2f98432903de368f449e942f67
Parents: 8ed6a07
Author: aadamchik <aa...@apache.org>
Authored: Sat Nov 15 12:59:23 2014 +0300
Committer: aadamchik <aa...@apache.org>
Committed: Sat Nov 15 12:59:23 2014 +0300
----------------------------------------------------------------------
.../java/org/apache/cayenne/query/SelectByIdIT.java | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7c852a5a/cayenne-server/src/test/java/org/apache/cayenne/query/SelectByIdIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/SelectByIdIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/SelectByIdIT.java
index bd5a623..0c9cec7 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/SelectByIdIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/SelectByIdIT.java
@@ -22,6 +22,7 @@ import org.apache.cayenne.DataRow;
import org.apache.cayenne.ObjectContext;
import org.apache.cayenne.ObjectId;
import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.test.jdbc.DBHelper;
import org.apache.cayenne.test.jdbc.TableHelper;
import org.apache.cayenne.testdo.testmap.Artist;
@@ -52,6 +53,9 @@ public class SelectByIdIT extends ServerCase {
@Inject
private ObjectContext context;
+ @Inject
+ private EntityResolver resolver;
+
@Override
protected void setUpAfterInjection() throws Exception {
dbHelper.deleteAll("PAINTING_INFO");
@@ -129,13 +133,13 @@ public class SelectByIdIT extends ServerCase {
@Test
public void testMetadataCacheKey() throws Exception {
SelectById<Painting> q1 = SelectById.query(Painting.class, 4).useLocalCache();
- QueryMetadata md1 = q1.getMetaData(context.getEntityResolver());
+ QueryMetadata md1 = q1.getMetaData(resolver);
assertNotNull(md1);
assertNotNull(md1.getCacheKey());
SelectById<Painting> q2 = SelectById.query(Painting.class, singletonMap(Painting.PAINTING_ID_PK_COLUMN, 4))
.useLocalCache();
- QueryMetadata md2 = q2.getMetaData(context.getEntityResolver());
+ QueryMetadata md2 = q2.getMetaData(resolver);
assertNotNull(md2);
assertNotNull(md2.getCacheKey());
@@ -144,20 +148,20 @@ public class SelectByIdIT extends ServerCase {
assertEquals(md1.getCacheKey(), md2.getCacheKey());
SelectById<Painting> q3 = SelectById.query(Painting.class, 5).useLocalCache();
- QueryMetadata md3 = q3.getMetaData(context.getEntityResolver());
+ QueryMetadata md3 = q3.getMetaData(resolver);
assertNotNull(md3);
assertNotNull(md3.getCacheKey());
assertNotEquals(md1.getCacheKey(), md3.getCacheKey());
SelectById<Artist> q4 = SelectById.query(Artist.class, 4).useLocalCache();
- QueryMetadata md4 = q4.getMetaData(context.getEntityResolver());
+ QueryMetadata md4 = q4.getMetaData(resolver);
assertNotNull(md4);
assertNotNull(md4.getCacheKey());
assertNotEquals(md1.getCacheKey(), md4.getCacheKey());
SelectById<Painting> q5 = SelectById.query(Painting.class,
new ObjectId("Painting", Painting.PAINTING_ID_PK_COLUMN, 4)).useLocalCache();
- QueryMetadata md5 = q5.getMetaData(context.getEntityResolver());
+ QueryMetadata md5 = q5.getMetaData(resolver);
assertNotNull(md5);
assertNotNull(md5.getCacheKey());
[3/8] cayenne git commit: refactoring, cleanup
Posted by aa...@apache.org.
refactoring, cleanup
* cleanup cache key generation flow for SelectQuery
* do not generate cache key for null cache strategy
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/8ed6a07b
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/8ed6a07b
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/8ed6a07b
Branch: refs/heads/CAY-1946
Commit: 8ed6a07b2720fdba06d06bc862b936c6eec56b1c
Parents: e787265
Author: aadamchik <aa...@apache.org>
Authored: Sat Nov 15 12:58:07 2014 +0300
Committer: aadamchik <aa...@apache.org>
Committed: Sat Nov 15 12:58:07 2014 +0300
----------------------------------------------------------------------
.../cayenne/query/SelectQueryMetadata.java | 261 ++++++++++---------
.../org/apache/cayenne/query/SelectByIdIT.java | 25 +-
2 files changed, 147 insertions(+), 139 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cayenne/blob/8ed6a07b/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 0367ac4..a05c987 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
@@ -33,131 +33,138 @@ import org.apache.cayenne.map.ObjEntity;
*/
class SelectQueryMetadata extends BaseQueryMetadata {
- Map<String, String> pathSplitAliases;
-
- @Override
- void copyFromInfo(QueryMetadata info) {
- super.copyFromInfo(info);
- this.pathSplitAliases = new HashMap<String, String>(info.getPathSplitAliases());
- }
-
- <T> boolean resolve(Object root, EntityResolver resolver, SelectQuery<T> query) {
-
- if (super.resolve(root, resolver, null)) {
-
- // generate unique cache key...
- if (QueryCacheStrategy.NO_CACHE == getCacheStrategy()) {
-
- } else {
- // create a unique key based on entity, qualifier, ordering and
- // fetch offset and limit
-
- StringBuilder key = new StringBuilder();
-
- ObjEntity entity = getObjEntity();
- if (entity != null) {
- key.append(entity.getName());
- } else if (dbEntity != null) {
- key.append("db:").append(dbEntity.getName());
- }
-
- if (query.getQualifier() != null) {
- key.append('/');
- try {
- query.getQualifier().appendAsString(key);
- } catch (IOException e) {
- throw new CayenneRuntimeException("Unexpected IO Exception appending to StringBuilder", e);
- }
- }
-
- if (!query.getOrderings().isEmpty()) {
- for (Ordering o : query.getOrderings()) {
- key.append('/').append(o.getSortSpecString());
- if (!o.isAscending()) {
- key.append(":d");
- }
-
- if (o.isCaseInsensitive()) {
- key.append(":i");
- }
- }
- }
-
- if (query.getFetchOffset() > 0 || query.getFetchLimit() > 0) {
- key.append('/');
- if (query.getFetchOffset() > 0) {
- key.append('o').append(query.getFetchOffset());
- }
- if (query.getFetchLimit() > 0) {
- key.append('l').append(query.getFetchLimit());
- }
- }
-
- this.cacheKey = key.toString();
- }
-
- resolveAutoAliases(query);
-
- return true;
- }
-
- return false;
- }
-
- private <T> void resolveAutoAliases(SelectQuery<T> query) {
- Expression qualifier = query.getQualifier();
- if (qualifier != null) {
- resolveAutoAliases(qualifier);
- }
-
- // TODO: include aliases in prefetches? flattened attributes?
- }
-
- private void resolveAutoAliases(Expression expression) {
- Map<String, String> aliases = expression.getPathAliases();
- if (!aliases.isEmpty()) {
- if (pathSplitAliases == null) {
- pathSplitAliases = new HashMap<String, String>();
- }
-
- pathSplitAliases.putAll(aliases);
- }
-
- int len = expression.getOperandCount();
- for (int i = 0; i < len; i++) {
- Object operand = expression.getOperand(i);
- if (operand instanceof Expression) {
- resolveAutoAliases((Expression) operand);
- }
- }
- }
-
- /**
- * @since 3.0
- */
- @Override
- public Map<String, String> getPathSplitAliases() {
- return pathSplitAliases != null ? pathSplitAliases : Collections.<String, String> emptyMap();
- }
-
- /**
- * @since 3.0
- */
- public void addPathSplitAliases(String path, String... aliases) {
- if (aliases == null) {
- throw new NullPointerException("Null aliases");
- }
-
- if (aliases.length == 0) {
- throw new IllegalArgumentException("No aliases specified");
- }
-
- if (pathSplitAliases == null) {
- pathSplitAliases = new HashMap<String, String>();
- }
-
- for (String alias : aliases) {
- pathSplitAliases.put(alias, path);
- }
- }
+ private static final long serialVersionUID = 7465922769303943945L;
+
+ Map<String, String> pathSplitAliases;
+
+ @Override
+ void copyFromInfo(QueryMetadata info) {
+ super.copyFromInfo(info);
+ this.pathSplitAliases = new HashMap<String, String>(info.getPathSplitAliases());
+ }
+
+ <T> boolean resolve(Object root, EntityResolver resolver, SelectQuery<T> query) {
+
+ if (super.resolve(root, resolver, null)) {
+
+ // generate unique cache key, but only if we are caching..
+
+ if (cacheStrategy != null && cacheStrategy != QueryCacheStrategy.NO_CACHE) {
+ this.cacheKey = makeCacheKey(query);
+ }
+
+ resolveAutoAliases(query);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private String makeCacheKey(SelectQuery<?> query) {
+
+ // create a unique key based on entity, qualifier, ordering and
+ // fetch offset and limit
+
+ StringBuilder key = new StringBuilder();
+
+ ObjEntity entity = getObjEntity();
+ if (entity != null) {
+ key.append(entity.getName());
+ } else if (dbEntity != null) {
+ key.append("db:").append(dbEntity.getName());
+ }
+
+ if (query.getQualifier() != null) {
+ key.append('/');
+ try {
+ query.getQualifier().appendAsString(key);
+ } catch (IOException e) {
+ throw new CayenneRuntimeException("Unexpected IO Exception appending to StringBuilder", e);
+ }
+ }
+
+ if (!query.getOrderings().isEmpty()) {
+ for (Ordering o : query.getOrderings()) {
+ key.append('/').append(o.getSortSpecString());
+ if (!o.isAscending()) {
+ key.append(":d");
+ }
+
+ if (o.isCaseInsensitive()) {
+ key.append(":i");
+ }
+ }
+ }
+
+ if (query.getFetchOffset() > 0 || query.getFetchLimit() > 0) {
+ key.append('/');
+ if (query.getFetchOffset() > 0) {
+ key.append('o').append(query.getFetchOffset());
+ }
+ if (query.getFetchLimit() > 0) {
+ key.append('l').append(query.getFetchLimit());
+ }
+ }
+
+ return key.toString();
+
+ }
+
+ private <T> void resolveAutoAliases(SelectQuery<T> query) {
+ Expression qualifier = query.getQualifier();
+ if (qualifier != null) {
+ resolveAutoAliases(qualifier);
+ }
+
+ // TODO: include aliases in prefetches? flattened attributes?
+ }
+
+ private void resolveAutoAliases(Expression expression) {
+ Map<String, String> aliases = expression.getPathAliases();
+ if (!aliases.isEmpty()) {
+ if (pathSplitAliases == null) {
+ pathSplitAliases = new HashMap<String, String>();
+ }
+
+ pathSplitAliases.putAll(aliases);
+ }
+
+ int len = expression.getOperandCount();
+ for (int i = 0; i < len; i++) {
+ Object operand = expression.getOperand(i);
+ if (operand instanceof Expression) {
+ resolveAutoAliases((Expression) operand);
+ }
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ @Override
+ public Map<String, String> getPathSplitAliases() {
+ return pathSplitAliases != null ? pathSplitAliases : Collections.<String, String> emptyMap();
+ }
+
+ /**
+ * @since 3.0
+ */
+ public void addPathSplitAliases(String path, String... aliases) {
+ if (aliases == null) {
+ throw new NullPointerException("Null aliases");
+ }
+
+ if (aliases.length == 0) {
+ throw new IllegalArgumentException("No aliases specified");
+ }
+
+ if (pathSplitAliases == null) {
+ pathSplitAliases = new HashMap<String, String>();
+ }
+
+ for (String alias : aliases) {
+ pathSplitAliases.put(alias, path);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/8ed6a07b/cayenne-server/src/test/java/org/apache/cayenne/query/SelectByIdIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/SelectByIdIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/SelectByIdIT.java
index f4e8ef8..bd5a623 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/SelectByIdIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/SelectByIdIT.java
@@ -72,7 +72,7 @@ public class SelectByIdIT extends ServerCase {
tArtist.insert(3, "artist3");
}
- @Test
+ @Test
public void testIntPk() throws Exception {
createTwoArtists();
@@ -85,7 +85,7 @@ public class SelectByIdIT extends ServerCase {
assertEquals("artist2", a2.getArtistName());
}
- @Test
+ @Test
public void testMapPk() throws Exception {
createTwoArtists();
@@ -98,7 +98,7 @@ public class SelectByIdIT extends ServerCase {
assertEquals("artist2", a2.getArtistName());
}
- @Test
+ @Test
public void testObjectIdPk() throws Exception {
createTwoArtists();
@@ -113,7 +113,7 @@ public class SelectByIdIT extends ServerCase {
assertEquals("artist2", a2.getArtistName());
}
- @Test
+ @Test
public void testDataRowIntPk() throws Exception {
createTwoArtists();
@@ -126,14 +126,15 @@ public class SelectByIdIT extends ServerCase {
assertEquals("artist2", a2.get("ARTIST_NAME"));
}
- @Test
+ @Test
public void testMetadataCacheKey() throws Exception {
- SelectById<Painting> q1 = SelectById.query(Painting.class, 4);
+ SelectById<Painting> q1 = SelectById.query(Painting.class, 4).useLocalCache();
QueryMetadata md1 = q1.getMetaData(context.getEntityResolver());
assertNotNull(md1);
assertNotNull(md1.getCacheKey());
- SelectById<Painting> q2 = SelectById.query(Painting.class, singletonMap(Painting.PAINTING_ID_PK_COLUMN, 4));
+ SelectById<Painting> q2 = SelectById.query(Painting.class, singletonMap(Painting.PAINTING_ID_PK_COLUMN, 4))
+ .useLocalCache();
QueryMetadata md2 = q2.getMetaData(context.getEntityResolver());
assertNotNull(md2);
assertNotNull(md2.getCacheKey());
@@ -142,20 +143,20 @@ public class SelectByIdIT extends ServerCase {
// cache entry
assertEquals(md1.getCacheKey(), md2.getCacheKey());
- SelectById<Painting> q3 = SelectById.query(Painting.class, 5);
+ SelectById<Painting> q3 = SelectById.query(Painting.class, 5).useLocalCache();
QueryMetadata md3 = q3.getMetaData(context.getEntityResolver());
assertNotNull(md3);
assertNotNull(md3.getCacheKey());
assertNotEquals(md1.getCacheKey(), md3.getCacheKey());
- SelectById<Artist> q4 = SelectById.query(Artist.class, 4);
+ SelectById<Artist> q4 = SelectById.query(Artist.class, 4).useLocalCache();
QueryMetadata md4 = q4.getMetaData(context.getEntityResolver());
assertNotNull(md4);
assertNotNull(md4.getCacheKey());
assertNotEquals(md1.getCacheKey(), md4.getCacheKey());
- SelectById<Painting> q5 = SelectById.query(Painting.class, new ObjectId("Painting",
- Painting.PAINTING_ID_PK_COLUMN, 4));
+ SelectById<Painting> q5 = SelectById.query(Painting.class,
+ new ObjectId("Painting", Painting.PAINTING_ID_PK_COLUMN, 4)).useLocalCache();
QueryMetadata md5 = q5.getMetaData(context.getEntityResolver());
assertNotNull(md5);
assertNotNull(md5.getCacheKey());
@@ -165,7 +166,7 @@ public class SelectByIdIT extends ServerCase {
assertEquals(md1.getCacheKey(), md5.getCacheKey());
}
- @Test
+ @Test
public void testLocalCache() throws Exception {
createTwoArtists();
[6/8] cayenne git commit: CAY-1959 Chainable API for SelectQuery
Posted by aa...@apache.org.
CAY-1959 Chainable API for SelectQuery
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/a0f941a0
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/a0f941a0
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/a0f941a0
Branch: refs/heads/CAY-1946
Commit: a0f941a02bdc3320e38fd830983b5f034a4a306f
Parents: 5f7b6dd
Author: aadamchik <aa...@apache.org>
Authored: Sat Nov 15 13:08:20 2014 +0300
Committer: aadamchik <aa...@apache.org>
Committed: Sat Nov 15 13:25:11 2014 +0300
----------------------------------------------------------------------
.../java/org/apache/cayenne/exp/Property.java | 19 +-
.../apache/cayenne/query/BaseQueryMetadata.java | 2 +-
.../org/apache/cayenne/query/ObjectSelect.java | 668 ++++++++++++++++++
.../java/org/apache/cayenne/query/Ordering.java | 696 ++++++++++---------
.../apache/cayenne/query/PrefetchTreeNode.java | 15 +
.../org/apache/cayenne/query/SelectQuery.java | 10 +-
.../cayenne/remote/IncrementalSelectQuery.java | 5 +-
.../apache/cayenne/query/ObjectSelectTest.java | 482 +++++++++++++
.../cayenne/query/ObjectSelect_CompileIT.java | 168 +++++
.../cayenne/query/ObjectSelect_RunIT.java | 94 +++
docs/doc/src/main/resources/RELEASE-NOTES.txt | 1 +
11 files changed, 1805 insertions(+), 355 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cayenne/blob/a0f941a0/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 48f1549..b42e890 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
@@ -385,9 +385,7 @@ public class Property<E> {
* prefetch semantics.
*/
public PrefetchTreeNode joint() {
- PrefetchTreeNode node = prefetch();
- node.setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
- return node.getRoot();
+ return PrefetchTreeNode.withPath(name, PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
}
/**
@@ -396,9 +394,7 @@ public class Property<E> {
* "disjoint" prefetch semantics.
*/
public PrefetchTreeNode disjoint() {
- PrefetchTreeNode node = prefetch();
- node.setSemantics(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS);
- return node.getRoot();
+ return PrefetchTreeNode.withPath(name, PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS);
}
/**
@@ -407,16 +403,7 @@ public class Property<E> {
* "disjoint by id" prefetch semantics.
*/
public PrefetchTreeNode disjointById() {
- PrefetchTreeNode node = prefetch();
- node.setSemantics(PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS);
- return node.getRoot();
- }
-
- PrefetchTreeNode prefetch() {
- PrefetchTreeNode root = new PrefetchTreeNode();
- PrefetchTreeNode node = root.addPath(name);
- node.setPhantom(false);
- return node;
+ return PrefetchTreeNode.withPath(name, PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS);
}
/**
http://git-wip-us.apache.org/repos/asf/cayenne/blob/a0f941a0/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java b/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java
index 1f17aac..d85b85a 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java
@@ -44,7 +44,7 @@ import org.apache.cayenne.util.XMLSerializable;
class BaseQueryMetadata implements QueryMetadata, XMLSerializable, Serializable {
private static final long serialVersionUID = 5129792493303459115L;
-
+
int fetchLimit = QueryMetadata.FETCH_LIMIT_DEFAULT;
int fetchOffset = QueryMetadata.FETCH_OFFSET_DEFAULT;
http://git-wip-us.apache.org/repos/asf/cayenne/blob/a0f941a0/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
new file mode 100644
index 0000000..7618bcc
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/ObjectSelect.java
@@ -0,0 +1,668 @@
+/*****************************************************************
+ * 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.List;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.DataRow;
+import org.apache.cayenne.ObjectContext;
+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;
+
+/**
+ * A selecting query providing chainable API. Can be viewed as an alternative to
+ * {@link SelectQuery}.
+ *
+ * @since 4.0
+ */
+public class ObjectSelect<T> extends IndirectQuery implements Select<T> {
+
+ private static final long serialVersionUID = -156124021150949227L;
+
+ private boolean fetchingDataRows;
+
+ private Class<?> entityType;
+ private String entityName;
+ private String dbEntityName;
+ private Expression exp;
+ 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;
+
+ /**
+ * Creates a ObjectSelect that selects objects of a given persistent class.
+ */
+ public static <T> ObjectSelect<T> query(Class<T> entityType) {
+ return new ObjectSelect<T>().entityType(entityType);
+ }
+
+ /**
+ * Creates a ObjectSelect that selects objects of a given persistent class
+ * and uses provided expression for its qualifier.
+ */
+ public static <T> ObjectSelect<T> query(Class<T> entityType, Expression expression) {
+ return new ObjectSelect<T>().entityType(entityType).exp(expression);
+ }
+
+ /**
+ * Creates a ObjectSelect that selects objects of a given persistent class
+ * and uses provided expression for its qualifier.
+ */
+ public static <T> ObjectSelect<T> query(Class<T> entityType, Expression expression, List<Ordering> orderings) {
+ return new ObjectSelect<T>().entityType(entityType).exp(expression).orderBy(orderings);
+ }
+
+ /**
+ * Creates a ObjectSelect that fetches data for an {@link ObjEntity}
+ * determined from a provided class.
+ */
+ public static ObjectSelect<DataRow> dataRowQuery(Class<?> entityType) {
+ return query(entityType).fetchDataRows();
+ }
+
+ /**
+ * Creates a ObjectSelect that fetches data for an {@link ObjEntity}
+ * determined from a provided class and uses provided expression for its
+ * qualifier.
+ */
+ public static ObjectSelect<DataRow> dataRowQuery(Class<?> entityType, Expression expression) {
+ return query(entityType).fetchDataRows().exp(expression);
+ }
+
+ /**
+ * Creates a ObjectSelect that fetches data for {@link ObjEntity} determined
+ * from provided "entityName", but fetches the result of a provided type.
+ * This factory method is most often used with generic classes that by
+ * themselves are not enough to resolve the entity to fetch.
+ */
+ public static <T> ObjectSelect<T> query(Class<T> resultType, String entityName) {
+ return new ObjectSelect<T>().entityName(entityName);
+ }
+
+ /**
+ * Creates a ObjectSelect that fetches DataRows for a {@link DbEntity}
+ * determined from provided "dbEntityName".
+ */
+ public static ObjectSelect<DataRow> dbQuery(String dbEntityName) {
+ return new ObjectSelect<Object>().fetchDataRows().dbEntityName(dbEntityName);
+ }
+
+ /**
+ * Creates a ObjectSelect that fetches DataRows for a {@link DbEntity}
+ * determined from provided "dbEntityName" and uses provided expression for
+ * its qualifier.
+ *
+ * @return this object
+ */
+ public static ObjectSelect<DataRow> dbQuery(String dbEntityName, Expression expression) {
+ return new ObjectSelect<Object>().fetchDataRows().dbEntityName(dbEntityName).exp(expression);
+ }
+
+ protected ObjectSelect() {
+ }
+
+ /**
+ * 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.setFetchingDataRows(fetchingDataRows);
+ replacement.setQualifier(exp);
+ 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 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".
+ *
+ * @return this object
+ */
+ @SuppressWarnings("unchecked")
+ public ObjectSelect<DataRow> fetchDataRows() {
+ this.fetchingDataRows = true;
+ return (ObjectSelect<DataRow>) this;
+ }
+
+ /**
+ * Initializes or resets a qualifier expression of this query.
+ *
+ * @return this object
+ */
+ public ObjectSelect<T> exp(Expression expression) {
+ this.exp = expression;
+ return this;
+ }
+
+ /**
+ * Initializes or resets a qualifier expression of this query, using
+ * provided expression String and an array of position parameters.
+ *
+ * @return this object
+ */
+ public ObjectSelect<T> exp(String expressionString, Object... parameters) {
+ this.exp = ExpressionFactory.exp(expressionString, parameters);
+ return this;
+ }
+
+ /**
+ * AND's provided expressions to the existing qualifier 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 qualifier expression.
+ *
+ * @return this object
+ */
+ public ObjectSelect<T> and(Collection<Expression> expressions) {
+
+ if (expressions == null || expressions.isEmpty()) {
+ return this;
+ }
+
+ Collection<Expression> all;
+
+ if (exp != null) {
+ all = new ArrayList<Expression>(expressions.size() + 1);
+ all.add(exp);
+ all.addAll(expressions);
+ } else {
+ all = expressions;
+ }
+
+ exp = ExpressionFactory.and(all);
+ return this;
+ }
+
+ /**
+ * OR's provided expressions to the existing qualifier 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 qualifier expression.
+ *
+ * @return this object
+ */
+ public ObjectSelect<T> or(Collection<Expression> expressions) {
+ if (expressions == null || expressions.isEmpty()) {
+ return this;
+ }
+
+ Collection<Expression> all;
+
+ if (exp != null) {
+ all = new ArrayList<Expression>(expressions.size() + 1);
+ all.add(exp);
+ all.addAll(expressions);
+ } else {
+ all = expressions;
+ }
+
+ exp = ExpressionFactory.or(all);
+ return this;
+ }
+
+ /**
+ * Initializes ordering clause of this query with a single ascending
+ * ordering on a given property.
+ *
+ * @return this object
+ */
+ public ObjectSelect<T> orderBy(String property) {
+ return orderBy(new Ordering(property));
+ }
+
+ /**
+ * Initializes ordering clause of this query with a single ordering on a
+ * given property.
+ *
+ * @return this object
+ */
+ public ObjectSelect<T> orderBy(String property, SortOrder sortOrder) {
+ return orderBy(new Ordering(property, sortOrder));
+ }
+
+ /**
+ * Initializes or resets a list of orderings of this query.
+ *
+ * @return this object
+ */
+ public ObjectSelect<T> orderBy(Ordering... orderings) {
+
+ if (this.orderings != null) {
+ this.orderings.clear();
+ }
+
+ return addOrderBy(orderings);
+ }
+
+ /**
+ * Initializes or resets a list of orderings of this query.
+ *
+ * @return this object
+ */
+ public ObjectSelect<T> orderBy(Collection<Ordering> orderings) {
+
+ if (this.orderings != null) {
+ this.orderings.clear();
+ }
+
+ return addOrderBy(orderings);
+ }
+
+ /**
+ * Adds a single ascending ordering on a given property to the existing
+ * ordering clause of this query.
+ *
+ * @return this object
+ */
+ public ObjectSelect<T> addOrderBy(String property) {
+ return addOrderBy(new Ordering(property));
+ }
+
+ /**
+ * Adds a single ordering on a given property to the existing ordering
+ * clause of this query.
+ *
+ * @return this object
+ */
+ public ObjectSelect<T> addOrderBy(String property, SortOrder sortOrder) {
+ return addOrderBy(new Ordering(property, sortOrder));
+ }
+
+ /**
+ * Adds new orderings to the list of the existing orderings.
+ *
+ * @return this object
+ */
+ public ObjectSelect<T> addOrderBy(Ordering... orderings) {
+
+ if (orderings == null || orderings == null) {
+ return this;
+ }
+
+ if (this.orderings == null) {
+ this.orderings = new ArrayList<Ordering>(orderings.length);
+ }
+
+ for (Ordering o : orderings) {
+ this.orderings.add(o);
+ }
+
+ return this;
+ }
+
+ /**
+ * Adds new orderings to the list of orderings.
+ *
+ * @return this object
+ */
+ public ObjectSelect<T> addOrderBy(Collection<Ordering> orderings) {
+
+ if (orderings == null || orderings == null) {
+ return this;
+ }
+
+ if (this.orderings == null) {
+ this.orderings = new ArrayList<Ordering>(orderings.size());
+ }
+
+ this.orderings.addAll(orderings);
+
+ return this;
+ }
+
+ /**
+ * Resets internal prefetches to the new value, which is a single prefetch
+ * with specified semantics.
+ *
+ * @return this object
+ */
+ public ObjectSelect<T> prefetch(String path, int semantics) {
+ this.prefetches = PrefetchTreeNode.withPath(path, semantics);
+ return this;
+ }
+
+ /**
+ * Resets internal prefetches to the new value.
+ *
+ * @return this object
+ */
+ public ObjectSelect<T> prefetch(PrefetchTreeNode prefetch) {
+ this.prefetches = prefetch;
+ return this;
+ }
+
+ /**
+ * Merges prefetch into the query prefetch tree.
+ *
+ * @return this object
+ */
+ public ObjectSelect<T> addPrefetch(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> addPrefetch(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:
+ *
+ * <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:
+ *
+ * <pre>
+ * query.cacheStrategy(QueryCacheStrategy.SHARED_CACHE, cacheGroups);
+ * </pre>
+ */
+ public ObjectSelect<T> 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 boolean isFetchingDataRows() {
+ return fetchingDataRows;
+ }
+
+ public Class<?> getEntityType() {
+ return entityType;
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public String getDbEntityName() {
+ return dbEntityName;
+ }
+
+ public Expression getExp() {
+ return exp;
+ }
+
+ public Collection<Ordering> getOrderings() {
+ return orderings;
+ }
+
+ public PrefetchTreeNode getPrefetches() {
+ return prefetches;
+ }
+
+ /**
+ * Selects objects using provided context. Essentially the inversion of
+ * "ObjectContext.select(query)".
+ */
+ public List<T> select(ObjectContext context) {
+ return context.select(this);
+ }
+
+ /**
+ * Selects a single object using provided context. Essentially the inversion
+ * of "ObjectContext.selectOne(Select)".
+ */
+ public T selectOne(ObjectContext context) {
+ return context.selectOne(this);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/a0f941a0/cayenne-server/src/main/java/org/apache/cayenne/query/Ordering.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/Ordering.java b/cayenne-server/src/main/java/org/apache/cayenne/query/Ordering.java
index 08c8544..ccd7a58 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/Ordering.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/Ordering.java
@@ -37,339 +37,371 @@ import org.apache.cayenne.util.XMLSerializable;
import org.apache.commons.collections.ComparatorUtils;
/**
- * Defines object sorting criteria, used either for in-memory sorting of object lists or
- * as a specification for building <em>ORDER BY</em> clause of a SelectQuery query. Note
- * that in case of in-memory sorting, Ordering can be used with any JavaBeans, not just
- * DataObjects.
+ * Defines object sorting criteria, used either for in-memory sorting of object
+ * lists or as a specification for building <em>ORDER BY</em> clause of a
+ * SelectQuery query. Note that in case of in-memory sorting, Ordering can be
+ * used with any JavaBeans, not just DataObjects.
*/
public class Ordering implements Comparator<Object>, Serializable, XMLSerializable {
- protected String sortSpecString;
- protected transient Expression sortSpec;
- protected SortOrder sortOrder;
- protected boolean pathExceptionSuppressed = false;
- protected boolean nullSortedFirst = true;
-
- /**
- * Orders a given list of objects, using a List of Orderings applied according the
- * default iteration order of the Orderings list. I.e. each Ordering with lower index
- * is more significant than any other Ordering with higher index. List being ordered
- * is modified in place.
- */
- public static void orderList(List<?> objects, List<? extends Ordering> orderings) {
- Collections.sort(objects, ComparatorUtils.chainedComparator(orderings));
- }
-
- public Ordering() {
- }
-
- /**
- * @since 3.0
- */
- public Ordering(String sortPathSpec, SortOrder sortOrder) {
- setSortSpecString(sortPathSpec);
- setSortOrder(sortOrder);
- }
-
- /**
- * Sets sortSpec to be an expression represented by string argument.
- *
- * @since 1.1
- */
- public void setSortSpecString(String sortSpecString) {
- if (!Util.nullSafeEquals(this.sortSpecString, sortSpecString)) {
- this.sortSpecString = sortSpecString;
- this.sortSpec = null;
- }
- }
-
- /**
- * Sets sort order for whether nulls are at the top or bottom of the resulting list.
- * Default is true.
- *
- * @param nullSortedFirst true sorts nulls to the top of the list, false sorts nulls
- * to the bottom
- */
- public void setNullSortedFirst(boolean nullSortedFirst) {
- this.nullSortedFirst = nullSortedFirst;
- }
-
- /**
- * Get sort order for nulls.
- *
- * @return true if nulls are sorted to the top of the list, false if sorted to the
- * bottom
- */
- public boolean isNullSortedFirst() {
- return nullSortedFirst;
- }
-
- /**
- * Sets whether a path with a null in the middle is ignored. For example, a sort from
- * <code>painting</code> on <code>artist.name</code> would by default throw an
- * exception if the artist was null. If set to true, then this is treated just like a
- * null value. Default is false.
- *
- * @param pathExceptionSuppressed true to suppress exceptions and sort as null
- */
- public void setPathExceptionSupressed(boolean pathExceptionSuppressed) {
- this.pathExceptionSuppressed = pathExceptionSuppressed;
- }
-
- /**
- * Is a path with a null in the middle is ignored.
- *
- * @return true is exception is suppressed and sorted as null
- */
- public boolean isPathExceptionSuppressed() {
- return pathExceptionSuppressed;
- }
-
- /**
- * Returns sortSpec string representation.
- *
- * @since 1.1
- */
- public String getSortSpecString() {
- return sortSpecString;
- }
-
- /**
- * Sets the sort order for this ordering.
- *
- * @since 3.0
- */
- public void setSortOrder(SortOrder order) {
- this.sortOrder = order;
- }
-
- /** Returns true if sorting is done in ascending order. */
- public boolean isAscending() {
- return sortOrder == null
- || sortOrder == SortOrder.ASCENDING
- || sortOrder == SortOrder.ASCENDING_INSENSITIVE;
- }
-
- /**
- * Returns true if the sorting is done in descending order.
- *
- * @since 3.0
- */
- public boolean isDescending() {
- return !isAscending();
- }
-
- /**
- * If the sort order is DESCENDING or DESCENDING_INSENSITIVE, sets the sort order to
- * ASCENDING or ASCENDING_INSENSITIVE, respectively.
- *
- * @since 3.0
- */
- public void setAscending() {
- if (sortOrder == null || sortOrder == SortOrder.DESCENDING)
- setSortOrder(SortOrder.ASCENDING);
- else if (sortOrder == SortOrder.DESCENDING_INSENSITIVE)
- setSortOrder(SortOrder.ASCENDING_INSENSITIVE);
- }
-
- /**
- * If the sort order is ASCENDING or ASCENDING_INSENSITIVE, sets the sort order to
- * DESCENDING or DESCENDING_INSENSITIVE, respectively.
- *
- * @since 3.0
- */
- public void setDescending() {
- if (sortOrder == null || sortOrder == SortOrder.ASCENDING)
- setSortOrder(SortOrder.DESCENDING);
- else if (sortOrder == SortOrder.ASCENDING_INSENSITIVE)
- setSortOrder(SortOrder.DESCENDING_INSENSITIVE);
- }
-
- /** Returns true if the sorting is case insensitive */
- public boolean isCaseInsensitive() {
- return !isCaseSensitive();
- }
-
- /**
- * Returns true if the sorting is case sensitive.
- *
- * @since 3.0
- */
- public boolean isCaseSensitive() {
- return sortOrder == null
- || sortOrder == SortOrder.ASCENDING
- || sortOrder == SortOrder.DESCENDING;
- }
-
- /**
- * If the sort order is ASCENDING or DESCENDING, sets the sort order to
- * ASCENDING_INSENSITIVE or DESCENDING_INSENSITIVE, respectively.
- *
- * @since 3.0
- */
- public void setCaseInsensitive() {
- if (sortOrder == null || sortOrder == SortOrder.ASCENDING)
- setSortOrder(SortOrder.ASCENDING_INSENSITIVE);
- else if (sortOrder == SortOrder.DESCENDING)
- setSortOrder(SortOrder.DESCENDING_INSENSITIVE);
- }
-
- /**
- * If the sort order is ASCENDING_INSENSITIVE or DESCENDING_INSENSITIVE, sets the sort
- * order to ASCENDING or DESCENDING, respectively.
- *
- * @since 3.0
- */
- public void setCaseSensitive() {
- if (sortOrder == null || sortOrder == SortOrder.ASCENDING_INSENSITIVE)
- setSortOrder(SortOrder.ASCENDING);
- else if (sortOrder == SortOrder.DESCENDING_INSENSITIVE)
- setSortOrder(SortOrder.DESCENDING);
- }
-
- /**
- * Returns the expression defining a ordering Java Bean property.
- */
- public Expression getSortSpec() {
- if (sortSpecString == null) {
- return null;
- }
-
- // compile on demand .. since orderings can only be paths, avoid the overhead of
- // Expression.fromString, and parse them manually
- if (sortSpec == null) {
-
- if (sortSpecString.startsWith(ASTDbPath.DB_PREFIX)) {
- sortSpec = new ASTDbPath(sortSpecString.substring(ASTDbPath.DB_PREFIX
- .length()));
- }
- else if (sortSpecString.startsWith(ASTObjPath.OBJ_PREFIX)) {
- sortSpec = new ASTObjPath(sortSpecString.substring(ASTObjPath.OBJ_PREFIX
- .length()));
- }
- else {
- sortSpec = new ASTObjPath(sortSpecString);
- }
- }
-
- return sortSpec;
- }
-
- /**
- * Sets the expression defining a ordering Java Bean property.
- */
- public void setSortSpec(Expression sortSpec) {
- this.sortSpec = sortSpec;
- this.sortSpecString = (sortSpec != null) ? sortSpec.toString() : null;
- }
-
- /**
- * Orders the given list of objects according to the ordering that this object
- * specifies. List is modified in-place.
- *
- * @param objects a List of objects to be sorted
- */
- public void orderList(List<?> objects) {
- Collections.sort(objects, this);
- }
-
- /**
- * Comparable interface implementation. Can compare two Java Beans based on the stored
- * expression.
- */
- public int compare(Object o1, Object o2) {
- Expression exp = getSortSpec();
- Object value1 = null;
- Object value2 = null;
- try {
- value1 = exp.evaluate(o1);
- }
- catch (ExpressionException e) {
- if (pathExceptionSuppressed
- && e.getCause() instanceof org.apache.cayenne.reflect.UnresolvablePathException) {
- // do nothing, we expect this
- }
- else {
- // re-throw
- throw e;
- }
- }
-
- try {
- value2 = exp.evaluate(o2);
- }
- catch (ExpressionException e) {
- if (pathExceptionSuppressed
- && e.getCause() instanceof org.apache.cayenne.reflect.UnresolvablePathException) {
- // do nothing, we expect this
- }
- else {
- // rethrow
- throw e;
- }
- }
-
- if (value1 == null && value2 == null) {
- return 0;
- }
- else if (value1 == null) {
- return nullSortedFirst ? -1 : 1;
- }
- else if (value2 == null) {
- return nullSortedFirst ? 1 : -1;
- }
-
- if (isCaseInsensitive()) {
- // TODO: to upper case should probably be defined as a separate expression
- // type
- value1 = ConversionUtil.toUpperCase(value1);
- value2 = ConversionUtil.toUpperCase(value2);
- }
-
- int compareResult = ConversionUtil.toComparable(value1).compareTo(
- ConversionUtil.toComparable(value2));
- return (isAscending()) ? compareResult : -compareResult;
- }
-
- /**
- * Encodes itself as a query ordering.
- *
- * @since 1.1
- */
- public void encodeAsXML(XMLEncoder encoder) {
- encoder.print("<ordering");
-
- if (isDescending()) {
- encoder.print(" descending=\"true\"");
- }
-
- if (isCaseInsensitive()) {
- encoder.print(" ignore-case=\"true\"");
- }
-
- encoder.print(">");
- if (getSortSpec() != null) {
- getSortSpec().encodeAsXML(encoder);
- }
- encoder.println("</ordering>");
- }
-
- @Override
- public String toString() {
- StringWriter buffer = new StringWriter();
- PrintWriter pw = new PrintWriter(buffer);
- XMLEncoder encoder = new XMLEncoder(pw);
- encodeAsXML(encoder);
- pw.close();
- buffer.flush();
- return buffer.toString();
- }
-
- /**
- * Returns sort order for this ordering
- * @since 3.1
- */
- public SortOrder getSortOrder() {
- return sortOrder;
- }
+ private static final long serialVersionUID = -9167074787055881422L;
+
+ protected String sortSpecString;
+ protected transient Expression sortSpec;
+ protected SortOrder sortOrder;
+ protected boolean pathExceptionSuppressed = false;
+ protected boolean nullSortedFirst = true;
+
+ /**
+ * Orders a given list of objects, using a List of Orderings applied
+ * according the default iteration order of the Orderings list. I.e. each
+ * Ordering with lower index is more significant than any other Ordering
+ * with higher index. List being ordered is modified in place.
+ */
+ public static void orderList(List<?> objects, List<? extends Ordering> orderings) {
+ Collections.sort(objects, ComparatorUtils.chainedComparator(orderings));
+ }
+
+ public Ordering() {
+ }
+
+ /**
+ * Create an ordering instance with a provided path and ascending sorting
+ * strategy.
+ *
+ * @since 4.0
+ */
+ public Ordering(String sortPathSpec) {
+ this(sortPathSpec, SortOrder.ASCENDING);
+ }
+
+ /**
+ * @since 3.0
+ */
+ public Ordering(String sortPathSpec, SortOrder sortOrder) {
+ setSortSpecString(sortPathSpec);
+ setSortOrder(sortOrder);
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+
+ if (!(object instanceof Ordering)) {
+ return false;
+ }
+
+ Ordering o = (Ordering) object;
+
+ if (!Util.nullSafeEquals(sortSpecString, o.sortSpecString)) {
+ return false;
+ }
+
+ if (sortOrder != o.sortOrder) {
+ return false;
+ }
+
+ if (pathExceptionSuppressed != o.pathExceptionSuppressed) {
+ return false;
+ }
+
+ if (nullSortedFirst != o.nullSortedFirst) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Sets sortSpec to be an expression represented by string argument.
+ *
+ * @since 1.1
+ */
+ public void setSortSpecString(String sortSpecString) {
+ if (!Util.nullSafeEquals(this.sortSpecString, sortSpecString)) {
+ this.sortSpecString = sortSpecString;
+ this.sortSpec = null;
+ }
+ }
+
+ /**
+ * Sets sort order for whether nulls are at the top or bottom of the
+ * resulting list. Default is true.
+ *
+ * @param nullSortedFirst
+ * true sorts nulls to the top of the list, false sorts nulls to
+ * the bottom
+ */
+ public void setNullSortedFirst(boolean nullSortedFirst) {
+ this.nullSortedFirst = nullSortedFirst;
+ }
+
+ /**
+ * Get sort order for nulls.
+ *
+ * @return true if nulls are sorted to the top of the list, false if sorted
+ * to the bottom
+ */
+ public boolean isNullSortedFirst() {
+ return nullSortedFirst;
+ }
+
+ /**
+ * Sets whether a path with a null in the middle is ignored. For example, a
+ * sort from <code>painting</code> on <code>artist.name</code> would by
+ * default throw an exception if the artist was null. If set to true, then
+ * this is treated just like a null value. Default is false.
+ *
+ * @param pathExceptionSuppressed
+ * true to suppress exceptions and sort as null
+ */
+ public void setPathExceptionSupressed(boolean pathExceptionSuppressed) {
+ this.pathExceptionSuppressed = pathExceptionSuppressed;
+ }
+
+ /**
+ * Is a path with a null in the middle is ignored.
+ *
+ * @return true is exception is suppressed and sorted as null
+ */
+ public boolean isPathExceptionSuppressed() {
+ return pathExceptionSuppressed;
+ }
+
+ /**
+ * Returns sortSpec string representation.
+ *
+ * @since 1.1
+ */
+ public String getSortSpecString() {
+ return sortSpecString;
+ }
+
+ /**
+ * Sets the sort order for this ordering.
+ *
+ * @since 3.0
+ */
+ public void setSortOrder(SortOrder order) {
+ this.sortOrder = order;
+ }
+
+ /** Returns true if sorting is done in ascending order. */
+ public boolean isAscending() {
+ return sortOrder == null || sortOrder == SortOrder.ASCENDING || sortOrder == SortOrder.ASCENDING_INSENSITIVE;
+ }
+
+ /**
+ * Returns true if the sorting is done in descending order.
+ *
+ * @since 3.0
+ */
+ public boolean isDescending() {
+ return !isAscending();
+ }
+
+ /**
+ * If the sort order is DESCENDING or DESCENDING_INSENSITIVE, sets the sort
+ * order to ASCENDING or ASCENDING_INSENSITIVE, respectively.
+ *
+ * @since 3.0
+ */
+ public void setAscending() {
+ if (sortOrder == null || sortOrder == SortOrder.DESCENDING)
+ setSortOrder(SortOrder.ASCENDING);
+ else if (sortOrder == SortOrder.DESCENDING_INSENSITIVE)
+ setSortOrder(SortOrder.ASCENDING_INSENSITIVE);
+ }
+
+ /**
+ * If the sort order is ASCENDING or ASCENDING_INSENSITIVE, sets the sort
+ * order to DESCENDING or DESCENDING_INSENSITIVE, respectively.
+ *
+ * @since 3.0
+ */
+ public void setDescending() {
+ if (sortOrder == null || sortOrder == SortOrder.ASCENDING)
+ setSortOrder(SortOrder.DESCENDING);
+ else if (sortOrder == SortOrder.ASCENDING_INSENSITIVE)
+ setSortOrder(SortOrder.DESCENDING_INSENSITIVE);
+ }
+
+ /** Returns true if the sorting is case insensitive */
+ public boolean isCaseInsensitive() {
+ return !isCaseSensitive();
+ }
+
+ /**
+ * Returns true if the sorting is case sensitive.
+ *
+ * @since 3.0
+ */
+ public boolean isCaseSensitive() {
+ return sortOrder == null || sortOrder == SortOrder.ASCENDING || sortOrder == SortOrder.DESCENDING;
+ }
+
+ /**
+ * If the sort order is ASCENDING or DESCENDING, sets the sort order to
+ * ASCENDING_INSENSITIVE or DESCENDING_INSENSITIVE, respectively.
+ *
+ * @since 3.0
+ */
+ public void setCaseInsensitive() {
+ if (sortOrder == null || sortOrder == SortOrder.ASCENDING)
+ setSortOrder(SortOrder.ASCENDING_INSENSITIVE);
+ else if (sortOrder == SortOrder.DESCENDING)
+ setSortOrder(SortOrder.DESCENDING_INSENSITIVE);
+ }
+
+ /**
+ * If the sort order is ASCENDING_INSENSITIVE or DESCENDING_INSENSITIVE,
+ * sets the sort order to ASCENDING or DESCENDING, respectively.
+ *
+ * @since 3.0
+ */
+ public void setCaseSensitive() {
+ if (sortOrder == null || sortOrder == SortOrder.ASCENDING_INSENSITIVE)
+ setSortOrder(SortOrder.ASCENDING);
+ else if (sortOrder == SortOrder.DESCENDING_INSENSITIVE)
+ setSortOrder(SortOrder.DESCENDING);
+ }
+
+ /**
+ * Returns the expression defining a ordering Java Bean property.
+ */
+ public Expression getSortSpec() {
+ if (sortSpecString == null) {
+ return null;
+ }
+
+ // compile on demand .. since orderings can only be paths, avoid the
+ // overhead of
+ // Expression.fromString, and parse them manually
+ if (sortSpec == null) {
+
+ if (sortSpecString.startsWith(ASTDbPath.DB_PREFIX)) {
+ sortSpec = new ASTDbPath(sortSpecString.substring(ASTDbPath.DB_PREFIX.length()));
+ } else if (sortSpecString.startsWith(ASTObjPath.OBJ_PREFIX)) {
+ sortSpec = new ASTObjPath(sortSpecString.substring(ASTObjPath.OBJ_PREFIX.length()));
+ } else {
+ sortSpec = new ASTObjPath(sortSpecString);
+ }
+ }
+
+ return sortSpec;
+ }
+
+ /**
+ * Sets the expression defining a ordering Java Bean property.
+ */
+ public void setSortSpec(Expression sortSpec) {
+ this.sortSpec = sortSpec;
+ this.sortSpecString = (sortSpec != null) ? sortSpec.toString() : null;
+ }
+
+ /**
+ * Orders the given list of objects according to the ordering that this
+ * object specifies. List is modified in-place.
+ *
+ * @param objects
+ * a List of objects to be sorted
+ */
+ public void orderList(List<?> objects) {
+ Collections.sort(objects, this);
+ }
+
+ /**
+ * Comparable interface implementation. Can compare two Java Beans based on
+ * the stored expression.
+ */
+ public int compare(Object o1, Object o2) {
+ Expression exp = getSortSpec();
+ Object value1 = null;
+ Object value2 = null;
+ try {
+ value1 = exp.evaluate(o1);
+ } catch (ExpressionException e) {
+ if (pathExceptionSuppressed && e.getCause() instanceof org.apache.cayenne.reflect.UnresolvablePathException) {
+ // do nothing, we expect this
+ } else {
+ // re-throw
+ throw e;
+ }
+ }
+
+ try {
+ value2 = exp.evaluate(o2);
+ } catch (ExpressionException e) {
+ if (pathExceptionSuppressed && e.getCause() instanceof org.apache.cayenne.reflect.UnresolvablePathException) {
+ // do nothing, we expect this
+ } else {
+ // rethrow
+ throw e;
+ }
+ }
+
+ if (value1 == null && value2 == null) {
+ return 0;
+ } else if (value1 == null) {
+ return nullSortedFirst ? -1 : 1;
+ } else if (value2 == null) {
+ return nullSortedFirst ? 1 : -1;
+ }
+
+ if (isCaseInsensitive()) {
+ // TODO: to upper case should probably be defined as a separate
+ // expression
+ // type
+ value1 = ConversionUtil.toUpperCase(value1);
+ value2 = ConversionUtil.toUpperCase(value2);
+ }
+
+ int compareResult = ConversionUtil.toComparable(value1).compareTo(ConversionUtil.toComparable(value2));
+ return (isAscending()) ? compareResult : -compareResult;
+ }
+
+ /**
+ * Encodes itself as a query ordering.
+ *
+ * @since 1.1
+ */
+ public void encodeAsXML(XMLEncoder encoder) {
+ encoder.print("<ordering");
+
+ if (isDescending()) {
+ encoder.print(" descending=\"true\"");
+ }
+
+ if (isCaseInsensitive()) {
+ encoder.print(" ignore-case=\"true\"");
+ }
+
+ encoder.print(">");
+ if (getSortSpec() != null) {
+ getSortSpec().encodeAsXML(encoder);
+ }
+ encoder.println("</ordering>");
+ }
+
+ @Override
+ public String toString() {
+ StringWriter buffer = new StringWriter();
+ PrintWriter pw = new PrintWriter(buffer);
+ XMLEncoder encoder = new XMLEncoder(pw);
+ encodeAsXML(encoder);
+ pw.close();
+ buffer.flush();
+ return buffer.toString();
+ }
+
+ /**
+ * Returns sort order for this ordering
+ *
+ * @since 3.1
+ */
+ public SortOrder getSortOrder() {
+ return sortOrder;
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/a0f941a0/cayenne-server/src/main/java/org/apache/cayenne/query/PrefetchTreeNode.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/PrefetchTreeNode.java b/cayenne-server/src/main/java/org/apache/cayenne/query/PrefetchTreeNode.java
index 11711fc..e44b08d 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/PrefetchTreeNode.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/PrefetchTreeNode.java
@@ -63,6 +63,21 @@ public class PrefetchTreeNode implements Serializable, XMLSerializable {
protected Collection<PrefetchTreeNode> children;
/**
+ * Creates and returns a prefetch tree spanning a single path. The tree is
+ * made of phantom nodes, up to the leaf node, which is non-phantom and has
+ * specified semantics.
+ *
+ * @since 4.0
+ */
+ public static PrefetchTreeNode withPath(String path, int semantics) {
+ PrefetchTreeNode root = new PrefetchTreeNode();
+ PrefetchTreeNode node = root.addPath(path);
+ node.setPhantom(false);
+ node.setSemantics(semantics);
+ return root;
+ }
+
+ /**
* Creates a root node of the prefetch tree. Children can be added to the
* parent by calling "addPath".
*/
http://git-wip-us.apache.org/repos/asf/cayenne/blob/a0f941a0/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 80dba3f..8618bed 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
@@ -20,6 +20,7 @@
package org.apache.cayenne.query;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -44,7 +45,7 @@ import org.apache.cayenne.util.XMLSerializable;
public class SelectQuery<T> extends AbstractQuery implements ParameterizedQuery, XMLSerializable, Select<T> {
private static final long serialVersionUID = 5486418811888197559L;
-
+
public static final String DISTINCT_PROPERTY = "cayenne.SelectQuery.distinct";
public static final boolean DISTINCT_DEFAULT = false;
@@ -500,11 +501,12 @@ public class SelectQuery<T> extends AbstractQuery implements ParameterizedQuery,
/**
* Adds a list of orderings.
*/
- public void addOrderings(List<? extends Ordering> orderings) {
+ public void addOrderings(Collection<? extends Ordering> orderings) {
// If the supplied list of orderings is null, do not attempt to add
// to the collection (addAll() will NPE otherwise).
- if (orderings != null)
+ if (orderings != null) {
nonNullOrderings().addAll(orderings);
+ }
}
/**
@@ -589,7 +591,7 @@ public class SelectQuery<T> extends AbstractQuery implements ParameterizedQuery,
* @since 4.0
*/
public void addPrefetch(PrefetchTreeNode prefetchElement) {
- metaData.mergePrefetch(prefetchElement);
+ metaData.mergePrefetch(prefetchElement);
}
/**
http://git-wip-us.apache.org/repos/asf/cayenne/blob/a0f941a0/cayenne-server/src/main/java/org/apache/cayenne/remote/IncrementalSelectQuery.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/IncrementalSelectQuery.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/IncrementalSelectQuery.java
index 2e6150b..bf935df 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/IncrementalSelectQuery.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/remote/IncrementalSelectQuery.java
@@ -18,6 +18,7 @@
****************************************************************/
package org.apache.cayenne.remote;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -156,8 +157,8 @@ class IncrementalSelectQuery<T> extends SelectQuery<T> {
}
@Override
- public void addOrderings(List orderings) {
- query.addOrderings(orderings);
+ public void addOrderings(Collection<? extends Ordering> orderings) {
+ query.addOrderings(orderings);
}
@Override
http://git-wip-us.apache.org/repos/asf/cayenne/blob/a0f941a0/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
new file mode 100644
index 0000000..cd69200
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelectTest.java
@@ -0,0 +1,482 @@
+/*****************************************************************
+ * 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 static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Collection;
+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.testdo.testmap.Artist;
+import org.junit.Test;
+
+public class ObjectSelectTest {
+
+ @Test
+ public void testDataRowQuery() {
+ ObjectSelect<DataRow> q = ObjectSelect.dataRowQuery(Artist.class);
+ assertNotNull(q);
+ assertTrue(q.isFetchingDataRows());
+
+ assertEquals(Artist.class, q.getEntityType());
+ assertNull(q.getEntityName());
+ assertNull(q.getDbEntityName());
+ }
+
+ @Test
+ public void testQuery_RootType() {
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+ assertNotNull(q);
+ assertNull(q.getExp());
+ assertFalse(q.isFetchingDataRows());
+
+ assertEquals(Artist.class, q.getEntityType());
+ assertNull(q.getEntityName());
+ assertNull(q.getDbEntityName());
+ }
+
+ @Test
+ public void testQuery_RootType_WithQualifier() {
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class, ExpressionFactory.matchExp("a", "A"));
+ assertNotNull(q);
+ assertEquals("a = \"A\"", q.getExp().toString());
+ assertFalse(q.isFetchingDataRows());
+
+ assertEquals(Artist.class, q.getEntityType());
+ assertNull(q.getEntityName());
+ assertNull(q.getDbEntityName());
+ }
+
+ @Test
+ public void testQuery_TypeAndEntity() {
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class, "Painting");
+ assertNotNull(q);
+ assertFalse(q.isFetchingDataRows());
+
+ assertNull(q.getEntityType());
+ assertEquals("Painting", q.getEntityName());
+ assertNull(q.getDbEntityName());
+ }
+
+ @Test
+ public void testQuery_TypeAndDbEntity() {
+ ObjectSelect<DataRow> q = ObjectSelect.dbQuery("PAINTING");
+ assertNotNull(q);
+ assertTrue(q.isFetchingDataRows());
+
+ assertNull(q.getEntityType());
+ assertNull(q.getEntityName());
+ assertEquals("PAINTING", q.getDbEntityName());
+ }
+
+ @Test
+ public void testExp() {
+
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+
+ q.exp(ExpressionFactory.matchExp("a", 3));
+ assertEquals("a = 3", q.getExp().toString());
+
+ q.exp(ExpressionFactory.matchExp("b", 4));
+ assertEquals("b = 4", q.getExp().toString());
+ }
+
+ @Test
+ public void testAnd_Array() {
+
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+
+ q.exp(ExpressionFactory.matchExp("a", 3));
+ assertEquals("a = 3", q.getExp().toString());
+
+ q.and(ExpressionFactory.matchExp("b", 4), ExpressionFactory.greaterExp("c", 5));
+ assertEquals("(a = 3) and (b = 4) and (c > 5)", q.getExp().toString());
+ }
+
+ @Test
+ public void testAnd_Collection() {
+
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+
+ q.exp(ExpressionFactory.matchExp("a", 3));
+ assertEquals("a = 3", q.getExp().toString());
+
+ Collection<Expression> exps = Arrays.asList(ExpressionFactory.matchExp("b", 4),
+ ExpressionFactory.greaterExp("c", 5));
+
+ q.and(exps);
+ assertEquals("(a = 3) and (b = 4) and (c > 5)", q.getExp().toString());
+ }
+
+ @Test
+ public void testAnd_ArrayNull() {
+
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+
+ q.exp(ExpressionFactory.matchExp("a", 3));
+ assertEquals("a = 3", q.getExp().toString());
+
+ q.and();
+ assertEquals("a = 3", q.getExp().toString());
+ }
+
+ @Test
+ public void testAnd_ArrayEmpty() {
+
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+
+ q.exp(ExpressionFactory.matchExp("a", 3));
+ assertEquals("a = 3", q.getExp().toString());
+
+ q.and(new Expression[0]);
+ assertEquals("a = 3", q.getExp().toString());
+ }
+
+ @Test
+ public void testAnd_CollectionEmpty() {
+
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+
+ q.exp(ExpressionFactory.matchExp("a", 3));
+ assertEquals("a = 3", q.getExp().toString());
+
+ q.and(Collections.<Expression> emptyList());
+ assertEquals("a = 3", q.getExp().toString());
+ }
+
+ @Test
+ public void testOr_Array() {
+
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+
+ q.exp(ExpressionFactory.matchExp("a", 3));
+ assertEquals("a = 3", q.getExp().toString());
+
+ q.or(ExpressionFactory.matchExp("b", 4), ExpressionFactory.greaterExp("c", 5));
+ assertEquals("(a = 3) or (b = 4) or (c > 5)", q.getExp().toString());
+ }
+
+ @Test
+ public void testOr_Collection() {
+
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+
+ q.exp(ExpressionFactory.matchExp("a", 3));
+ assertEquals("a = 3", q.getExp().toString());
+
+ Collection<Expression> exps = Arrays.asList(ExpressionFactory.matchExp("b", 4),
+ ExpressionFactory.greaterExp("c", 5));
+
+ q.or(exps);
+ assertEquals("(a = 3) or (b = 4) or (c > 5)", q.getExp().toString());
+ }
+
+ @Test
+ public void testOrderBy_Array() {
+
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+
+ Ordering o1 = new Ordering("x");
+ q.orderBy(o1);
+
+ Object[] result1 = q.getOrderings().toArray();
+ assertEquals(1, result1.length);
+ assertSame(o1, result1[0]);
+
+ Ordering o2 = new Ordering("y");
+ q.orderBy(o2);
+
+ Object[] result2 = q.getOrderings().toArray();
+ assertEquals(1, result2.length);
+ assertSame(o2, result2[0]);
+ }
+
+ @Test
+ public void testAddOrderBy_Array() {
+
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+
+ Ordering o1 = new Ordering("x");
+ q.orderBy(o1);
+
+ Object[] result1 = q.getOrderings().toArray();
+ assertEquals(1, result1.length);
+ assertSame(o1, result1[0]);
+
+ Ordering o2 = new Ordering("y");
+ q.addOrderBy(o2);
+
+ Object[] result2 = q.getOrderings().toArray();
+ assertEquals(2, result2.length);
+ assertSame(o1, result2[0]);
+ assertSame(o2, result2[1]);
+ }
+
+ @Test
+ public void testOrderBy_Collection() {
+
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+
+ Ordering o1 = new Ordering("x");
+ q.orderBy(Collections.singletonList(o1));
+
+ Object[] result1 = q.getOrderings().toArray();
+ assertEquals(1, result1.length);
+ assertSame(o1, result1[0]);
+
+ Ordering o2 = new Ordering("y");
+ q.orderBy(Collections.singletonList(o2));
+
+ Object[] result2 = q.getOrderings().toArray();
+ assertEquals(1, result2.length);
+ assertSame(o2, result2[0]);
+ }
+
+ @Test
+ public void testAddOrderBy_Collection() {
+
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+
+ Ordering o1 = new Ordering("x");
+ q.orderBy(Collections.singletonList(o1));
+
+ Object[] result1 = q.getOrderings().toArray();
+ assertEquals(1, result1.length);
+ assertSame(o1, result1[0]);
+
+ Ordering o2 = new Ordering("y");
+ q.addOrderBy(Collections.singletonList(o2));
+
+ Object[] result2 = q.getOrderings().toArray();
+ assertEquals(2, result2.length);
+ assertSame(o1, result2[0]);
+ assertSame(o2, result2[1]);
+ }
+
+ @Test
+ public void testOrderBy_Property() {
+
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+
+ q.orderBy("x");
+
+ Object[] result1 = q.getOrderings().toArray();
+ assertEquals(1, result1.length);
+ assertEquals(new Ordering("x", SortOrder.ASCENDING), result1[0]);
+
+ q.orderBy("y");
+
+ Object[] result2 = q.getOrderings().toArray();
+ assertEquals(1, result2.length);
+ assertEquals(new Ordering("y", SortOrder.ASCENDING), result2[0]);
+ }
+
+ @Test
+ public void testOrderBy_PropertyStrategy() {
+
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+
+ q.orderBy("x", SortOrder.ASCENDING_INSENSITIVE);
+
+ Object[] result1 = q.getOrderings().toArray();
+ assertEquals(1, result1.length);
+ assertEquals(new Ordering("x", SortOrder.ASCENDING_INSENSITIVE), result1[0]);
+
+ q.orderBy("y", SortOrder.DESCENDING);
+
+ Object[] result2 = q.getOrderings().toArray();
+ assertEquals(1, result2.length);
+ assertEquals(new Ordering("y", SortOrder.DESCENDING), result2[0]);
+ }
+
+ @Test
+ public void testAddOrderBy_Property() {
+
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+
+ q.addOrderBy("x");
+
+ Object[] result1 = q.getOrderings().toArray();
+ assertEquals(1, result1.length);
+ assertEquals(new Ordering("x", SortOrder.ASCENDING), result1[0]);
+
+ q.addOrderBy("y");
+
+ Object[] result2 = q.getOrderings().toArray();
+ assertEquals(2, result2.length);
+ assertEquals(new Ordering("x", SortOrder.ASCENDING), result2[0]);
+ assertEquals(new Ordering("y", SortOrder.ASCENDING), result2[1]);
+ }
+
+ @Test
+ public void testPrefetch() {
+
+ PrefetchTreeNode root = PrefetchTreeNode.withPath("a.b", PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
+
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+ q.prefetch(root);
+
+ assertSame(root, q.getPrefetches());
+ }
+
+ @Test
+ public void testPrefetch_Path() {
+
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+ q.prefetch("a.b", PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS);
+ PrefetchTreeNode root1 = q.getPrefetches();
+
+ assertNotNull(root1);
+ assertNotNull(root1.getNode("a.b"));
+
+ q.prefetch("a.c", PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS);
+ PrefetchTreeNode root2 = q.getPrefetches();
+
+ assertNotNull(root2);
+ assertNotNull(root2.getNode("a.c"));
+ assertNull(root2.getNode("a.b"));
+ assertNotSame(root1, root2);
+ }
+
+ @Test
+ public void testAddPrefetch() {
+
+ PrefetchTreeNode root = PrefetchTreeNode.withPath("a.b", PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
+
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+ q.prefetch(root);
+
+ assertSame(root, q.getPrefetches());
+
+ PrefetchTreeNode subRoot = PrefetchTreeNode.withPath("a.b.c", PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
+ q.addPrefetch(subRoot);
+
+ assertSame(root, q.getPrefetches());
+
+ assertNotNull(root.getNode("a.b.c"));
+ }
+
+ @Test
+ public void testLimit() {
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+
+ assertEquals(0, q.getLimit());
+ q.limit(2);
+ assertEquals(2, q.getLimit());
+
+ q.limit(3).limit(5);
+ assertEquals(5, q.getLimit());
+ }
+
+ @Test
+ public void testOffset() {
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+
+ assertEquals(0, q.getOffset());
+ q.offset(2);
+ assertEquals(2, q.getOffset());
+
+ q.offset(3).offset(5);
+ assertEquals(5, q.getOffset());
+ }
+
+ @Test
+ public void testStatementFetchSize() {
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+
+ assertEquals(0, q.getStatementFetchSize());
+ q.statementFetchSize(2);
+ assertEquals(2, q.getStatementFetchSize());
+
+ q.statementFetchSize(3).statementFetchSize(5);
+ assertEquals(5, q.getStatementFetchSize());
+ }
+
+
+ @Test
+ public void testCacheGroups_Collection() {
+ ObjectSelect<DataRow> q = ObjectSelect.dataRowQuery(Artist.class);
+
+ assertNull(q.getCacheStrategy());
+ assertNull(q.getCacheGroups());
+
+ q.cacheGroups(Arrays.asList("a", "b"));
+ assertNull(q.getCacheStrategy());
+ assertArrayEquals(new String[] { "a", "b" }, q.getCacheGroups());
+ }
+
+ @Test
+ public void testCacheStrategy() {
+ ObjectSelect<DataRow> q = ObjectSelect.dataRowQuery(Artist.class);
+
+ assertNull(q.getCacheStrategy());
+ assertNull(q.getCacheGroups());
+
+ q.cacheStrategy(QueryCacheStrategy.LOCAL_CACHE, "a", "b");
+ assertSame(QueryCacheStrategy.LOCAL_CACHE, q.getCacheStrategy());
+ assertArrayEquals(new String[] { "a", "b" }, q.getCacheGroups());
+
+ q.cacheStrategy(QueryCacheStrategy.SHARED_CACHE);
+ assertSame(QueryCacheStrategy.SHARED_CACHE, q.getCacheStrategy());
+ assertNull(q.getCacheGroups());
+ }
+
+ @Test
+ public void testLocalCache() {
+ ObjectSelect<DataRow> q = ObjectSelect.dataRowQuery(Artist.class);
+
+ assertNull(q.getCacheStrategy());
+ assertNull(q.getCacheGroups());
+
+ q.localCache("a", "b");
+ assertSame(QueryCacheStrategy.LOCAL_CACHE, q.getCacheStrategy());
+ assertArrayEquals(new String[] { "a", "b" }, q.getCacheGroups());
+
+ q.localCache();
+ assertSame(QueryCacheStrategy.LOCAL_CACHE, q.getCacheStrategy());
+ assertNull(q.getCacheGroups());
+ }
+
+ @Test
+ public void testSharedCache() {
+ ObjectSelect<DataRow> q = ObjectSelect.dataRowQuery(Artist.class);
+
+ assertNull(q.getCacheStrategy());
+ assertNull(q.getCacheGroups());
+
+ q.sharedCache("a", "b");
+ assertSame(QueryCacheStrategy.SHARED_CACHE, q.getCacheStrategy());
+ assertArrayEquals(new String[] { "a", "b" }, q.getCacheGroups());
+
+ q.sharedCache();
+ assertSame(QueryCacheStrategy.SHARED_CACHE, q.getCacheStrategy());
+ assertNull(q.getCacheGroups());
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/a0f941a0/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
new file mode 100644
index 0000000..9a7a590
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_CompileIT.java
@@ -0,0 +1,168 @@
+/*****************************************************************
+ * 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 static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.DataRow;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.testdo.testmap.Artist;
+import org.apache.cayenne.unit.di.server.ServerCase;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+import org.junit.Test;
+
+@UseServerRuntime(ServerCase.TESTMAP_PROJECT)
+public class ObjectSelect_CompileIT extends ServerCase {
+
+ @Inject
+ private EntityResolver resolver;
+
+ @Test
+ public void testCreateReplacementQuery_Bare() {
+
+ // use only a minimal number of attributes, with null/defaults for
+ // everything else
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+
+ Query replacement = q.createReplacementQuery(resolver);
+ assertThat(replacement, instanceOf(SelectQuery.class));
+
+ @SuppressWarnings("unchecked")
+ SelectQuery<Artist> selectQuery = (SelectQuery<Artist>) replacement;
+ assertNull(selectQuery.getQualifier());
+ assertEquals(Artist.class, selectQuery.getRoot());
+ assertEquals(0, selectQuery.getOrderings().size());
+ assertNull(selectQuery.getPrefetchTree());
+
+ assertEquals(QueryCacheStrategy.NO_CACHE, selectQuery.getCacheStrategy());
+ assertNull(selectQuery.getCacheGroups());
+ assertEquals(0, selectQuery.getFetchLimit());
+ assertEquals(0, selectQuery.getFetchOffset());
+ assertEquals(0, selectQuery.getPageSize());
+ assertEquals(0, selectQuery.getStatementFetchSize());
+ }
+
+ @Test
+ public void testCreateReplacementQuery_Full() {
+
+ // add all possible attributes to the query and make sure they got
+ // propagated
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class).exp(Artist.ARTIST_NAME.eq("me"))
+ .orderBy(Artist.DATE_OF_BIRTH.asc(), Artist.ARTIST_NAME.desc()).prefetch(Artist.PAINTING_ARRAY.joint())
+ .localCache("cg2", "cg1").limit(46).offset(9).pageSize(6).statementFetchSize(789);
+
+ Query replacement = q.createReplacementQuery(resolver);
+ assertThat(replacement, instanceOf(SelectQuery.class));
+
+ @SuppressWarnings("unchecked")
+ SelectQuery<Artist> selectQuery = (SelectQuery<Artist>) replacement;
+ assertEquals("artistName = \"me\"", selectQuery.getQualifier().toString());
+
+ assertEquals(2, selectQuery.getOrderings().size());
+ assertArrayEquals(new Object[] { Artist.DATE_OF_BIRTH.asc(), Artist.ARTIST_NAME.desc() }, selectQuery
+ .getOrderings().toArray());
+
+ PrefetchTreeNode prefetch = selectQuery.getPrefetchTree();
+ assertNotNull(prefetch);
+ assertEquals(1, prefetch.getChildren().size());
+
+ PrefetchTreeNode childPrefetch = prefetch.getNode(Artist.PAINTING_ARRAY.getName());
+ assertEquals(Artist.PAINTING_ARRAY.getName(), childPrefetch.getName());
+ assertEquals(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS, childPrefetch.getSemantics());
+
+ assertEquals(QueryCacheStrategy.LOCAL_CACHE, selectQuery.getCacheStrategy());
+ assertArrayEquals(new Object[] { "cg2", "cg1" }, selectQuery.getCacheGroups());
+
+ assertEquals(46, selectQuery.getFetchLimit());
+ assertEquals(9, selectQuery.getFetchOffset());
+ assertEquals(6, selectQuery.getPageSize());
+ assertEquals(789, selectQuery.getStatementFetchSize());
+ }
+
+ @Test
+ public void testCreateReplacementQuery_RootClass() {
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+
+ @SuppressWarnings("rawtypes")
+ SelectQuery qr = (SelectQuery) q.createReplacementQuery(resolver);
+ assertEquals(Artist.class, qr.getRoot());
+ assertFalse(qr.isFetchingDataRows());
+ }
+
+ @Test
+ public void testCreateReplacementQuery_RootDataRow() {
+ ObjectSelect<DataRow> q = ObjectSelect.dataRowQuery(Artist.class);
+
+ @SuppressWarnings("rawtypes")
+ SelectQuery qr = (SelectQuery) q.createReplacementQuery(resolver);
+ assertEquals(Artist.class, qr.getRoot());
+ assertTrue(qr.isFetchingDataRows());
+ }
+
+ @Test
+ public void testCreateReplacementQuery_RootDbEntity() {
+ ObjectSelect<DataRow> q = ObjectSelect.dbQuery("ARTIST");
+
+ @SuppressWarnings("rawtypes")
+ SelectQuery qr = (SelectQuery) q.createReplacementQuery(resolver);
+ assertEquals(resolver.getDbEntity("ARTIST"), qr.getRoot());
+ assertTrue(qr.isFetchingDataRows());
+ }
+
+ @Test
+ public void testCreateReplacementQuery_RootObjEntity() {
+ ObjectSelect<CayenneDataObject> q = ObjectSelect.query(CayenneDataObject.class, "Artist");
+
+ @SuppressWarnings("rawtypes")
+ SelectQuery qr = (SelectQuery) q.createReplacementQuery(resolver);
+ assertEquals(resolver.getObjEntity(Artist.class), qr.getRoot());
+ assertFalse(qr.isFetchingDataRows());
+ }
+
+ @Test(expected = CayenneRuntimeException.class)
+ public void testCreateReplacementQuery_RootAbscent() {
+ ObjectSelect<DataRow> q = ObjectSelect.dataRowQuery(Artist.class).entityName(null);
+ q.createReplacementQuery(resolver);
+ }
+
+ @Test
+ public void testCreateReplacementQuery_DataRows() {
+ ObjectSelect<Artist> q = ObjectSelect.query(Artist.class);
+
+ @SuppressWarnings("rawtypes")
+ SelectQuery selectQuery1 = (SelectQuery) q.createReplacementQuery(resolver);
+ assertFalse(selectQuery1.isFetchingDataRows());
+
+ q.fetchDataRows();
+
+ @SuppressWarnings("rawtypes")
+ SelectQuery selectQuery2 = (SelectQuery) q.createReplacementQuery(resolver);
+ assertTrue(selectQuery2.isFetchingDataRows());
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/a0f941a0/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
new file mode 100644
index 0000000..1b921a4
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_RunIT.java
@@ -0,0 +1,94 @@
+/*****************************************************************
+ * 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 static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+import java.util.List;
+
+import org.apache.cayenne.DataRow;
+import org.apache.cayenne.access.DataContext;
+import org.apache.cayenne.di.Inject;
+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.di.server.ServerCase;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+import org.junit.Test;
+
+@UseServerRuntime(ServerCase.TESTMAP_PROJECT)
+public class ObjectSelect_RunIT extends ServerCase {
+
+ @Inject
+ private DataContext context;
+
+ @Inject
+ private DBHelper dbHelper;
+
+ @Override
+ protected void setUpAfterInjection() throws Exception {
+ dbHelper.deleteAll("PAINTING_INFO");
+ dbHelper.deleteAll("PAINTING");
+ dbHelper.deleteAll("ARTIST_EXHIBIT");
+ dbHelper.deleteAll("ARTIST_GROUP");
+ dbHelper.deleteAll("ARTIST");
+ }
+
+ protected void createArtistsDataSet() throws Exception {
+ TableHelper tArtist = new TableHelper(dbHelper, "ARTIST");
+ tArtist.setColumns("ARTIST_ID", "ARTIST_NAME", "DATE_OF_BIRTH");
+
+ long dateBase = System.currentTimeMillis();
+
+ for (int i = 1; i <= 20; i++) {
+ tArtist.insert(i, "artist" + i, new java.sql.Date(dateBase + 10000 * i));
+ }
+ }
+
+ @Test
+ public void test_SelectObjects() throws Exception {
+
+ createArtistsDataSet();
+
+ List<Artist> result = ObjectSelect.query(Artist.class).select(context);
+ assertEquals(20, result.size());
+ assertThat(result.get(0), instanceOf(Artist.class));
+
+ Artist a = ObjectSelect.query(Artist.class).exp(Artist.ARTIST_NAME.eq("artist14")).selectOne(context);
+ assertNotNull(a);
+ assertEquals("artist14", a.getArtistName());
+ }
+
+ @Test
+ public void test_SelectDataRows() throws Exception {
+
+ createArtistsDataSet();
+
+ List<DataRow> result = ObjectSelect.dataRowQuery(Artist.class).select(context);
+ assertEquals(20, result.size());
+ assertThat(result.get(0), instanceOf(DataRow.class));
+
+ DataRow a = ObjectSelect.dataRowQuery(Artist.class).exp(Artist.ARTIST_NAME.eq("artist14")).selectOne(context);
+ assertNotNull(a);
+ assertEquals("artist14", a.get("ARTIST_NAME"));
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/a0f941a0/docs/doc/src/main/resources/RELEASE-NOTES.txt
----------------------------------------------------------------------
diff --git a/docs/doc/src/main/resources/RELEASE-NOTES.txt b/docs/doc/src/main/resources/RELEASE-NOTES.txt
index 86ec1c3..7711b36 100644
--- a/docs/doc/src/main/resources/RELEASE-NOTES.txt
+++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt
@@ -66,6 +66,7 @@ CAY-1952 Undeprecate (actually restore) ObjectContext.deleteObject(..)
CAY-1953 Redo ResultIteratorCallback to handle single row callback instead of iterator
CAY-1954 Make Cayenne class constructor protected
CAY-1958 SelectById - a new full-featured select query to get objects by id
+CAY-1959 Chainable API for SelectQuery
CAY-1960 ExpressionFactory.exp(..) , and(..), or(..)
CAY-1962 Implement CayenneTable column resize on double-click on the header separator
CAY-1965 Change version from 3.2 to 4.0
[8/8] cayenne git commit: restoring master version of pom.xml
Posted by aa...@apache.org.
restoring master version of pom.xml
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/7c1f69a1
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/7c1f69a1
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/7c1f69a1
Branch: refs/heads/CAY-1946
Commit: 7c1f69a1a0c15226ea90be3420e484665d54b1c0
Parents: 28f5793
Author: aadamchik <aa...@apache.org>
Authored: Sun Nov 16 15:09:35 2014 +0300
Committer: aadamchik <aa...@apache.org>
Committed: Sun Nov 16 15:09:35 2014 +0300
----------------------------------------------------------------------
pom.xml | 28 +++++++++++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7c1f69a1/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 56334a2..d5c1f76 100644
--- a/pom.xml
+++ b/pom.xml
@@ -535,7 +535,7 @@
<enabled>true</enabled>
</releases>
</pluginRepository>
- </pluginRepositories>
+ </pluginRepositories>
<build>
<defaultGoal>install</defaultGoal>
@@ -676,6 +676,32 @@
<ignore />
</action>
</pluginExecution>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <versionRange>[1.0,)</versionRange>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore />
+ </action>
+ </pluginExecution>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-pmd-plugin</artifactId>
+ <versionRange>[1.0,)</versionRange>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore />
+ </action>
+ </pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>