org.apache.cayenne.query.SelectQuery Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cayenne-client-nodeps
Show all versions of cayenne-client-nodeps
Cayenne Object Persistence Framework
/*****************************************************************
* 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.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.map.Procedure;
import org.apache.cayenne.map.QueryBuilder;
import org.apache.cayenne.util.Util;
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.
*
* @author Andrei Adamchik
*/
public class SelectQuery extends QualifiedQuery implements GenericSelectQuery,
ParameterizedQuery, XMLSerializable {
public static final String DISTINCT_PROPERTY = "cayenne.SelectQuery.distinct";
public static final boolean DISTINCT_DEFAULT = false;
protected List customDbAttributes;
protected List orderings;
protected boolean distinct;
protected Expression parentQualifier;
protected String parentObjEntityName;
BaseQueryMetadata selectInfo = new BaseQueryMetadata();
/** 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 specifed 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();
this.init(root, qualifier);
}
/**
* 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.
*/
public SelectQuery(Class rootClass, Expression qualifier) {
init(rootClass, qualifier);
}
/**
* Creates a SelectQuery for the specifed DbEntity.
*
* @param root the DbEntity this SelectQuery is for.
* @since 1.1
*/
public SelectQuery(DbEntity root) {
this(root, null);
}
/**
* Creates a SelectQuery for the specifed 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();
this.init(root, qualifier);
}
/**
* 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) {
init(objEntityName, qualifier);
}
private void init(Object root, Expression qualifier) {
this.setRoot(root);
this.setQualifier(qualifier);
}
/**
* @since 1.2
*/
public QueryMetadata getMetaData(EntityResolver resolver) {
selectInfo.resolve(root, resolver, getName());
// must force DataRows if custom attributes are fetched
if (isFetchingCustomAttributes()) {
QueryMetadataWrapper wrapper = new QueryMetadataWrapper(selectInfo);
wrapper.override(QueryMetadata.FETCHING_DATA_ROWS_PROPERTY, Boolean.TRUE);
return wrapper;
}
else {
return selectInfo;
}
}
/**
* Routes itself and if there are any prefetches configured, creates prefetch queries
* and routes them as well.
*
* @since 1.2
*/
public void route(QueryRouter router, EntityResolver resolver, Query substitutedQuery) {
super.route(router, resolver, substitutedQuery);
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
*/
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;
selectInfo.initWithProperties(properties);
}
/**
* Prints itself as XML to the provided PrintWriter.
*
* @since 1.1
*/
public void encodeAsXML(XMLEncoder encoder) {
encoder.print("");
encoder.indent(1);
// print properties
if (distinct != DISTINCT_DEFAULT) {
encoder.printProperty(DISTINCT_PROPERTY, distinct);
}
selectInfo.encodeAsXML(encoder);
// encode qualifier
if (qualifier != null) {
encoder.print("");
qualifier.encodeAsXML(encoder);
encoder.println(" ");
}
// encode orderings
if (orderings != null && !orderings.isEmpty()) {
Iterator it = orderings.iterator();
while (it.hasNext()) {
Ordering ordering = (Ordering) it.next();
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.selectInfo.copyFromInfo(this.selectInfo);
query.setParentObjEntityName(parentObjEntityName);
query.setParentQualifier(parentQualifier);
query.setRoot(root);
// The following algorithm is for building the new query name based
// on the original query name and a hashcode of the map of parameters.
// This way the query clone can take advantage of caching. Fixes
// problem reported in CAY-360.
if (!Util.isEmptyString(name)) {
StringBuffer buffer = new StringBuffer(name);
if (parameters != null && !parameters.isEmpty()) {
buffer.append(parameters.hashCode());
}
query.setName(buffer.toString());
}
if (orderings != null) {
query.addOrderings(orderings);
}
if (customDbAttributes != null) {
query.addCustomDbAttributes(customDbAttributes);
}
// 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) {
nonNullOrderings().addAll(orderings);
}
/** Adds ordering specification to this query orderings. */
public void addOrdering(String sortPathSpec, boolean isAscending) {
this.addOrdering(new Ordering(sortPathSpec, isAscending));
}
/** Adds ordering specification to this query orderings. */
public void addOrdering(String sortPathSpec, boolean isAscending, boolean ignoreCase) {
this.addOrdering(new Ordering(sortPathSpec, isAscending, ignoreCase));
}
/**
* 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;
}
/**
* Returns a list of attributes that will be included in the results of this query.
*/
public List getCustomDbAttributes() {
// if query root is DbEntity, and no custom attributes
// are defined, return DbEntity attributes.
if ((customDbAttributes == null || customDbAttributes.isEmpty())
&& (root instanceof DbEntity)) {
Collection attributes = ((DbEntity) root).getAttributes();
List attributeNames = new ArrayList(attributes.size());
Iterator it = attributes.iterator();
while (it.hasNext()) {
DbAttribute attribute = (DbAttribute) it.next();
attributeNames.add(attribute.getName());
}
return attributeNames;
}
else {
return (customDbAttributes != null)
? customDbAttributes
: Collections.EMPTY_LIST;
}
}
/**
* Adds a path to the DbAttribute that should be included in the results of this
* query. Valid paths would look like ARTIST_NAME
,
* PAINTING_ARRAY.PAINTING_ID
, etc.
*/
public void addCustomDbAttribute(String attributePath) {
nonNullCustomDbAttributes().add(attributePath);
}
public void addCustomDbAttributes(List attrPaths) {
nonNullCustomDbAttributes().addAll(attrPaths);
}
/**
* Returns true
if there is at least one custom query attribute
* specified, otherwise returns false
for the case when the query
* results will contain only the root entity attributes.
*
* Note that queries that are fetching custom attributes always return data rows
* instead of DataObjects.
*
*/
public boolean isFetchingCustomAttributes() {
return (root instanceof DbEntity)
|| (customDbAttributes != null && !customDbAttributes.isEmpty());
}
/**
* Returns a collection of String paths indicating relationships to objects that are
* prefetched together with this query.
*
* @deprecated since 1.2 use 'getPrefetchTree' to explore prefetches.
*/
public Collection getPrefetches() {
if (getPrefetchTree() != null) {
Collection nodes = getPrefetchTree().nonPhantomNodes();
Collection paths = new ArrayList(nodes.size());
Iterator it = nodes.iterator();
while (it.hasNext()) {
paths.add(((PrefetchTreeNode) it.next()).getPath());
}
return paths;
}
return Collections.EMPTY_LIST;
}
/**
* @since 1.2
*/
public PrefetchTreeNode getPrefetchTree() {
return selectInfo.getPrefetchTree();
}
/**
* @since 1.2
*/
public void setPrefetchTree(PrefetchTreeNode prefetchTree) {
selectInfo.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 selectInfo.addPrefetch(prefetchPath, PrefetchTreeNode.UNDEFINED_SEMANTICS);
}
/**
* Adds all prefetches to the internal prefetch set.
*
* @deprecated since 1.2
*/
public void addPrefetches(Collection prefetches) {
selectInfo.addPrefetches(prefetches, PrefetchTreeNode.UNDEFINED_SEMANTICS);
}
/**
* Clears all stored prefetch paths.
*/
public void clearPrefetches() {
selectInfo.clearPrefetches();
}
/**
* Removes prefetch.
*
* @since 1.1
*/
public void removePrefetch(String prefetchPath) {
selectInfo.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 this.isFetchingCustomAttributes() || selectInfo.isFetchingDataRows();
}
/**
* Sets query result type. If flag
parameter is true
,
* then results will be in the form of data rows.
*
* Note that if isFetchingCustAttributes()
returns true
,
* this setting has no effect, and data rows are always fetched.
*
*/
public void setFetchingDataRows(boolean flag) {
selectInfo.setFetchingDataRows(flag);
}
/**
* Returns refresh policy of this query. Default is true
.
*
* @since 1.1
*/
public boolean isRefreshingObjects() {
return selectInfo.isRefreshingObjects();
}
/**
* @since 1.1
*/
public void setRefreshingObjects(boolean flag) {
selectInfo.setRefreshingObjects(flag);
}
/**
* @since 1.1
*/
public String getCachePolicy() {
return selectInfo.getCachePolicy();
}
/**
* @since 1.1
*/
public void setCachePolicy(String policy) {
this.selectInfo.setCachePolicy(policy);
}
/**
* Returns the fetchLimit.
*
* @return int
*/
public int getFetchLimit() {
return selectInfo.getFetchLimit();
}
/**
* Sets the fetchLimit.
*
* @param fetchLimit The fetchLimit to set
*/
public void setFetchLimit(int fetchLimit) {
this.selectInfo.setFetchLimit(fetchLimit);
}
/** Setter for query's parent entity qualifier. */
public void setParentQualifier(Expression parentQualifier) {
this.parentQualifier = parentQualifier;
}
/** Getter for query parent entity qualifier. */
public Expression getParentQualifier() {
return parentQualifier;
}
/**
* Adds specified parent entity qualifier to the existing parent entity qualifier
* joining it using "AND".
*/
public void andParentQualifier(Expression e) {
parentQualifier = (parentQualifier != null) ? parentQualifier.andExp(e) : e;
}
/**
* Adds specified parent entity qualifier to the existing qualifier joining it using
* "OR".
*/
public void orParentQualifier(Expression e) {
parentQualifier = (parentQualifier != null) ? parentQualifier.orExp(e) : e;
}
/**
* Returns the name of parent ObjEntity.
*
* @return String
*/
public String getParentObjEntityName() {
return parentObjEntityName;
}
/**
* Sets the name of parent ObjEntity. If query's root ObjEntity maps to a derived
* entity in the DataMap, this query qualifier will resolve to a HAVING clause of an
* SQL statement. To allow fine tuning the query before applying GROUP BY and HAVING,
* callers can setup the name of parent ObjEntity and parent qualifier that will be
* used to create WHERE clause preceeding GROUP BY.
*
* For instance this is helpful to qualify the fetch on a related entity attributes,
* since HAVING does not allow joins.
*
*
* @param parentObjEntityName The parentObjEntityName to set
*/
public void setParentObjEntityName(String parentObjEntityName) {
this.parentObjEntityName = parentObjEntityName;
}
/**
* Returns true
if this query has an extra qualifier that uses a parent
* entity of the query root entity for additional result filtering.
*/
public boolean isQualifiedOnParent() {
return getParentObjEntityName() != null && parentQualifier != null;
}
/**
* Returns pageSize
property. Page size is a hint telling Cayenne
* QueryEngine that query result should use paging instead of reading the whole result
* in the memory.
*
* @return int
*/
public int getPageSize() {
return selectInfo.getPageSize();
}
/**
* Sets pageSize
property.
*
* @param pageSize The pageSize to set
*/
public void setPageSize(int pageSize) {
selectInfo.setPageSize(pageSize);
}
/**
* Returns true if objects fetched via this query should be fully resolved according
* to the inheritance hierarchy.
*
* @since 1.1
*/
public boolean isResolvingInherited() {
return selectInfo.isResolvingInherited();
}
/**
* Sets whether the objects fetched via this query should be fully resolved according
* to the inheritance hierarchy.
*
* @since 1.1
*/
public void setResolvingInherited(boolean b) {
selectInfo.setResolvingInherited(b);
}
/**
* Returns a list that internally stores custom db attributes, creating it on demand.
*
* @since 1.2
*/
List nonNullCustomDbAttributes() {
if (customDbAttributes == null) {
customDbAttributes = new ArrayList();
}
return customDbAttributes;
}
/**
* Returns a list that internally stores orderings, creating it on demand.
*
* @since 1.2
*/
List nonNullOrderings() {
if (orderings == null) {
orderings = new ArrayList();
}
return orderings;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy