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(&quot;group1&quot;, &quot;group2&quot;);
+	 * 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(&quot;group1&quot;, &quot;group2&quot;);
+	 * 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>