cayenne-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dkazimirc...@apache.org
Subject cayenne git commit: CAY-2062 MappedSelect/MappedExec fluent query API.
Date Wed, 24 Feb 2016 09:16:29 GMT
Repository: cayenne
Updated Branches:
  refs/heads/master c052cec39 -> 212bc53ae


CAY-2062 MappedSelect/MappedExec fluent query API.


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/212bc53a
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/212bc53a
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/212bc53a

Branch: refs/heads/master
Commit: 212bc53ae988c547ba3eed778c594a12c0b9b2a0
Parents: c052cec
Author: Dzmitry Kazimirchyk <dkazimirchyk@gmail.com>
Authored: Tue Feb 23 16:07:25 2016 +0300
Committer: Dzmitry Kazimirchyk <dkazimirchyk@gmail.com>
Committed: Wed Feb 24 12:15:27 2016 +0300

----------------------------------------------------------------------
 .../CayenneContextNamedQueryCachingIT.java      |  21 +-
 .../access/ClientServerChannelQueryIT.java      |  10 +-
 .../org/apache/cayenne/access/DataContext.java  |  15 +-
 .../cayenne/query/AbstractMappedQuery.java      | 106 ++++++++
 .../org/apache/cayenne/query/MappedExec.java    |  77 ++++++
 .../org/apache/cayenne/query/MappedSelect.java  | 264 +++++++++++++++++++
 .../org/apache/cayenne/query/NamedQuery.java    |   1 +
 .../apache/cayenne/access/DateTimeTypesIT.java  |  17 +-
 .../cayenne/access/ReturnTypesMappingIT.java    | 132 ++++------
 .../org/apache/cayenne/query/MappedQueryIT.java | 180 +++++++++++++
 10 files changed, 704 insertions(+), 119 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/212bc53a/cayenne-client/src/test/java/org/apache/cayenne/CayenneContextNamedQueryCachingIT.java
----------------------------------------------------------------------
diff --git a/cayenne-client/src/test/java/org/apache/cayenne/CayenneContextNamedQueryCachingIT.java b/cayenne-client/src/test/java/org/apache/cayenne/CayenneContextNamedQueryCachingIT.java
index c5b7bdf..5100945 100644
--- a/cayenne-client/src/test/java/org/apache/cayenne/CayenneContextNamedQueryCachingIT.java
+++ b/cayenne-client/src/test/java/org/apache/cayenne/CayenneContextNamedQueryCachingIT.java
@@ -20,7 +20,7 @@
 package org.apache.cayenne;
 
 import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.query.NamedQuery;
+import org.apache.cayenne.query.MappedSelect;
 import org.apache.cayenne.test.jdbc.DBHelper;
 import org.apache.cayenne.test.jdbc.TableHelper;
 import org.apache.cayenne.unit.di.DataChannelInterceptor;
@@ -31,7 +31,6 @@ import org.apache.cayenne.unit.di.server.UseServerRuntime;
 import org.junit.Before;
 import org.junit.Test;
 
-import java.util.Collections;
 import java.util.List;
 
 import static org.junit.Assert.assertEquals;
@@ -68,7 +67,7 @@ public class CayenneContextNamedQueryCachingIT extends ClientCase {
     public void testLocalCache() throws Exception {
         createThreeMtTable1sDataSet();
 
-        final NamedQuery q1 = new NamedQuery("MtQueryWithLocalCache");
+        final MappedSelect q1 = MappedSelect.query("MtQueryWithLocalCache");
 
         final List<?> result1 = context.performQuery(q1);
         assertEquals(3, result1.size());
@@ -82,7 +81,7 @@ public class CayenneContextNamedQueryCachingIT extends ClientCase {
         });
 
         // refresh
-        q1.setForceNoCache(true);
+        q1.forceNoCache();
         List<?> result3 = context.performQuery(q1);
         assertNotSame(result1, result3);
         assertEquals(3, result3.size());
@@ -92,13 +91,8 @@ public class CayenneContextNamedQueryCachingIT extends ClientCase {
     public void testLocalCacheParameterized() throws Exception {
         createThreeMtTable1sDataSet();
 
-        final NamedQuery q1 = new NamedQuery(
-                "ParameterizedMtQueryWithLocalCache",
-                Collections.singletonMap("g", "g1"));
-
-        final NamedQuery q2 = new NamedQuery(
-                "ParameterizedMtQueryWithLocalCache",
-                Collections.singletonMap("g", "g2"));
+        final MappedSelect q1 = MappedSelect.query("ParameterizedMtQueryWithLocalCache").param("g", "g1");
+        final MappedSelect q2 = MappedSelect.query("ParameterizedMtQueryWithLocalCache").param("g", "g2");
 
         final List<?> result1 = context.performQuery(q1);
         assertEquals(1, result1.size());
@@ -132,9 +126,8 @@ public class CayenneContextNamedQueryCachingIT extends ClientCase {
     public void testParameterizedMappedToEJBQLQueries() throws Exception {
         
         createThreeMtTable1sDataSet();
-        NamedQuery query = new NamedQuery("ParameterizedEJBQLMtQuery", Collections.singletonMap("g", "g1"));
-        
-        List<?> r1 = context.performQuery(query);
+
+        List<?> r1 = context.performQuery(MappedSelect.query("ParameterizedEJBQLMtQuery").param("g", "g1"));
         assertEquals(1, r1.size());
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/212bc53a/cayenne-client/src/test/java/org/apache/cayenne/access/ClientServerChannelQueryIT.java
----------------------------------------------------------------------
diff --git a/cayenne-client/src/test/java/org/apache/cayenne/access/ClientServerChannelQueryIT.java b/cayenne-client/src/test/java/org/apache/cayenne/access/ClientServerChannelQueryIT.java
index ae189f7..ecb99c1 100644
--- a/cayenne-client/src/test/java/org/apache/cayenne/access/ClientServerChannelQueryIT.java
+++ b/cayenne-client/src/test/java/org/apache/cayenne/access/ClientServerChannelQueryIT.java
@@ -24,7 +24,7 @@ import org.apache.cayenne.ValueHolder;
 import org.apache.cayenne.cache.QueryCache;
 import org.apache.cayenne.di.Inject;
 import org.apache.cayenne.exp.Expression;
-import org.apache.cayenne.query.NamedQuery;
+import org.apache.cayenne.query.MappedSelect;
 import org.apache.cayenne.query.SelectQuery;
 import org.apache.cayenne.query.SortOrder;
 import org.apache.cayenne.test.jdbc.DBHelper;
@@ -39,7 +39,6 @@ import org.apache.cayenne.util.PersistentObjectList;
 import org.junit.Before;
 import org.junit.Test;
 
-import java.util.Collections;
 import java.util.List;
 
 import static org.junit.Assert.assertEquals;
@@ -114,9 +113,7 @@ public class ClientServerChannelQueryIT extends ClientCase {
     public void testParameterizedMappedToEJBQLQueries() throws Exception {
         createTwoMtTable1sAnd2sDataSet();
 
-        NamedQuery query = new NamedQuery("ParameterizedEJBQLMtQuery", Collections.singletonMap("g", "g1"));
-        
-        List<?> r1 = context.performQuery(query);
+        List<?> r1 = context.performQuery(MappedSelect.query("ParameterizedEJBQLMtQuery").param("g", "g1"));
         assertEquals(1, r1.size());
         assertTrue(r1.get(0) instanceof ClientMtTable1);
     }
@@ -125,8 +122,7 @@ public class ClientServerChannelQueryIT extends ClientCase {
     public void testNamedQuery() throws Exception {
         createTwoMtTable1sAnd2sDataSet();
 
-        NamedQuery q = new NamedQuery("AllMtTable1");
-        List<?> results = context.performQuery(q);
+        List<?> results = context.performQuery(MappedSelect.query("AllMtTable1"));
 
         assertEquals(2, results.size());
         assertTrue(results.get(0) instanceof ClientMtTable1);

http://git-wip-us.apache.org/repos/asf/cayenne/blob/212bc53a/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java
index 0fdab21..209d78e 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java
@@ -53,10 +53,7 @@ import org.apache.cayenne.map.DbRelationship;
 import org.apache.cayenne.map.ObjAttribute;
 import org.apache.cayenne.map.ObjEntity;
 import org.apache.cayenne.map.ObjRelationship;
-import org.apache.cayenne.query.NamedQuery;
-import org.apache.cayenne.query.Query;
-import org.apache.cayenne.query.QueryMetadata;
-import org.apache.cayenne.query.Select;
+import org.apache.cayenne.query.*;
 import org.apache.cayenne.reflect.AttributeProperty;
 import org.apache.cayenne.reflect.ClassDescriptor;
 import org.apache.cayenne.reflect.PropertyVisitor;
@@ -1003,7 +1000,7 @@ public class DataContext extends BaseContext {
      * @since 1.1
      */
     public int[] performNonSelectingQuery(String queryName) {
-        return performNonSelectingQuery(new NamedQuery(queryName));
+        return performNonSelectingQuery(MappedExec.query(queryName));
     }
 
     /**
@@ -1013,7 +1010,7 @@ public class DataContext extends BaseContext {
      * @since 1.1
      */
     public int[] performNonSelectingQuery(String queryName, Map<String, ?> parameters) {
-        return performNonSelectingQuery(new NamedQuery(queryName, parameters));
+        return performNonSelectingQuery(MappedExec.query(queryName).params(parameters));
     }
 
     /**
@@ -1053,9 +1050,9 @@ public class DataContext extends BaseContext {
      * @since 1.1
      */
     public List<?> performQuery(String queryName, Map parameters, boolean expireCachedLists) {
-        NamedQuery query = new NamedQuery(queryName, parameters);
-        query.setForceNoCache(expireCachedLists);
-        return performQuery(query);
+        return performQuery(expireCachedLists ?
+                MappedSelect.query(queryName).params(parameters).forceNoCache() :
+                MappedSelect.query(queryName).params(parameters));
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/cayenne/blob/212bc53a/cayenne-server/src/main/java/org/apache/cayenne/query/AbstractMappedQuery.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/AbstractMappedQuery.java b/cayenne-server/src/main/java/org/apache/cayenne/query/AbstractMappedQuery.java
new file mode 100644
index 0000000..dbc76a3
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/AbstractMappedQuery.java
@@ -0,0 +1,106 @@
+/*****************************************************************
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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 org.apache.cayenne.Persistent;
+import org.apache.cayenne.map.EntityResolver;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public abstract class AbstractMappedQuery extends IndirectQuery {
+
+    protected String queryName;
+    protected Map<String, Object> params;
+
+    protected AbstractMappedQuery(String queryName) {
+        this.queryName = queryName;
+    }
+
+    public AbstractMappedQuery name(String queryName) {
+        this.queryName = queryName;
+        return this;
+    }
+
+    protected AbstractMappedQuery params(Map<String, ?> parameters) {
+        if (this.params == null) {
+            this.params = new HashMap<>(parameters);
+        } else {
+            Map bareMap = parameters;
+            this.params.putAll(bareMap);
+        }
+
+        this.replacementQuery = null;
+
+        return this;
+    }
+
+    public AbstractMappedQuery param(String name, Object value) {
+        if (this.params == null) {
+            this.params = new HashMap<>();
+        }
+
+        this.params.put(name, value);
+
+        this.replacementQuery = null;
+
+        return this;
+    }
+
+    /**
+     * Returns a non-null parameter map, substituting all persistent objects in the
+     * initial map with ObjectIds. This is needed so that a query could work uniformly on
+     * the server and client sides.
+     */
+    Map<String, ?> normalizedParameters() {
+        if (params == null || params.isEmpty()) {
+            return Collections.emptyMap();
+        }
+
+        Map<String, Object> substitutes = new HashMap<>(params);
+
+        for (Map.Entry<String, Object> entry : params.entrySet()) {
+            Object value = entry.getValue();
+
+            if (value instanceof Persistent) {
+                value = ((Persistent) value).getObjectId();
+            }
+
+            substitutes.put(entry.getKey(), value);
+        }
+
+        return substitutes;
+    }
+
+    @Override
+    protected Query createReplacementQuery(EntityResolver resolver) {
+        Query query = resolver.getQueryDescriptor(queryName).buildQuery();
+
+        if (query instanceof ParameterizedQuery) {
+            query = ((ParameterizedQuery) query).createQuery(normalizedParameters());
+        } else if (query instanceof EJBQLQuery) {
+            for (Map.Entry<String, ?> entry : normalizedParameters().entrySet()) {
+                ((EJBQLQuery) query).setParameter(entry.getKey(), entry.getValue());
+            }
+        }
+
+        return query;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/212bc53a/cayenne-server/src/main/java/org/apache/cayenne/query/MappedExec.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/MappedExec.java b/cayenne-server/src/main/java/org/apache/cayenne/query/MappedExec.java
new file mode 100644
index 0000000..be36365
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/MappedExec.java
@@ -0,0 +1,77 @@
+/*****************************************************************
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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 org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.QueryResult;
+import org.apache.cayenne.util.QueryResultBuilder;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A query that represents a named parameterized non selecting query stored in the mapping. The
+ * actual query is resolved during execution.
+ *
+ * @since 4.0
+ */
+public class MappedExec extends AbstractMappedQuery {
+
+    public static MappedExec query(String queryName) {
+        return new MappedExec(queryName);
+    }
+
+    protected MappedExec(String queryName) {
+        super(queryName);
+    }
+
+    @Override
+    public MappedExec params(Map<String, ?> parameters) {
+        return (MappedExec) super.params(parameters);
+    }
+
+    @Override
+    public MappedExec param(String name, Object value) {
+        return (MappedExec) super.param(name, value);
+    }
+
+
+    public List<QueryResult> execute(ObjectContext context) {
+        // TODO: switch ObjectContext to QueryResult instead of QueryResponse
+        // and create its own 'exec' method
+        QueryResponse response = context.performGenericQuery(this);
+
+        QueryResultBuilder builder = QueryResultBuilder.builder(response.size());
+        for (response.reset(); response.next(); ) {
+
+            if (response.isList()) {
+                builder.addSelectResult(response.currentList());
+            } else {
+                builder.addBatchUpdateResult(response.currentUpdateCount());
+            }
+        }
+
+        return builder.build();
+    }
+
+    public int[] update(ObjectContext context) {
+        return context.performGenericQuery(this).firstUpdateCount();
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/212bc53a/cayenne-server/src/main/java/org/apache/cayenne/query/MappedSelect.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/MappedSelect.java b/cayenne-server/src/main/java/org/apache/cayenne/query/MappedSelect.java
new file mode 100644
index 0000000..40bf4f5
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/MappedSelect.java
@@ -0,0 +1,264 @@
+/*****************************************************************
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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 org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.ResultBatchIterator;
+import org.apache.cayenne.ResultIterator;
+import org.apache.cayenne.ResultIteratorCallback;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.map.QueryDescriptor;
+
+import java.sql.Statement;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A query that represents a named parameterized selecting query stored in the mapping. The
+ * actual query is resolved during execution.
+ *
+ * @since 4.0
+ */
+public class MappedSelect<T> extends AbstractMappedQuery implements Select<T> {
+
+    /**
+     * Loads query with the given name, which selects objects of a given persistent class,
+     * from the mapping configuration.
+     *
+     * @param queryName name of the mapped query
+     * @param rootClass the Class of objects fetched by this query
+     */
+    public static <T> MappedSelect<T> query(String queryName, Class<T> rootClass) {
+        return new MappedSelect<>(queryName, rootClass);
+    }
+
+    /**
+     * Loads query with the given name from the mapping configuration.
+     *
+     * @param queryName name of the mapped query
+     */
+    public static MappedSelect<?> query(String queryName) {
+        return new MappedSelect<>(queryName);
+    }
+
+    protected Class<T> resultClass;
+    protected Integer fetchLimit;
+    protected Integer fetchOffset;
+    protected Integer statementFetchSize;
+    protected Integer pageSize;
+    protected boolean forceNoCache;
+
+    protected MappedSelect(String queryName) {
+        super(queryName);
+    }
+
+    protected MappedSelect(String queryName, Class<T> resultClass) {
+        super(queryName);
+        this.resultClass = resultClass;
+    }
+
+    /**
+     * Resets query fetch limit - a parameter that defines max number of objects
+     * that should be ever be fetched from the database.
+     */
+    public MappedSelect<T> limit(int fetchLimit) {
+        this.fetchLimit = 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 MappedSelect<T> offset(int fetchOffset) {
+        this.fetchOffset = fetchOffset;
+        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 MappedSelect<T> statementFetchSize(int statementFetchSize) {
+        this.statementFetchSize = statementFetchSize;
+        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 MappedSelect<T> pageSize(int pageSize) {
+        this.pageSize = pageSize;
+        this.replacementQuery = null;
+        return this;
+    }
+
+    /**
+     * Forces query cache to be refreshed during the execution of this query.
+     */
+    public MappedSelect<T> forceNoCache() {
+        this.forceNoCache = true;
+        this.replacementQuery = null;
+        return this;
+    }
+
+    @Override
+    public MappedSelect<T> params(Map<String, ?> parameters) {
+        return (MappedSelect<T>) super.params(parameters);
+    }
+
+    @Override
+    public MappedSelect<T> param(String name, Object value) {
+        return (MappedSelect<T>) super.param(name, value);
+    }
+
+    public List<T> select(ObjectContext context) {
+        return context.select(this);
+    }
+
+    @Override
+    public T selectOne(ObjectContext context) {
+        return context.selectOne(this);
+    }
+
+    @Override
+    public T selectFirst(ObjectContext context) {
+        return context.selectFirst(limit(1));
+    }
+
+    @Override
+    public void iterate(ObjectContext context, ResultIteratorCallback<T> callback) {
+        context.iterate(this, callback);
+    }
+
+    @Override
+    public ResultIterator<T> iterator(ObjectContext context) {
+        return context.iterator(this);
+    }
+
+    @Override
+    public ResultBatchIterator<T> batchIterator(ObjectContext context, int size) {
+        return context.batchIterator(this, size);
+    }
+
+    @Override
+    protected Query createReplacementQuery(EntityResolver resolver) {
+        QueryDescriptor descriptor = resolver.getQueryDescriptor(queryName);
+
+        Query query = super.createReplacementQuery(resolver);
+
+        QueryCacheStrategy cacheStrategyOverride = null;
+
+        if (forceNoCache) {
+            QueryCacheStrategy cacheStrategy = query.getMetaData(resolver).getCacheStrategy();
+            if (QueryCacheStrategy.LOCAL_CACHE == cacheStrategy) {
+                cacheStrategyOverride = QueryCacheStrategy.LOCAL_CACHE_REFRESH;
+            } else if (QueryCacheStrategy.SHARED_CACHE == cacheStrategy) {
+                cacheStrategyOverride = QueryCacheStrategy.SHARED_CACHE_REFRESH;
+            }
+        }
+
+        switch (descriptor.getType()) {
+            case QueryDescriptor.SELECT_QUERY:
+                SelectQuery selectQuery = (SelectQuery) query;
+                if (fetchLimit != null) {
+                    selectQuery.setFetchLimit(fetchLimit);
+                }
+                if (fetchOffset != null) {
+                    selectQuery.setFetchOffset(fetchOffset);
+                }
+                if (statementFetchSize != null) {
+                    selectQuery.setStatementFetchSize(statementFetchSize);
+                }
+                if (pageSize != null) {
+                    selectQuery.setPageSize(pageSize);
+                }
+                if (cacheStrategyOverride != null) {
+                    selectQuery.setCacheStrategy(cacheStrategyOverride);
+                }
+                break;
+            case QueryDescriptor.SQL_TEMPLATE:
+                SQLTemplate sqlTemplate = (SQLTemplate) query;
+                if (fetchLimit != null) {
+                    sqlTemplate.setFetchLimit(fetchLimit);
+                }
+                if (fetchOffset != null) {
+                    sqlTemplate.setFetchOffset(fetchOffset);
+                }
+                if (statementFetchSize != null) {
+                    sqlTemplate.setStatementFetchSize(statementFetchSize);
+                }
+                if (pageSize != null) {
+                    sqlTemplate.setPageSize(pageSize);
+                }
+                if (cacheStrategyOverride != null) {
+                    sqlTemplate.setCacheStrategy(cacheStrategyOverride);
+                }
+                break;
+            case QueryDescriptor.EJBQL_QUERY:
+                EJBQLQuery ejbqlQuery = (EJBQLQuery) query;
+                if (fetchLimit != null) {
+                    ejbqlQuery.setFetchLimit(fetchLimit);
+                }
+                if (fetchOffset != null) {
+                    ejbqlQuery.setFetchOffset(fetchOffset);
+                }
+                if (statementFetchSize != null) {
+                    ejbqlQuery.setStatementFetchSize(statementFetchSize);
+                }
+                if (pageSize != null) {
+                    ejbqlQuery.setPageSize(pageSize);
+                }
+                if (cacheStrategyOverride != null) {
+                    ejbqlQuery.setCacheStrategy(cacheStrategyOverride);
+                }
+                break;
+            case QueryDescriptor.PROCEDURE_QUERY:
+                ProcedureQuery procedureQuery = (ProcedureQuery) query;
+                if (fetchLimit != null) {
+                    procedureQuery.setFetchLimit(fetchLimit);
+                }
+                if (fetchOffset != null) {
+                    procedureQuery.setFetchOffset(fetchOffset);
+                }
+                if (statementFetchSize != null) {
+                    procedureQuery.setStatementFetchSize(statementFetchSize);
+                }
+                if (pageSize != null) {
+                    procedureQuery.setPageSize(pageSize);
+                }
+                if (cacheStrategyOverride != null) {
+                    procedureQuery.setCacheStrategy(cacheStrategyOverride);
+                }
+                break;
+            default:
+                throw new CayenneRuntimeException("Unknown query type: " + descriptor.getType());
+        }
+
+        return query;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/212bc53a/cayenne-server/src/main/java/org/apache/cayenne/query/NamedQuery.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/NamedQuery.java b/cayenne-server/src/main/java/org/apache/cayenne/query/NamedQuery.java
index d24a4ce..b4bd2e6 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/NamedQuery.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/NamedQuery.java
@@ -34,6 +34,7 @@ import org.apache.cayenne.util.Util;
  * actual query is resolved during execution.
  * 
  * @since 1.2
+ * @deprecated since 4.0 you should use {@link MappedQuery} instead
  */
 public class NamedQuery extends IndirectQuery {
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/212bc53a/cayenne-server/src/test/java/org/apache/cayenne/access/DateTimeTypesIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/DateTimeTypesIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/DateTimeTypesIT.java
index 1883fca..f187985 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/DateTimeTypesIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/DateTimeTypesIT.java
@@ -21,7 +21,7 @@ package org.apache.cayenne.access;
 
 import org.apache.cayenne.DataRow;
 import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.query.NamedQuery;
+import org.apache.cayenne.query.MappedSelect;
 import org.apache.cayenne.query.SelectQuery;
 import org.apache.cayenne.testdo.date_time.CalendarEntity;
 import org.apache.cayenne.testdo.date_time.DateTestEntity;
@@ -146,9 +146,8 @@ public class DateTimeTypesIT extends ServerCase {
         Date now = cal.getTime();
         test.setTimestampColumn(now);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectDateTest");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectDateTest")).get(0);
         Date columnValue = (Date) testRead.get("TIMESTAMP_COLUMN");
         assertNotNull(columnValue);
         assertEquals(now, columnValue);
@@ -168,9 +167,8 @@ public class DateTimeTypesIT extends ServerCase {
         java.sql.Date now = new java.sql.Date(cal.getTime().getTime());
         test.setDateColumn(now);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectDateTest");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectDateTest")).get(0);
         Date columnValue = (Date) testRead.get("DATE_COLUMN");
         assertNotNull(columnValue);
         assertEquals(now.toString(), new java.sql.Date(columnValue.getTime()).toString());
@@ -190,9 +188,8 @@ public class DateTimeTypesIT extends ServerCase {
         Time now = new Time(cal.getTime().getTime());
         test.setTimeColumn(now);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectDateTest");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectDateTest")).get(0);
         Date columnValue = (Date) testRead.get("TIME_COLUMN");
         assertNotNull(testRead.toString(), columnValue);
         assertNotNull(columnValue);

http://git-wip-us.apache.org/repos/asf/cayenne/blob/212bc53a/cayenne-server/src/test/java/org/apache/cayenne/access/ReturnTypesMappingIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/ReturnTypesMappingIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/ReturnTypesMappingIT.java
index 651d37c..4426e78 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/ReturnTypesMappingIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/ReturnTypesMappingIT.java
@@ -20,7 +20,7 @@ package org.apache.cayenne.access;
 
 import org.apache.cayenne.DataRow;
 import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.query.NamedQuery;
+import org.apache.cayenne.query.MappedSelect;
 import org.apache.cayenne.query.SelectQuery;
 import org.apache.cayenne.testdo.return_types.ReturnTypesMap1;
 import org.apache.cayenne.testdo.return_types.ReturnTypesMap2;
@@ -68,9 +68,8 @@ public class ReturnTypesMappingIT extends ServerCase {
         Long bigintValue = 5326457654783454355l;
         test.setBigintColumn(bigintValue);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectReturnTypesMap1");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap1")).get(0);
         Object columnValue = testRead.get(columnName);
         assertNotNull(columnValue);
         assertEquals(Long.class, columnValue.getClass());
@@ -104,9 +103,8 @@ public class ReturnTypesMappingIT extends ServerCase {
             };
             test.setBinaryColumn(binaryValue);
             context.commitChanges();
-    
-            NamedQuery q = new NamedQuery("SelectReturnTypesMap2");
-            DataRow testRead = (DataRow) context.performQuery(q).get(0);
+			
+            DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap2")).get(0);
             Object columnValue = testRead.get(columnName);
             assertNotNull(columnValue);
             assertEquals(byte[].class, columnValue.getClass());
@@ -142,9 +140,8 @@ public class ReturnTypesMappingIT extends ServerCase {
         Boolean bitValue = true;
         test.setBitColumn(bitValue);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectReturnTypesMap1");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap1")).get(0);
         Object columnValue = testRead.get(columnName);
         assertNotNull(columnValue);
         assertTrue(Boolean.class.equals(columnValue.getClass())
@@ -182,9 +179,8 @@ public class ReturnTypesMappingIT extends ServerCase {
             };
             test.setBlobColumn(blobValue);
             context.commitChanges();
-    
-            NamedQuery q = new NamedQuery("SelectReturnTypesMap2");
-            DataRow testRead = (DataRow) context.performQuery(q).get(0);
+			
+            DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap2")).get(0);
             Object columnValue = testRead.get(columnName);
             assertNotNull(columnValue);
             assertEquals(byte[].class, columnValue.getClass());
@@ -220,9 +216,8 @@ public class ReturnTypesMappingIT extends ServerCase {
         Boolean booleanValue = true;
         test.setBooleanColumn(booleanValue);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectReturnTypesMap1");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap1")).get(0);
         Object columnValue = testRead.get(columnName);
         assertNotNull(columnValue);
         assertTrue(Boolean.class.equals(columnValue.getClass())
@@ -255,9 +250,8 @@ public class ReturnTypesMappingIT extends ServerCase {
         String charValue = "Char string for tests!";
         test.setCharColumn(charValue);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectReturnTypesMap1");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap1")).get(0);
         Object columnValue = testRead.get(columnName);
         assertNotNull(columnValue);
         assertEquals(String.class, columnValue.getClass());
@@ -272,9 +266,8 @@ public class ReturnTypesMappingIT extends ServerCase {
         String charValue = "درخت‌های جستجوی متوازن، نیازی ندارد که به صورت!";
         test.setNCharColumn(charValue);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectReturnTypesMap1");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap1")).get(0);
         Object columnValue = testRead.get(columnName);
         assertNotNull(columnValue);
         assertEquals(String.class, columnValue.getClass());
@@ -310,9 +303,8 @@ public class ReturnTypesMappingIT extends ServerCase {
             String clobValue = buffer.toString();
             test.setClobColumn(clobValue);
             context.commitChanges();
-    
-            NamedQuery q = new NamedQuery("SelectReturnTypesLobsMap1");
-            DataRow testRead = (DataRow) context.performQuery(q).get(0);
+			
+            DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesLobsMap1")).get(0);
             Object columnValue = testRead.get(columnName);
             if (columnValue == null && testRead.containsKey(columnName.toLowerCase())) {
                 columnValue = testRead.get(columnName.toLowerCase());
@@ -337,9 +329,8 @@ public class ReturnTypesMappingIT extends ServerCase {
             String clobValue = buffer.toString();
             test.setNClobColumn(clobValue);
             context.commitChanges();
-
-            NamedQuery q = new NamedQuery("SelectReturnTypesLobsMap1");
-            DataRow testRead = (DataRow) context.performQuery(q).get(0);
+			
+            DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesLobsMap1")).get(0);
             Object columnValue = testRead.get(columnName);
             if (columnValue == null && testRead.containsKey(columnName.toLowerCase())) {
                 columnValue = testRead.get(columnName.toLowerCase());
@@ -383,9 +374,8 @@ public class ReturnTypesMappingIT extends ServerCase {
         Date dateValue = cal.getTime();
         test.setDateColumn(dateValue);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectReturnTypesMap1");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap1")).get(0);
         Object columnValue = testRead.get(columnName);
         assertNotNull(columnValue);
         assertEquals(Date.class, columnValue.getClass());
@@ -419,9 +409,8 @@ public class ReturnTypesMappingIT extends ServerCase {
         BigDecimal decimalValue = new BigDecimal("578438.57843");
         test.setDecimalColumn(decimalValue);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectReturnTypesMap1");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap1")).get(0);
         Object columnValue = testRead.get(columnName);
         assertNotNull(columnValue);
         assertEquals(BigDecimal.class, columnValue.getClass());
@@ -452,9 +441,8 @@ public class ReturnTypesMappingIT extends ServerCase {
         Double doubleValue = 3298.4349783d;
         test.setDoubleColumn(doubleValue);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectReturnTypesMap1");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap1")).get(0);
         Object columnValue = testRead.get(columnName);
         assertNotNull(columnValue);
         assertEquals(Double.class, columnValue.getClass());
@@ -485,9 +473,8 @@ public class ReturnTypesMappingIT extends ServerCase {
         Float floatValue = 375.437f;
         test.setFloatColumn(floatValue);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectReturnTypesMap1");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap1")).get(0);
         Object columnValue = testRead.get(columnName);
         assertNotNull(columnValue);
         assertTrue(Float.class.equals(columnValue.getClass())
@@ -519,9 +506,8 @@ public class ReturnTypesMappingIT extends ServerCase {
         Integer integerValue = 54235;
         test.setIntegerColumn(integerValue);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectReturnTypesMap1");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap1")).get(0);
         Object columnValue = testRead.get(columnName);
         assertNotNull(columnValue);
         assertEquals(Integer.class, columnValue.getClass());
@@ -555,9 +541,8 @@ public class ReturnTypesMappingIT extends ServerCase {
             };
             test.setLongvarbinaryColumn(longvarbinaryValue);
             context.commitChanges();
-    
-            NamedQuery q = new NamedQuery("SelectReturnTypesMap2");
-            DataRow testRead = (DataRow) context.performQuery(q).get(0);
+			
+            DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap2")).get(0);
             Object columnValue = testRead.get(columnName);
             assertNotNull(columnValue);
             assertEquals(byte[].class, columnValue.getClass());
@@ -597,9 +582,8 @@ public class ReturnTypesMappingIT extends ServerCase {
         String longvarcharValue = buffer.toString();
         test.setLongvarcharColumn(longvarcharValue);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectReturnTypesMap1");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap1")).get(0);
         Object columnValue = testRead.get(columnName);
         assertNotNull(columnValue);
         assertEquals(String.class, columnValue.getClass());
@@ -618,9 +602,8 @@ public class ReturnTypesMappingIT extends ServerCase {
         String longnvarcharValue = buffer.toString();
         test.setLongnvarcharColumn(longnvarcharValue);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectReturnTypesMap1");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap1")).get(0);
         Object columnValue = testRead.get(columnName);
         assertNotNull(columnValue);
         assertEquals(String.class, columnValue.getClass());
@@ -655,9 +638,8 @@ public class ReturnTypesMappingIT extends ServerCase {
         BigDecimal numericValue = new BigDecimal("578438.57843");
         test.setNumericColumn(numericValue);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectReturnTypesMap1");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap1")).get(0);
         Object columnValue = testRead.get(columnName);
         assertNotNull(columnValue);
         assertEquals(BigDecimal.class, columnValue.getClass());
@@ -688,9 +670,8 @@ public class ReturnTypesMappingIT extends ServerCase {
         Float realValue = 5788.57843f;
         test.setRealColumn(realValue);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectReturnTypesMap1");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap1")).get(0);
         Object columnValue = testRead.get(columnName);
         assertNotNull(columnValue);
 
@@ -729,9 +710,8 @@ public class ReturnTypesMappingIT extends ServerCase {
         Short smallintValue = 32564;
         test.setSmallintColumn(smallintValue);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectReturnTypesMap1");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap1")).get(0);
         Object columnValue = testRead.get(columnName);
         assertNotNull(columnValue);
         assertEquals(Short.class, columnValue.getClass());
@@ -765,9 +745,8 @@ public class ReturnTypesMappingIT extends ServerCase {
         Date timeValue = new Time(cal.getTime().getTime());
         test.setTimeColumn(timeValue);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectReturnTypesMap1");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap1")).get(0);
         Object columnValue = testRead.get(columnName);
         assertNotNull(columnValue);
         assertEquals(Date.class, columnValue.getClass());
@@ -802,9 +781,8 @@ public class ReturnTypesMappingIT extends ServerCase {
         Date timestampValue = Calendar.getInstance().getTime();
         test.setTimestampColumn(timestampValue);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectReturnTypesMap1");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap1")).get(0);
         Object columnValue = testRead.get(columnName);
         assertNotNull(columnValue);
         assertEquals(Date.class, columnValue.getClass());
@@ -842,9 +820,8 @@ public class ReturnTypesMappingIT extends ServerCase {
         Byte tinyintValue = 89;
         test.setTinyintColumn(tinyintValue);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectReturnTypesMap1");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap1")).get(0);
         Object columnValue = testRead.get(columnName);
         assertNotNull(columnValue);
         assertTrue(Short.class.equals(columnValue.getClass()));
@@ -878,9 +855,8 @@ public class ReturnTypesMappingIT extends ServerCase {
             };
             test.setVarbinaryColumn(varbinaryValue);
             context.commitChanges();
-    
-            NamedQuery q = new NamedQuery("SelectReturnTypesMap2");
-            DataRow testRead = (DataRow) context.performQuery(q).get(0);
+			
+            DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap2")).get(0);
             Object columnValue = testRead.get(columnName);
             assertNotNull(columnValue);
             assertEquals(byte[].class, columnValue.getClass());
@@ -916,9 +892,8 @@ public class ReturnTypesMappingIT extends ServerCase {
         String varcharValue = "VARChar string for tests!";
         test.setVarcharColumn(varcharValue);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectReturnTypesMap1");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap1")).get(0);
         Object columnValue = testRead.get(columnName);
         assertNotNull(columnValue);
         assertEquals(String.class, columnValue.getClass());
@@ -933,9 +908,8 @@ public class ReturnTypesMappingIT extends ServerCase {
         String varcharValue = "ی متوازن، نیازی ندارد که ب";
         test.setNVarcharColumn(varcharValue);
         context.commitChanges();
-
-        NamedQuery q = new NamedQuery("SelectReturnTypesMap1");
-        DataRow testRead = (DataRow) context.performQuery(q).get(0);
+		
+        DataRow testRead = (DataRow) context.performQuery(MappedSelect.query("SelectReturnTypesMap1")).get(0);
         Object columnValue = testRead.get(columnName);
         assertNotNull(columnValue);
         assertEquals(String.class, columnValue.getClass());

http://git-wip-us.apache.org/repos/asf/cayenne/blob/212bc53a/cayenne-server/src/test/java/org/apache/cayenne/query/MappedQueryIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/MappedQueryIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/MappedQueryIT.java
new file mode 100644
index 0000000..dd3e12d
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/MappedQueryIT.java
@@ -0,0 +1,180 @@
+/*****************************************************************
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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 org.apache.cayenne.DataRow;
+import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.access.DataContext;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.log.JdbcEventLogger;
+import org.apache.cayenne.test.jdbc.DBHelper;
+import org.apache.cayenne.test.jdbc.TableHelper;
+import org.apache.cayenne.testdo.testmap.Artist;
+import org.apache.cayenne.testdo.testmap.Painting;
+import org.apache.cayenne.tx.BaseTransaction;
+import org.apache.cayenne.tx.ExternalTransaction;
+import org.apache.cayenne.unit.UnitDbAdapter;
+import org.apache.cayenne.unit.di.server.CayenneProjects;
+import org.apache.cayenne.unit.di.server.ServerCase;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+import org.junit.Test;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.*;
+
+@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
+public class MappedQueryIT extends ServerCase {
+
+    @Inject
+    private DataContext context;
+
+    @Inject
+    private DBHelper dbHelper;
+
+    @Inject
+    private UnitDbAdapter accessStackAdapter;
+
+    @Inject
+    private JdbcEventLogger jdbcEventLogger;
+
+    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 testSelectQuery() throws Exception {
+        createArtistsDataSet();
+
+        Artist a = MappedSelect.query("ParameterizedQueryWithLocalCache", Artist.class)
+                .param("name", "artist14").select(context).get(0);
+        assertNotNull(a);
+        assertEquals("artist14", a.getArtistName());
+    }
+
+    @Test
+    public void testSQLTemplateSelect() throws Exception {
+        createArtistsDataSet();
+
+        List<DataRow> result = MappedSelect.query("SelectTestLower", DataRow.class).select(context);
+        assertEquals(20, result.size());
+        assertThat(result.get(0), instanceOf(DataRow.class));
+    }
+
+    @Test
+    public void testSQLTemplateUpdate() throws Exception {
+        int updated = MappedExec.query("NonSelectingQuery").update(context)[0];
+
+        assertEquals(1, updated);
+
+        Painting painting = ObjectSelect.query(Painting.class).selectOne(context);
+        assertEquals("No Painting Like This", painting.getPaintingTitle());
+        assertEquals(BigDecimal.valueOf(12.5), painting.getEstimatedPrice());
+    }
+
+    @Test
+    public void testProcedureQuery() throws Exception {
+        if (!accessStackAdapter.supportsStoredProcedures()) {
+            return;
+        }
+
+        if (!accessStackAdapter.canMakeObjectsOutOfProcedures()) {
+            return;
+        }
+
+        // create an artist with painting in the database
+        Artist a = context.newObject(Artist.class);
+        a.setArtistName("An Artist");
+
+        Painting p = context.newObject(Painting.class);
+        p.setPaintingTitle("A Painting");
+        // converting double to string prevents rounding weirdness...
+        p.setEstimatedPrice(new BigDecimal(1000));
+        a.addToPaintingArray(p);
+
+        context.commitChanges();
+
+        List<?> artists = runProcedureSelect(MappedSelect.query("ProcedureQuery", Artist.class)
+                .param("aName", "An Artist")
+                .param("paintingPrice", 3000).forceNoCache()).firstList();
+
+        // check the results
+        assertNotNull("Null result from StoredProcedure.", artists);
+        assertEquals(1, artists.size());
+        Artist artistRow = (Artist) artists.get(0);
+        assertEquals("An Artist", artistRow.getArtistName());
+    }
+
+    @Test
+    public void testEJBQLQuery() throws Exception {
+        createArtistsDataSet();
+
+        List result = MappedSelect.query("EjbqlQueryTest").select(context);
+        assertEquals(20, result.size());
+        assertThat(result.get(0), instanceOf(DataRow.class));
+    }
+
+    @Test
+    public void testCacheKey() {
+        // ensure queries initialized with different parameters receive different cache keys
+        MappedSelect<Artist> query1 = MappedSelect
+                .query("ParameterizedQueryWithLocalCache", Artist.class).param("name", "artist1");
+        MappedSelect<Artist> query2 = MappedSelect
+                .query("ParameterizedQueryWithLocalCache", Artist.class).param("name", "artist2");
+        MappedSelect<Artist> query3 = MappedSelect
+                .query("ParameterizedQueryWithLocalCache", Artist.class).param("name", "artist2");
+
+        assertNotEquals(
+                query1.getMetaData(context.getEntityResolver()).getCacheKey(),
+                query2.getMetaData(context.getEntityResolver()).getCacheKey());
+        assertEquals(
+                query2.getMetaData(context.getEntityResolver()).getCacheKey(),
+                query3.getMetaData(context.getEntityResolver()).getCacheKey());
+    }
+
+    protected QueryResponse runProcedureSelect(AbstractMappedQuery q) throws Exception {
+        // Sybase blows whenever a transaction wraps a SP, so turn off
+        // transactions
+
+        // TODO: it is quite the opposite with PostgreSQL. If an SP returns an
+        // open refcursor, it actually expects a TX in progress, so while we
+        // don't have refcursor unit tests, this is something to keep in mind
+        // e.g.
+        // http://stackoverflow.com/questions/16921942/porting-apache-cayenne-from-oracle-to-postgresql
+
+        BaseTransaction t = new ExternalTransaction(jdbcEventLogger);
+        BaseTransaction.bindThreadTransaction(t);
+
+        try {
+            return context.performGenericQuery(q);
+        } finally {
+            BaseTransaction.bindThreadTransaction(null);
+            t.commit();
+        }
+    }
+}


Mime
View raw message