You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by an...@apache.org on 2010/03/10 20:12:06 UTC

svn commit: r921518 [2/2] - in /cayenne/sandbox/cayenne-gwt/src: main/java/org/apache/cayenne/gwt/client/ main/java/org/apache/cayenne/gwt/server/ main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/ main/resources/org/apache/cayenne/query/...

Added: cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/query/SQLTemplate.java
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/query/SQLTemplate.java?rev=921518&view=auto
==============================================================================
--- cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/query/SQLTemplate.java (added)
+++ cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/query/SQLTemplate.java Wed Mar 10 19:12:05 2010
@@ -0,0 +1,485 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.query;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.cayenne.map.SQLResult;
+import org.apache.commons.collections.Transformer;
+
+/**
+ * A query that executes unchanged (except for template preprocessing) "raw" SQL specified
+ * by the user. <h3>Template Script</h3>
+ * <p>
+ * SQLTemplate stores a dynamic template for the SQL query that supports parameters and
+ * customization using Velocity scripting language. The most straightforward use of
+ * scripting abilities is to build parameterized queries. For example:
+ * </p>
+ * 
+ * <pre>
+ *                  SELECT ID, NAME FROM SOME_TABLE WHERE NAME LIKE $a
+ * </pre>
+ * <p>
+ * <i>For advanced scripting options see "Scripting SQLTemplate" chapter in the User
+ * Guide. </i>
+ * </p>
+ * <h3>Per-Database Template Customization</h3>
+ * <p>
+ * SQLTemplate has a {@link #getDefaultTemplate() default template script}, but also it
+ * allows to configure multiple templates and switch them dynamically. This way a single
+ * query can have multiple "dialects" specific to a given database.
+ * </p>
+ * <h3>Parameter Sets</h3>
+ * <p>
+ * SQLTemplate supports multiple sets of parameters, so a single query can be executed
+ * multiple times with different parameters. "Scrolling" through parameter list is done by
+ * calling {@link #parametersIterator()}. This iterator goes over parameter sets,
+ * returning a Map on each call to "next()"
+ * </p>
+ * 
+ * @since 1.1
+ */
+public class SQLTemplate extends AbstractQuery implements ParameterizedQuery {
+
+    static final String COLUMN_NAME_CAPITALIZATION_PROPERTY = "cayenne.SQLTemplate.columnNameCapitalization";
+    
+    private static final Transformer nullMapTransformer = new Transformer() {
+
+        public Object transform(Object input) {
+            return (input != null) ? input : Collections.EMPTY_MAP;
+        }
+    };
+
+    protected String defaultTemplate;
+    protected Map<String, String> templates;
+    protected Map<String, ?>[] parameters;
+    protected CapsStrategy columnNamesCapitalization;
+    protected SQLResult result;
+
+    SQLTemplateMetadata metaData = new SQLTemplateMetadata();
+
+    /**
+     * Creates an empty SQLTemplate. Note this constructor does not specify the "root" of
+     * the query, so a user must call "setRoot" later to make sure SQLTemplate can be
+     * executed.
+     * 
+     * @since 1.2
+     */
+    public SQLTemplate() {
+    }
+
+    /**
+     * @since 1.2
+     */
+    public SQLTemplate(String objEntityName, String defaultTemplate) {
+        setRoot(objEntityName);
+        setDefaultTemplate(defaultTemplate);
+    }
+
+    /**
+     * Initializes query parameters using a set of properties.
+     * 
+     * @since 1.1
+     */
+    public void initWithProperties(Map<String, ?> properties) {
+        // must init defaults even if properties are empty
+        metaData.initWithProperties(properties);
+
+        if (properties == null) {
+            properties = Collections.EMPTY_MAP;
+        }
+
+        Object columnNamesCapitalization = properties
+                .get(COLUMN_NAME_CAPITALIZATION_PROPERTY);
+        this.columnNamesCapitalization = (columnNamesCapitalization != null)
+                ? CapsStrategy.valueOf(columnNamesCapitalization
+                        .toString()
+                        .toUpperCase())
+                : null;
+    }
+
+    /**
+     * Returns the number of parameter sets.
+     */
+    public int parametersSize() {
+        return (parameters != null) ? parameters.length : 0;
+    }
+
+    /**
+     * Returns a new query built using this query as a prototype and a new set of
+     * parameters.
+     */
+    public SQLTemplate queryWithParameters(Map<String, ?>... parameters) {
+        // create a query replica
+        SQLTemplate query = new SQLTemplate();
+
+        query.setRoot(root);
+        query.setDefaultTemplate(getDefaultTemplate());
+
+        if (templates != null) {
+            query.templates = new HashMap<String, String>(templates);
+        }
+
+        query.metaData.copyFromInfo(this.metaData);
+        query.setParameters(parameters);
+        query.setColumnNamesCapitalization(this.getColumnNamesCapitalization());
+
+        return query;
+    }
+
+    /**
+     * Creates and returns a new SQLTemplate built using this query as a prototype and
+     * substituting template parameters with the values from the map.
+     * 
+     * @since 1.1
+     */
+    public Query createQuery(Map<String, ?> parameters) {
+        return queryWithParameters(parameters);
+    }
+
+    /**
+     * @deprecated since 3.0 {@link #getCacheStrategy()} replaces this method.
+     */
+    @Deprecated
+    public String getCachePolicy() {
+        return metaData.getCachePolicy();
+    }
+
+    /**
+     * @deprecated since 3.0 {@link #setCacheStrategy(QueryCacheStrategy)} replaces this
+     *             method.
+     */
+    @Deprecated
+    public void setCachePolicy(String policy) {
+        metaData.setCachePolicy(policy);
+    }
+
+    /**
+     * @since 3.0
+     */
+    public QueryCacheStrategy getCacheStrategy() {
+        return metaData.getCacheStrategy();
+    }
+
+    /**
+     * @since 3.0
+     */
+    public void setCacheStrategy(QueryCacheStrategy strategy) {
+        metaData.setCacheStrategy(strategy);
+    }
+
+    /**
+     * @since 3.0
+     */
+    public String[] getCacheGroups() {
+        return metaData.getCacheGroups();
+    }
+
+    /**
+     * @since 3.0
+     */
+    public void setCacheGroups(String... cacheGroups) {
+        this.metaData.setCacheGroups(cacheGroups);
+    }
+
+    public int getFetchLimit() {
+        return metaData.getFetchLimit();
+    }
+
+    public void setFetchLimit(int fetchLimit) {
+        this.metaData.setFetchLimit(fetchLimit);
+    }
+
+    /**
+     * @since 3.0
+     */
+    public int getFetchOffset() {
+        return metaData.getFetchOffset();
+    }
+
+    /**
+     * @since 3.0
+     */
+    public void setFetchOffset(int fetchOffset) {
+        metaData.setFetchOffset(fetchOffset);
+    }
+
+    public int getPageSize() {
+        return metaData.getPageSize();
+    }
+
+    public void setPageSize(int pageSize) {
+        metaData.setPageSize(pageSize);
+    }
+
+    public void setFetchingDataRows(boolean flag) {
+        metaData.setFetchingDataRows(flag);
+    }
+
+    public boolean isFetchingDataRows() {
+        return metaData.isFetchingDataRows();
+    }
+
+    /**
+     * @deprecated since 3.0. With introduction of the cache strategies this setting is
+     *             redundant, although it is still being taken into account. It will be
+     *             removed in the later versions of Cayenne.
+     */
+    @Deprecated
+    public boolean isRefreshingObjects() {
+        return metaData.isRefreshingObjects();
+    }
+
+    /**
+     * @deprecated since 3.0. With introduction of the cache strategies this setting is
+     *             redundant, although it is still being taken into account. It will be
+     *             removed in the later versions of Cayenne.
+     */
+    @Deprecated
+    public void setRefreshingObjects(boolean flag) {
+        // noop
+    }
+
+    /**
+     * @deprecated since 3.0. Inheritance resolving is not optional anymore.
+     */
+    @Deprecated
+    public boolean isResolvingInherited() {
+        return true;
+    }
+
+    /**
+     * @deprecated since 3.0. Inheritance resolving is not optional anymore.
+     */
+    @Deprecated
+    public void setResolvingInherited(boolean b) {
+        // noop
+    }
+
+    /**
+     * Returns default SQL template for this query.
+     */
+    public String getDefaultTemplate() {
+        return defaultTemplate;
+    }
+
+    /**
+     * Sets default SQL template for this query.
+     */
+    public void setDefaultTemplate(String string) {
+        defaultTemplate = string;
+    }
+
+    /**
+     * Returns a template for key, or a default template if a template for key is not
+     * found.
+     */
+    public synchronized String getTemplate(String key) {
+        if (templates == null) {
+            return defaultTemplate;
+        }
+
+        String template = templates.get(key);
+        return (template != null) ? template : defaultTemplate;
+    }
+
+    /**
+     * Returns template for key, or null if there is no template configured for this key.
+     * Unlike {@link #getTemplate(String)}this method does not return a default template
+     * as a failover strategy, rather it returns null.
+     */
+    public synchronized String getCustomTemplate(String key) {
+        return (templates != null) ? templates.get(key) : null;
+    }
+
+    /**
+     * Adds a SQL template string for a given key. Note the the keys understood by Cayenne
+     * must be fully qualified adapter class names. This way the framework can related
+     * current DataNode to the right template. E.g.
+     * "org.apache.cayenne.dba.oracle.OracleAdapter" is a key that should be used to setup
+     * an Oracle-specific template.
+     * 
+     * @see #setDefaultTemplate(String)
+     */
+    public synchronized void setTemplate(String key, String template) {
+        if (templates == null) {
+            templates = new HashMap<String, String>();
+        }
+
+        templates.put(key, template);
+    }
+
+    public synchronized void removeTemplate(String key) {
+        if (templates != null) {
+            templates.remove(key);
+        }
+    }
+
+    /**
+     * Returns a collection of configured template keys.
+     */
+    public synchronized Collection<String> getTemplateKeys() {
+        return (templates != null) ? Collections.unmodifiableCollection(templates
+                .keySet()) : Collections.EMPTY_LIST;
+    }
+
+    /**
+     * Utility method to get the first set of parameters, since most queries will only
+     * have one.
+     */
+    public Map<String, ?> getParameters() {
+        Map<String, ?> map = (parameters != null && parameters.length > 0)
+                ? parameters[0]
+                : null;
+        return (map != null) ? map : Collections.EMPTY_MAP;
+    }
+
+    /**
+     * Utility method to initialize query with one or more sets of parameters.
+     */
+    public void setParameters(Map<String, ?>... parameters) {
+
+        if (parameters == null) {
+            this.parameters = null;
+        }
+        else {
+            // clone parameters to ensure that we don't have immutable maps that are not
+            // serializable with Hessian...
+            this.parameters = new Map[parameters.length];
+            for (int i = 0; i < parameters.length; i++) {
+                this.parameters[i] = parameters[i] != null ? new HashMap<String, Object>(
+                        parameters[i]) : new HashMap<String, Object>();
+            }
+        }
+    }
+
+    /**
+     * @since 1.2
+     */
+    public PrefetchTreeNode getPrefetchTree() {
+        return metaData.getPrefetchTree();
+    }
+
+    /**
+     * Adds a prefetch.
+     * 
+     * @since 1.2
+     */
+    public PrefetchTreeNode addPrefetch(String prefetchPath) {
+        // by default use JOINT_PREFETCH_SEMANTICS
+        return metaData.addPrefetch(
+                prefetchPath,
+                PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
+    }
+
+    /**
+     * @since 1.2
+     */
+    public void removePrefetch(String prefetch) {
+        metaData.removePrefetch(prefetch);
+    }
+
+    /**
+     * Adds all prefetches from a provided collection.
+     * 
+     * @since 1.2
+     */
+    public void addPrefetches(Collection<String> prefetches) {
+        metaData.addPrefetches(prefetches, PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
+    }
+
+    /**
+     * Clears all prefetches.
+     * 
+     * @since 1.2
+     */
+    public void clearPrefetches() {
+        metaData.clearPrefetches();
+    }
+
+    /**
+     * Returns a column name capitalization policy applied to selecting queries. This is
+     * used to simplify mapping of the queries like "SELECT * FROM ...", ensuring that a
+     * chosen Cayenne column mapping strategy (e.g. all column names in uppercase) is
+     * portable across database engines that can have varying default capitalization.
+     * Default (null) value indicates that column names provided in result set are used
+     * unchanged.
+     * 
+     * @since 3.0
+     */
+    public CapsStrategy getColumnNamesCapitalization() {
+        return columnNamesCapitalization != null
+                ? columnNamesCapitalization
+                : CapsStrategy.DEFAULT;
+    }
+
+    /**
+     * Sets a column name capitalization policy applied to selecting queries. This is used
+     * to simplify mapping of the queries like "SELECT * FROM ...", ensuring that a chosen
+     * Cayenne column mapping strategy (e.g. all column names in uppercase) is portable
+     * across database engines that can have varying default capitalization. Default
+     * (null) value indicates that column names provided in result set are used unchanged.
+     * <p/>
+     * Note that while a non-default setting is useful for queries that do not rely on a
+     * #result directive to describe columns, it works for all SQLTemplates the same way.
+     * 
+     * @since 3.0
+     */
+    public void setColumnNamesCapitalization(CapsStrategy columnNameCapitalization) {
+        this.columnNamesCapitalization = columnNameCapitalization;
+    }
+
+    /**
+     * Sets an optional explicit mapping of the result set. If result set mapping is
+     * specified, the result of SQLTemplate may not be a normal list of Persistent objects
+     * or DataRows, instead it will follow the {@link SQLResult} rules.
+     * 
+     * @since 3.0
+     */
+    public void setResult(SQLResult resultSet) {
+        this.result = resultSet;
+    }
+
+    /**
+     * @since 3.0
+     */
+    public SQLResult getResult() {
+        return result;
+    }
+    
+    /**
+     * Sets statement's fetch size (0 for no default size)
+     * @since 3.0 
+     */
+    public void setStatementFetchSize(int size) {
+        metaData.setStatementFetchSize(size);
+    }
+    
+    /**
+     * @return statement's fetch size
+     * @since 3.0
+     */
+    public int getStatementFetchSize() {
+        return metaData.getStatementFetchSize();
+    }
+}

Added: cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/query/SQLTemplateMetadata.java
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/query/SQLTemplateMetadata.java?rev=921518&view=auto
==============================================================================
--- cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/query/SQLTemplateMetadata.java (added)
+++ cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/query/SQLTemplateMetadata.java Wed Mar 10 19:12:05 2010
@@ -0,0 +1,26 @@
+/*****************************************************************
+ *   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;
+
+
+/**
+ * @since 3.0
+ */
+class SQLTemplateMetadata extends BaseQueryMetadata {
+}

Added: cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/query/SelectQuery.java
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/query/SelectQuery.java?rev=921518&view=auto
==============================================================================
--- cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/query/SelectQuery.java (added)
+++ cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/query/SelectQuery.java Wed Mar 10 19:12:05 2010
@@ -0,0 +1,375 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.query;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.exp.ExpressionFactory;
+
+/**
+ * A query that selects persistent objects of a certain type or "raw data" (aka DataRows).
+ * Supports expression qualifier, multiple orderings and a number of other parameters that
+ * serve as runtime hints to Cayenne on how to optimize the fetch and result processing.
+ */
+public class SelectQuery extends QualifiedQuery implements ParameterizedQuery {
+
+    public static final String DISTINCT_PROPERTY = "cayenne.SelectQuery.distinct";
+    public static final boolean DISTINCT_DEFAULT = false;
+
+    protected List<Ordering> orderings;
+    protected boolean distinct;
+
+    SelectQueryMetadata metaData = new SelectQueryMetadata();
+
+    /** Creates an empty SelectQuery. */
+    public SelectQuery() {
+    }
+
+    /**
+     * Creates SelectQuery with <code>objEntityName</code> parameter.
+     */
+    public SelectQuery(String objEntityName) {
+        this(objEntityName, null);
+    }
+
+    /**
+     * Creates SelectQuery with <code>objEntityName</code> and <code>qualifier</code>
+     * parameters.
+     */
+    public SelectQuery(String objEntityName, Expression qualifier) {
+        init(objEntityName, qualifier);
+    }
+
+    private void init(Object root, Expression qualifier) {
+        this.setRoot(root);
+        this.setQualifier(qualifier);
+    }
+
+    /**
+     * Initializes query parameters using a set of properties.
+     * 
+     * @since 1.1
+     */
+    public void initWithProperties(Map<String, ?> properties) {
+
+        // must init defaults even if properties are empty
+        if (properties == null) {
+            properties = Collections.EMPTY_MAP;
+        }
+
+        Object distinct = properties.get(DISTINCT_PROPERTY);
+
+        // init ivars from properties
+        this.distinct = (distinct != null)
+                ? "true".equalsIgnoreCase(distinct.toString())
+                : DISTINCT_DEFAULT;
+
+        metaData.initWithProperties(properties);
+    }
+
+    /**
+     * A shortcut for {@link #queryWithParameters(Map, boolean)}that prunes parts of
+     * qualifier that have no parameter value set.
+     */
+    public SelectQuery queryWithParameters(Map<String, ?> parameters) {
+        return queryWithParameters(parameters, true);
+    }
+
+    /**
+     * Returns a query built using this query as a prototype, using a set of parameters to
+     * build the qualifier.
+     * 
+     * @see org.apache.cayenne.exp.Expression#expWithParameters(java.util.Map, boolean)
+     *      parameter substitution.
+     */
+    public SelectQuery queryWithParameters(Map<String, ?> parameters, boolean pruneMissing) {
+        // create a query replica
+        SelectQuery query = new SelectQuery();
+        query.setDistinct(distinct);
+
+        query.metaData.copyFromInfo(this.metaData);
+        query.setRoot(root);
+
+        if (orderings != null) {
+            query.addOrderings(orderings);
+        }
+
+        // substitute qualifier parameters
+        if (qualifier != null) {
+            query.setQualifier(qualifier.expWithParameters(parameters, pruneMissing));
+        }
+
+        return query;
+    }
+
+    /**
+     * Creates and returns a new SelectQuery built using this query as a prototype and
+     * substituting qualifier parameters with the values from the map.
+     * 
+     * @since 1.1
+     */
+    public Query createQuery(Map<String, ?> parameters) {
+        return queryWithParameters(parameters);
+    }
+
+    /**
+     * Adds ordering specification to this query orderings.
+     */
+    public void addOrdering(Ordering ordering) {
+        nonNullOrderings().add(ordering);
+    }
+
+    /**
+     * Adds a list of orderings.
+     */
+    public void addOrderings(List<Ordering> orderings) {
+        nonNullOrderings().addAll(orderings);
+    }
+
+    /**
+     * Adds ordering specification to this query orderings.
+     * 
+     * @since 3.0
+     */
+    public void addOrdering(String sortPathSpec, SortOrder order) {
+        addOrdering(new Ordering(sortPathSpec, order));
+    }
+
+    /**
+     * Removes ordering.
+     * 
+     * @since 1.1
+     */
+    public void removeOrdering(Ordering ordering) {
+        if (orderings != null) {
+            orderings.remove(ordering);
+        }
+    }
+
+    /**
+     * Returns a list of orderings used by this query.
+     */
+    public List<Ordering> getOrderings() {
+        return (orderings != null) ? orderings : Collections.EMPTY_LIST;
+    }
+
+    /**
+     * Clears all configured orderings.
+     */
+    public void clearOrderings() {
+        orderings = null;
+    }
+
+    /**
+     * Returns true if this query returns distinct rows.
+     */
+    public boolean isDistinct() {
+        return distinct;
+    }
+
+    /**
+     * Sets <code>distinct</code> property that determines whether this query returns
+     * distinct row.
+     */
+    public void setDistinct(boolean distinct) {
+        this.distinct = distinct;
+    }
+
+    /**
+     * Adds one or more aliases for the qualifier expression path. Aliases serve to
+     * instruct Cayenne to generate separate sets of joins for overlapping paths, that
+     * maybe needed for complex conditions. An example of an <i>implicit<i> splits is this
+     * method: {@link ExpressionFactory#matchAllExp(String, Object...)}.
+     * 
+     * @since 3.0
+     */
+    public void aliasPathSplits(String path, String... aliases) {
+        metaData.addPathSplitAliases(path, aliases);
+    }
+
+    /**
+     * @since 1.2
+     */
+    public PrefetchTreeNode getPrefetchTree() {
+        return metaData.getPrefetchTree();
+    }
+
+    /**
+     * @since 1.2
+     */
+    public void setPrefetchTree(PrefetchTreeNode prefetchTree) {
+        metaData.setPrefetchTree(prefetchTree);
+    }
+
+    /**
+     * Adds a prefetch with specified relationship path to the query.
+     * 
+     * @since 1.2 signature changed to return created PrefetchTreeNode.
+     */
+    public PrefetchTreeNode addPrefetch(String prefetchPath) {
+        return metaData.addPrefetch(prefetchPath, PrefetchTreeNode.UNDEFINED_SEMANTICS);
+    }
+
+    /**
+     * Clears all stored prefetch paths.
+     */
+    public void clearPrefetches() {
+        metaData.clearPrefetches();
+    }
+
+    /**
+     * Removes prefetch.
+     * 
+     * @since 1.1
+     */
+    public void removePrefetch(String prefetchPath) {
+        metaData.removePrefetch(prefetchPath);
+    }
+
+    /**
+     * 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.
+     */
+    public boolean isFetchingDataRows() {
+        return metaData.isFetchingDataRows();
+    }
+
+    /**
+     * Sets query result type. If <code>flag</code> parameter is <code>true</code>, then
+     * results will be in the form of data rows.
+     * <p>
+     * </p>
+     */
+    public void setFetchingDataRows(boolean flag) {
+        metaData.setFetchingDataRows(flag);
+    }
+
+    /**
+     * @since 3.0
+     */
+    public QueryCacheStrategy getCacheStrategy() {
+        return metaData.getCacheStrategy();
+    }
+
+    /**
+     * @since 3.0
+     */
+    public void setCacheStrategy(QueryCacheStrategy strategy) {
+        metaData.setCacheStrategy(strategy);
+    }
+
+    /**
+     * @since 3.0
+     */
+    public String[] getCacheGroups() {
+        return metaData.getCacheGroups();
+    }
+
+    /**
+     * @since 3.0
+     */
+    public void setCacheGroups(String... cacheGroups) {
+        this.metaData.setCacheGroups(cacheGroups);
+    }
+
+    /**
+     * Returns the fetchOffset.
+     * 
+     * @since 3.0
+     */
+    public int getFetchOffset() {
+        return metaData.getFetchOffset();
+    }
+
+    /**
+     * Returns the fetchLimit.
+     */
+    public int getFetchLimit() {
+        return metaData.getFetchLimit();
+    }
+
+    /**
+     * Sets the fetchLimit.
+     */
+    public void setFetchLimit(int fetchLimit) {
+        this.metaData.setFetchLimit(fetchLimit);
+    }
+
+    /**
+     * @since 3.0
+     */
+    public void setFetchOffset(int fetchOffset) {
+        this.metaData.setFetchOffset(fetchOffset);
+    }
+
+    /**
+     * Returns <code>pageSize</code> property. Page size is a hint telling Cayenne
+     * QueryEngine that query result should use paging instead of reading the whole result
+     * in the memory.
+     */
+    public int getPageSize() {
+        return metaData.getPageSize();
+    }
+
+    /**
+     * Sets <code>pageSize</code> property.
+     * 
+     * @param pageSize The pageSize to set
+     */
+    public void setPageSize(int pageSize) {
+    	throw new UnsupportedOperationException("Paging is not supported on GWT client");
+        //metaData.setPageSize(pageSize);
+    }
+
+    /**
+     * Returns a list that internally stores orderings, creating it on demand.
+     * 
+     * @since 1.2
+     */
+    List<Ordering> nonNullOrderings() {
+        if (orderings == null) {
+            orderings = new ArrayList<Ordering>(3);
+        }
+
+        return orderings;
+    }
+
+    /**
+     * Sets statement's fetch size (0 for no default size)
+     * 
+     * @since 3.0
+     */
+    public void setStatementFetchSize(int size) {
+        metaData.setStatementFetchSize(size);
+    }
+
+    /**
+     * @return statement's fetch size
+     * @since 3.0
+     */
+    public int getStatementFetchSize() {
+        return metaData.getStatementFetchSize();
+    }
+}

Added: cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/query/SelectQueryMetadata.java
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/query/SelectQueryMetadata.java?rev=921518&view=auto
==============================================================================
--- cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/query/SelectQueryMetadata.java (added)
+++ cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/query/SelectQueryMetadata.java Wed Mar 10 19:12:05 2010
@@ -0,0 +1,66 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.query;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @since 3.0
+ */
+class SelectQueryMetadata extends BaseQueryMetadata {
+
+    Map<String, String> pathSplitAliases;
+
+    @Override
+    void copyFromInfo(QueryMetadata info) {
+        super.copyFromInfo(info);
+        this.pathSplitAliases = new HashMap<String, String>(info.getPathSplitAliases());
+    }
+
+    /**
+     * @since 3.0
+     */
+    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);
+        }
+    }
+}

Added: cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/query/SortOrder.java
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/query/SortOrder.java?rev=921518&view=auto
==============================================================================
--- cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/query/SortOrder.java (added)
+++ cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/query/SortOrder.java Wed Mar 10 19:12:05 2010
@@ -0,0 +1,34 @@
+/*****************************************************************
+ *   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;
+
+/**
+ * Constants to order query results (the ORDER BY clause).
+ */
+public enum SortOrder {
+    /**
+     * ASCENDING = Ascending order, case sensitive.<br/>
+     * ASCENDING_INSENSITIVE = Ascending order, case insensitive<br/>
+     * DESCENDING = Descending order, case sensitive.<br>
+     * DESCENDING_INSENSITIVE = Descending order, case insensitive.<br>
+     */
+    ASCENDING,  ASCENDING_INSENSITIVE,
+    DESCENDING, DESCENDING_INSENSITIVE
+}

Added: cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/util/GenericResponse.java
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/util/GenericResponse.java?rev=921518&view=auto
==============================================================================
--- cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/util/GenericResponse.java (added)
+++ cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/util/GenericResponse.java Wed Mar 10 19:12:05 2010
@@ -0,0 +1,150 @@
+/*****************************************************************
+ *   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.util;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.cayenne.QueryResponse;
+
+/**
+ * A simple serializable implementation of QueryResponse.
+ * 
+ * @since 1.2
+ */
+public class GenericResponse implements QueryResponse, Serializable {
+
+    protected List results;
+
+    protected transient int currentIndex;
+
+    /**
+     * Creates an empty BaseResponse.
+     */
+    public GenericResponse() {
+        results = new ArrayList();
+    }
+
+    /**
+     * Creates a BaseResponse with a single result list.
+     */
+    public GenericResponse(List list) {
+        results = new ArrayList(1);
+        addResultList(list);
+    }
+
+    /**
+     * Creates a response that it a shallow copy of another response.
+     */
+    public GenericResponse(QueryResponse response) {
+
+        results = new ArrayList(response.size());
+
+        response.reset();
+        while (response.next()) {
+            if (response.isList()) {
+                addResultList(response.currentList());
+            }
+            else {
+                addBatchUpdateCount(response.currentUpdateCount());
+            }
+        }
+    }
+
+    public List firstList() {
+        for (reset(); next();) {
+            if (isList()) {
+                return currentList();
+            }
+        }
+
+        return null;
+    }
+
+    public int[] firstUpdateCount() {
+        for (reset(); next();) {
+            if (!isList()) {
+                return currentUpdateCount();
+            }
+        }
+
+        return null;
+    }
+
+    public List currentList() {
+        return (List) results.get(currentIndex - 1);
+    }
+
+    public int[] currentUpdateCount() {
+        return (int[]) results.get(currentIndex - 1);
+    }
+
+    public boolean isList() {
+        return results.get(currentIndex - 1) instanceof List;
+    }
+
+    public boolean next() {
+        return ++currentIndex <= results.size();
+    }
+
+    public void reset() {
+        // use a zero-based index, not -1, as this will simplify serialization handling
+        currentIndex = 0;
+    }
+
+    public int size() {
+        return results.size();
+    }
+
+    /**
+     * Clears any previously collected information.
+     */
+    public void clear() {
+        results.clear();
+    }
+
+    public void addBatchUpdateCount(int[] resultCount) {
+
+        if (resultCount != null) {
+            results.add(resultCount);
+        }
+    }
+
+    public void addUpdateCount(int resultCount) {
+        results.add(new int[] {
+            resultCount
+        });
+    }
+
+    public void addResultList(List list) {
+        this.results.add(list);
+    }
+
+    /**
+     * Replaces previously stored result with a new result.
+     */
+    public void replaceResult(Object oldResult, Object newResult) {
+        int index = results.indexOf(oldResult);
+        if (index >= 0) {
+            results.set(index, newResult);
+        }
+    }
+}

Added: cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/util/ListResponse.java
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/util/ListResponse.java?rev=921518&view=auto
==============================================================================
--- cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/util/ListResponse.java (added)
+++ cayenne/sandbox/cayenne-gwt/src/main/resources/org/apache/cayenne/query/emul/org/apache/cayenne/util/ListResponse.java Wed Mar 10 19:12:05 2010
@@ -0,0 +1,95 @@
+/*****************************************************************
+ *   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.util;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.cayenne.QueryResponse;
+
+/**
+ * A QueryResponse optimized to hold a single object or data row list.
+ * 
+ * @since 1.2
+ */
+public class ListResponse implements QueryResponse, Serializable {
+
+    protected List objectList;
+
+    protected transient int currentIndex;
+
+    /**
+     * Creates an empty response.
+     */
+    public ListResponse() {
+        this.objectList = new ArrayList(1);
+    }
+
+    public ListResponse(Object object) {
+        this.objectList = Collections.singletonList(object);
+    }
+
+    public ListResponse(List objectList) {
+        this.objectList = objectList;
+    }
+
+    public int size() {
+        return 1;
+    }
+
+    public boolean isList() {
+        if (currentIndex != 1) {
+            throw new IndexOutOfBoundsException("Past iteration end: " + currentIndex);
+        }
+
+        return true;
+    }
+
+    public List currentList() {
+        if (currentIndex != 1) {
+            throw new IndexOutOfBoundsException("Past iteration end: " + currentIndex);
+        }
+
+        return objectList;
+    }
+
+    public int[] currentUpdateCount() {
+        throw new IllegalStateException("Current object is not an update count");
+    }
+
+    public boolean next() {
+        return ++currentIndex <= 1;
+    }
+
+    public void reset() {
+        // use a zero-based index, not -1, as this will simplify serialization handling
+        currentIndex = 0;
+    }
+
+    public List firstList() {
+        return objectList;
+    }
+
+    public int[] firstUpdateCount() {
+        return new int[0];
+    }
+}

Modified: cayenne/sandbox/cayenne-gwt/src/test/java/org/apache/cayenne/gwt/test/client/GwtTestQuery.java
URL: http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-gwt/src/test/java/org/apache/cayenne/gwt/test/client/GwtTestQuery.java?rev=921518&r1=921517&r2=921518&view=diff
==============================================================================
--- cayenne/sandbox/cayenne-gwt/src/test/java/org/apache/cayenne/gwt/test/client/GwtTestQuery.java (original)
+++ cayenne/sandbox/cayenne-gwt/src/test/java/org/apache/cayenne/gwt/test/client/GwtTestQuery.java Wed Mar 10 19:12:05 2010
@@ -18,13 +18,16 @@
  ****************************************************************/
 package org.apache.cayenne.gwt.test.client;
 
+import java.util.Collections;
 import java.util.List;
 
 import org.apache.cayenne.DataRow;
+import org.apache.cayenne.QueryResponse;
 import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.gwt.client.GWTDataContext;
 import org.apache.cayenne.gwt.test.client.model.Tree;
 import org.apache.cayenne.query.Query;
+import org.apache.cayenne.query.SQLTemplate;
 import org.apache.cayenne.query.SelectQuery;
 import org.apache.cayenne.query.SortOrder;
 
@@ -40,8 +43,6 @@ public class GwtTestQuery extends Cayenn
 				Tree pine = result.get(0);
 				assertEquals(pine.getName(), "Pine");
 				assertEquals(result.get(1).getName(), "Fir");
-				
-				finishTest();
 			}
 		});
 	}
@@ -55,8 +56,6 @@ public class GwtTestQuery extends Cayenn
 				assertEquals(result.size(), 1);
 				Tree pine = result.get(0);
 				assertEquals(pine.getName(), "Pine");
-				
-				finishTest();
 			}
 		});
 	}
@@ -73,8 +72,6 @@ public class GwtTestQuery extends Cayenn
 				assertEquals(result.size(), 1);
 				DataRow pine = result.get(0);
 				assertEquals(pine.get("name"), "Pine");
-				
-				finishTest();
 			}
 		});
 	}
@@ -90,4 +87,22 @@ public class GwtTestQuery extends Cayenn
 		
 		delayTestFinish(500);
 	}
+	
+	public void testSQLTemplate() {
+		SQLTemplate template = new SQLTemplate("Tree", "DELETE FROM TREE WHERE TREE_ID=#bind($id)");
+		template.setParameters(Collections.singletonMap("id", 0));
+		GWTDataContext.getInstance().performGenericQuery(template, new TestCallback<QueryResponse>() {
+			@Override
+			public void onSuccess(QueryResponse result) {
+				//mock assert, but at least we check that GenericResponse was tranferred correctly
+				int[] res = result.firstUpdateCount();
+				assertEquals(res.length, 1);
+				assertEquals(res[0], 0);
+				
+				finishTest();
+			}
+		});
+		
+		delayTestFinish(500);
+	}
 }