All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.cayenne.query.SelectQuery Maven / Gradle / Ivy

There is a newer version: 4.2.1
Show newest version
/*****************************************************************
 *   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;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.map.MapLoader;
import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.map.Procedure;
import org.apache.cayenne.util.XMLEncoder;
import org.apache.cayenne.util.XMLSerializable;

/**
 * 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,
        XMLSerializable {

    public static final String DISTINCT_PROPERTY = "cayenne.SelectQuery.distinct";
    public static final boolean DISTINCT_DEFAULT = false;

    protected List orderings;
    protected boolean distinct;

    SelectQueryMetadata metaData = new SelectQueryMetadata();

    /** Creates an empty SelectQuery. */
    public SelectQuery() {
    }

    /**
     * Creates a SelectQuery with null qualifier, for the specifed ObjEntity
     *
     * @param root the ObjEntity this SelectQuery is for.
     */
    public SelectQuery(ObjEntity root) {
        this(root, null);
    }

    /**
     * Creates a SelectQuery for the specified ObjEntity with the given qualifier.
     *
     * @param root the ObjEntity this SelectQuery is for.
     * @param qualifier an Expression indicating which objects should be fetched
     */
    public SelectQuery(ObjEntity root, Expression qualifier) {
        this(root, qualifier, null);
    }

    /**
     * Creates a SelectQuery for the specified ObjEntity with the given
     * qualifier and orderings.
     *
     * @param root the ObjEntity this SelectQuery is for.
     * @param qualifier an Expression indicating which objects should be fetched.
     * @param orderings defines how to order the results, may be null.
     * @since 3.1
     */
    public SelectQuery(ObjEntity root, Expression qualifier, List orderings) {
        this();
        this.init(root, qualifier);
        addOrderings(orderings);
    }

    /**
     * Creates a SelectQuery that selects all objects of a given persistent class.
     *
     * @param rootClass the Class of objects fetched by this query.
     */
    public SelectQuery(Class rootClass) {
        this(rootClass, null);
    }

    /**
     * Creates a SelectQuery that selects objects of a given persistent class that match
     * supplied qualifier.
     *
     * @param rootClass the Class of objects fetched by this query.
     * @param qualifier an Expression indicating which objects should be fetched.
     */
    public SelectQuery(Class rootClass, Expression qualifier) {
        this(rootClass, qualifier, null);
    }

    /**
     * Creates a SelectQuery that selects objects of a given persistent class that match
     * supplied qualifier.
     *
     * @param rootClass the Class of objects fetched by this query.
     * @param qualifier an Expression indicating which objects should be fetched.
     * @param orderings defines how to order the results, may be null.
     * @since 3.1
     */
    public SelectQuery(Class rootClass, Expression qualifier, List orderings) {
        init(rootClass, qualifier);
        addOrderings(orderings);
    }

    /**
     * Creates a SelectQuery for the specified DbEntity.
     *
     * @param root the DbEntity this SelectQuery is for.
     * @since 1.1
     */
    public SelectQuery(DbEntity root) {
        this(root, null);
    }

    /**
     * Creates a SelectQuery for the specified DbEntity with the given qualifier.
     *
     * @param root the DbEntity this SelectQuery is for.
     * @param qualifier an Expression indicating which objects should be fetched.
     * @since 1.1
     */
    public SelectQuery(DbEntity root, Expression qualifier) {
        this(root, qualifier, null);
    }

    /**
     * Creates a SelectQuery for the specified DbEntity with the given qualifier and orderings.
     *
     * @param root the DbEntity this SelectQuery is for.
     * @param qualifier an Expression indicating which objects should be fetched.
     * @param orderings defines how to order the results, may be null.
     * @since 3.1
     */
    public SelectQuery(DbEntity root, Expression qualifier, List orderings) {
        this();
        this.init(root, qualifier);
        addOrderings(orderings);
    }

    /**
     * Creates SelectQuery with objEntityName parameter.
     */
    public SelectQuery(String objEntityName) {
        this(objEntityName, null);
    }

    /**
     * Creates SelectQuery with objEntityName and qualifier
     * parameters.
     */
    public SelectQuery(String objEntityName, Expression qualifier) {
        this(objEntityName, qualifier, null);
    }

    /**
     * Creates a SelectQuery that selects objects of a given persistent class that match
     * supplied qualifier.
     *
     * @param objEntityName the name of the ObjEntity to fetch from.
     * @param qualifier an Expression indicating which objects should be fetched.
     * @param orderings defines how to order the results, may be null.
     * @since 3.1
     */
    public SelectQuery(String objEntityName, Expression qualifier, List orderings) {
        init(objEntityName, qualifier);
        addOrderings(orderings);
    }

    private void init(Object root, Expression qualifier) {
        this.setRoot(root);
        this.setQualifier(qualifier);
    }

    /**
     * @since 1.2
     */
    @Override
    public QueryMetadata getMetaData(EntityResolver resolver) {
        metaData.resolve(root, resolver, this);

        // must force DataRows if DbEntity is fetched
        if (root instanceof DbEntity) {
            QueryMetadataWrapper wrapper = new QueryMetadataWrapper(metaData);
            wrapper.override(QueryMetadata.FETCHING_DATA_ROWS_PROPERTY, Boolean.TRUE);
            return wrapper;
        }
        else {
            return metaData;
        }
    }

    /**
     * Routes itself and if there are any prefetches configured, creates prefetch queries
     * and routes them as well.
     *
     * @since 1.2
     */
    @Override
    public void route(QueryRouter router, EntityResolver resolver, Query substitutedQuery) {
        super.route(router, resolver, substitutedQuery);

        // suppress prefetches for paginated queries.. instead prefetches will be resolved
        // per row...
        if (metaData.getPageSize() <= 0) {
            routePrefetches(router, resolver);
        }
    }

    /**
     * Creates and routes extra disjoint prefetch queries.
     *
     * @since 1.2
     */
    void routePrefetches(QueryRouter router, EntityResolver resolver) {
        new SelectQueryPrefetchRouterAction().route(this, router, resolver);
    }

    /**
     * Calls "makeSelect" on the visitor.
     *
     * @since 1.2
     */
    @Override
    public SQLAction createSQLAction(SQLActionVisitor visitor) {
        return visitor.objectSelectAction(this);
    }

    /**
     * Initializes query parameters using a set of properties.
     *
     * @since 1.1
     */
    public void initWithProperties(Map 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);
    }

    /**
     * Prints itself as XML to the provided PrintWriter.
     *
     * @since 1.1
     */
    public void encodeAsXML(XMLEncoder encoder) {
        encoder.print(") {
            rootType = MapLoader.JAVA_CLASS_ROOT;
            rootString = ((Class) root).getName();
        }

        if (rootType != null) {
            encoder.print("\" root=\"");
            encoder.print(rootType);
            encoder.print("\" root-name=\"");
            encoder.print(rootString);
        }

        encoder.println("\">");

        encoder.indent(1);

        // print properties
        if (distinct != DISTINCT_DEFAULT) {
            encoder.printProperty(DISTINCT_PROPERTY, distinct);
        }

        metaData.encodeAsXML(encoder);

        // encode qualifier
        if (qualifier != null) {
            encoder.print("");
            qualifier.encodeAsXML(encoder);
            encoder.println("");
        }

        // encode orderings
        if (orderings != null && !orderings.isEmpty()) {
            for (Ordering ordering : orderings) {
                ordering.encodeAsXML(encoder);
            }
        }

        encoder.indent(-1);
        encoder.println("");
    }

    /**
     * A shortcut for {@link #queryWithParameters(Map, boolean)}that prunes parts of
     * qualifier that have no parameter value set.
     */
    public SelectQuery queryWithParameters(Map 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 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 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 orderings) {
        // If the supplied list of orderings is null, do not attempt to add
        // to the collection (addAll() will NPE otherwise).
        if (orderings != null)
            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 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 distinct 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 implicit 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 true if this query should produce a list of data rows as
     * opposed to DataObjects, false for DataObjects. This is a hint to
     * QueryEngine executing this query.
     */
    public boolean isFetchingDataRows() {
        return (root instanceof DbEntity) || metaData.isFetchingDataRows();
    }

    /**
     * Sets query result type. If flag parameter is true, then
     * results will be in the form of data rows.
     * 

* Note that if the root of this query is a {@link DbEntity}, this setting has no * effect, and data rows are always fetched. *

*/ 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 pageSize property. See setPageSize for more details. */ public int getPageSize() { return metaData.getPageSize(); } /** * Sets pageSize property. * * By setting a page size, the Collection returned by performing a query will return * hollow DataObjects. This is considerably faster and uses a tiny fraction of the memory * compared to a non-paged query when large numbers of objects are returned in the result. * When a hollow DataObject is accessed all DataObjects on the same page will be faulted into * memory. There will be a small delay when faulting objects while the data is fetched * from the data source, but otherwise you do not need to do anything special to access data * in hollow objects. The first page is always faulted into memory immediately. * * @param pageSize The pageSize to set */ public void setPageSize(int pageSize) { metaData.setPageSize(pageSize); } /** * Returns a list that internally stores orderings, creating it on demand. * * @since 1.2 */ List nonNullOrderings() { if (orderings == null) { orderings = new ArrayList(3); } return orderings; } /** * Sets statement's fetch size (0 for 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(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy