This is really an amazing contribution! Thank you Scott. Jacopo
On Sep 27, 2014, at 11:22 AM, lekt...@apache.org wrote: > Author: lektran > Date: Sat Sep 27 09:22:31 2014 > New Revision: 1627940 > > URL: http://svn.apache.org/r1627940 > Log: > OFBIZ-4053 Implement an entity query builder to be used as a friendlier API > for executing entity queries. > > Entry point is the static EntityQuery.use(Delegator) method which will then > return an EntityQuery instance whose methods support method chaining to set > query options. > The query can then be executed using the first(), list(), iterator() and > one() methods which respectively return: > - The first result from a result set > - The full list of results from a result set > - An EntityListIterator to iterate over a result set > - The single record from a query that will return only one record (such as a > lookup by primary key) > > Added: > ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityQuery.java > (with props) > > Added: ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityQuery.java > URL: > http://svn.apache.org/viewvc/ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityQuery.java?rev=1627940&view=auto > ============================================================================== > --- ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityQuery.java > (added) > +++ ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityQuery.java > Sat Sep 27 09:22:31 2014 > @@ -0,0 +1,416 @@ > +/******************************************************************************* > + * 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.ofbiz.entity.util; > + > +import java.sql.Timestamp; > +import java.util.Arrays; > +import java.util.List; > +import java.util.Map; > +import java.util.Set; > + > +import org.ofbiz.base.util.Debug; > +import org.ofbiz.base.util.UtilMisc; > +import org.ofbiz.entity.Delegator; > +import org.ofbiz.entity.GenericEntityException; > +import org.ofbiz.entity.GenericValue; > +import org.ofbiz.entity.condition.EntityCondition; > +import org.ofbiz.entity.model.DynamicViewEntity; > +import org.ofbiz.entity.util.EntityFindOptions; > +import org.ofbiz.entity.util.EntityListIterator; > +import org.ofbiz.entity.util.EntityUtil; > + > +/** > + * Used to setup various options for and subsequently execute entity queries. > + * > + * All methods to set options modify the EntityQuery instance then return > this modified object to allow method call chaining. It is > + * important to note that this object is not immutable and is modified > internally, and returning EntityQuery is just a > + * self reference for convenience. > + * > + * After a query the object can be further modified and then used to perform > another query if desired. > + */ > +public class EntityQuery { > + > + public static final String module = EntityQuery.class.getName(); > + > + private Delegator delegator; > + private String entityName = null; > + private DynamicViewEntity dynamicViewEntity = null; > + private boolean useCache = false; > + private EntityCondition whereEntityCondition = null; > + private Set<String> fieldsToSelect = null; > + private List<String> orderBy = null; > + private Integer resultSetType = EntityFindOptions.TYPE_FORWARD_ONLY; > + private Integer fetchSize = null; > + private Integer maxRows = null; > + private Boolean distinct = null; > + private EntityCondition havingEntityCondition = null; > + private boolean filterByDate = false; > + private Timestamp filterByDateMoment; > + > + > + > + /** Construct an EntityQuery object for use against the specified > Delegator > + * @param delegator - The delegator instance to use for the query > + * @return Returns a new EntityQuery object > + */ > + public static EntityQuery use(Delegator delegator) { > + return new EntityQuery(delegator); > + } > + > + /** Construct an EntityQuery object for use against the specified > Delegator > + * @param delegator - The delegator instance to use for the query > + * @return Returns a new EntityQuery object > + */ > + public EntityQuery(Delegator delegator) { > + this.delegator = delegator; > + } > + > + /** Set the fields to be returned when the query is executed. > + * > + * Note that the select methods are not additive, if a subsequent > + * call is made to select then the existing fields for selection > + * will be replaced. > + * @param fieldsToSelect - A Set of Strings containing the field names > to be selected > + * @return this EntityQuery object, to enable chaining > + */ > + public EntityQuery select(Set<String> fieldsToSelect) { > + this.fieldsToSelect = fieldsToSelect; > + return this; > + } > + > + /** Set the fields to be returned when the query is executed. > + * > + * Note that the select methods are not additive, if a subsequent > + * call is made to select then the existing fields for selection > + * will be replaced. > + * @param fieldsToSelect - Strings containing the field names to be > selected > + * @return this EntityQuery object, to enable chaining > + */ > + public EntityQuery select(String...fields) { > + this.fieldsToSelect = UtilMisc.toSetArray(fields); > + return this; > + } > + > + /** Set the entity to query against > + * @param entityName - The name of the entity to query against > + * @return this EntityQuery object, to enable chaining > + */ > + public EntityQuery from(String entityName) { > + this.entityName = entityName; > + this.dynamicViewEntity = null; > + return this; > + } > + > + /** Set the entity to query against > + * @param dynamicViewEntity - The DynamicViewEntity object to query > against > + * @return this EntityQuery object, to enable chaining > + */ > + public EntityQuery from(DynamicViewEntity dynamicViewEntity) { > + this.dynamicViewEntity = dynamicViewEntity; > + this.entityName = null; > + return this; > + } > + > + /** Set the EntityCondition to be used as the WHERE clause for the query > + * > + * NOTE: Each successive call to any of the where(...) methods will > replace the currently set condition for the query. > + * @param entityCondition - An EntityCondition object to be used as the > where clause for this query > + * @return this EntityQuery object, to enable chaining > + */ > + public EntityQuery where(EntityCondition entityCondition) { > + this.whereEntityCondition = entityCondition; > + return this; > + } > + > + /** Set a Map of field name/values to be ANDed together as the WHERE > clause for the query > + * > + * NOTE: Each successive call to any of the where(...) methods will > replace the currently set condition for the query. > + * @param fieldMap - A Map of field names/values to be ANDed together as > the where clause for the query > + * @return this EntityQuery object, to enable chaining > + */ > + public EntityQuery where(Map<String, Object> fieldMap) { > + this.whereEntityCondition = EntityCondition.makeCondition(fieldMap); > + return this; > + } > + > + /** Set a series of field name/values to be ANDed together as the WHERE > clause for the query > + * > + * NOTE: Each successive call to any of the where(...) methods will > replace the currently set condition for the query. > + * @param fieldMap - A series of field names/values to be ANDed together > as the where clause for the query > + * @return this EntityQuery object, to enable chaining > + */ > + public EntityQuery where(Object...fields) { > + this.whereEntityCondition = > EntityCondition.makeCondition(UtilMisc.toMap(fields)); > + return this; > + } > + > + /** Set a list of EntityCondition objects to be ANDed together as the > WHERE clause for the query > + * > + * NOTE: Each successive call to any of the where(...) methods will > replace the currently set condition for the query. > + * @param fieldMap - A list of EntityCondition objects to be ANDed > together as the WHERE clause for the query > + * @return this EntityQuery object, to enable chaining > + */ > + public EntityQuery where(List<EntityCondition> andConditions) { > + this.whereEntityCondition = > EntityCondition.makeCondition(andConditions); > + return this; > + } > + > + /** Set the EntityCondition to be used as the HAVING clause for the > query. > + * > + * NOTE: Each successive call to any of the having(...) methods will > replace the currently set condition for the query. > + * @param entityCondition - The EntityCondition object that specifies > how to constrain > + * this query after any groupings are done (if this is a view > + * entity with group-by aliases) > + * @return this EntityQuery object, to enable chaining > + */ > + public EntityQuery having(EntityCondition entityCondition) { > + this.havingEntityCondition = entityCondition; > + return this; > + } > + > + /** The fields of the named entity to order the resultset by; optionally > add a " ASC" for ascending or " DESC" for descending > + * > + * NOTE: Each successive call to any of the orderBy(...) methods will > replace the currently set orderBy fields for the query. > + * @param orderBy - The fields of the named entity to order the > resultset by > + * @return this EntityQuery object, to enable chaining > + */ > + public EntityQuery orderBy(List<String> orderBy) { > + this.orderBy = orderBy; > + return this; > + } > + > + /** The fields of the named entity to order the resultset by; optionally > add a " ASC" for ascending or " DESC" for descending > + * > + * NOTE: Each successive call to any of the orderBy(...) methods will > replace the currently set orderBy fields for the query. > + * @param orderBy - The fields of the named entity to order the > resultset by > + * @return this EntityQuery object, to enable chaining > + */ > + public EntityQuery orderBy(String...fields) { > + this.orderBy = Arrays.asList(fields); > + return this; > + } > + > + /** Indicate that the ResultSet object's cursor may move only forward > (this is the default behavior) > + * > + * @return this EntityQuery object, to enable chaining > + */ > + public EntityQuery cursorForwardOnly() { > + this.resultSetType = EntityFindOptions.TYPE_FORWARD_ONLY; > + return this; > + } > + > + /** Indicate that the ResultSet object's cursor is scrollable but > generally sensitive to changes to the data that underlies the ResultSet. > + * > + * @return this EntityQuery object, to enable chaining > + */ > + public EntityQuery cursorScrollSensitive() { > + this.resultSetType = EntityFindOptions.TYPE_SCROLL_SENSITIVE; > + return this; > + } > + > + /** Indicate that the ResultSet object's cursor is scrollable but > generally not sensitive to changes to the data that underlies the ResultSet. > + * > + * @return this EntityQuery object, to enable chaining > + */ > + public EntityQuery cursorScrollInsensitive() { > + this.resultSetType = EntityFindOptions.TYPE_SCROLL_INSENSITIVE; > + return this; > + } > + > + /** Specifies the fetch size for this query. -1 will fall back to > datasource settings. > + * > + * @param fetchSize - The fetch size for this query > + * @return this EntityQuery object, to enable chaining > + */ > + public EntityQuery fetchSize(int fetchSize) { > + this.fetchSize = fetchSize; > + return this; > + } > + > + /** Specifies the max number of rows to return, 0 means all rows. > + * > + * @param maxRows - the max number of rows to return > + * @return this EntityQuery object, to enable chaining > + */ > + public EntityQuery maxRows(int maxRows) { > + this.maxRows = maxRows; > + return this; > + } > + > + /** Specifies that the values returned should be filtered to remove > duplicate values. > + * > + * @return this EntityQuery object, to enable chaining > + */ > + public EntityQuery distinct() { > + this.distinct = true; > + return this; > + } > + > + /** Specifies whether the values returned should be filtered to remove > duplicate values. > + * > + * @param distinct - boolean indicating whether the values returned > should be filtered to remove duplicate values > + * @return this EntityQuery object, to enable chaining > + */ > + public EntityQuery distinct(boolean distinct) { > + this.distinct = distinct; > + return this; > + } > + > + /** Specifies whether results should be read from the cache (or written > to the cache if the results have not yet been cached) > + * > + * @return this EntityQuery object, to enable chaining > + */ > + public EntityQuery cache() { > + this.useCache = true; > + return this; > + } > + > + /** Specifies whether the query should return only values that are > currently active using from/thruDate fields. > + * > + * @return this EntityQuery object, to enable chaining > + */ > + public EntityQuery filterByDate() { > + this.filterByDate = true; > + this.filterByDateMoment = null; > + return this; > + } > + > + /** Specifies whether the query should return only values that are > active during the specified moment using from/thruDate fields. > + * > + * @param moment - Timestamp representing the moment in time that the > values should be active during > + * @return this EntityQuery object, to enable chaining > + */ > + public EntityQuery filterByDate(Timestamp moment) { > + this.filterByDate = true; > + this.filterByDateMoment = moment; > + return this; > + } > + > + /** Executes the EntityQuery and returns a list of results > + * > + * @return Returns a List of GenericValues representing the results of > the query > + */ > + public List<GenericValue> queryList() throws GenericEntityException { > + return query(null); > + } > + > + /** Executes the EntityQuery and returns an EntityListIterator > representing the result of the query. > + * > + * NOTE: THAT THIS MUST BE CLOSED (preferably in a finally block) WHEN > YOU > + * ARE DONE WITH IT, AND DON'T LEAVE IT OPEN TOO LONG BEACUSE IT > + * WILL MAINTAIN A DATABASE CONNECTION. > + * > + * @return Returns an EntityListIterator representing the result of the > query > + */ > + public EntityListIterator queryIterator() throws GenericEntityException { > + if (useCache) { > + Debug.logWarning("Call to iterator() with cache, ignoring > cache", module); > + } > + if (dynamicViewEntity == null) { > + return delegator.find(entityName, makeWhereCondition(false), > havingEntityCondition, fieldsToSelect, orderBy, makeEntityFindOptions()); > + } else { > + return delegator.findListIteratorByCondition(dynamicViewEntity, > makeWhereCondition(false), havingEntityCondition, fieldsToSelect, orderBy, > makeEntityFindOptions()); > + } > + } > + > + /** Executes the EntityQuery and returns the first result > + * > + * @return GenericValue representing the first result record from the > query > + */ > + public GenericValue queryFirst() throws GenericEntityException { > + EntityFindOptions efo = makeEntityFindOptions(); > + efo.setMaxRows(1); > + GenericValue result = EntityUtil.getFirst(query(efo)); > + return result; > + } > + > + /** Executes the EntityQuery and a single result record > + * > + * @return GenericValue representing the only result record from the > query > + */ > + public GenericValue queryOne() throws GenericEntityException { > + GenericValue result = EntityUtil.getOnly(queryList()); > + return result; > + } > + > + /** Executes the EntityQuery and returns the result count > + * > + * If the query generates more than a single result then an exception is > thrown > + * > + * @return GenericValue representing the only result record from the > query > + */ > + public long queryCount() throws GenericEntityException { > + if (dynamicViewEntity != null) { > + return queryIterator().getResultsSizeAfterPartialList(); > + } > + return delegator.findCountByCondition(entityName, > makeWhereCondition(false), havingEntityCondition, makeEntityFindOptions()); > + } > + > + private List<GenericValue> query(EntityFindOptions efo) throws > GenericEntityException { > + EntityFindOptions findOptions = null; > + if (efo == null) { > + findOptions = makeEntityFindOptions(); > + } else { > + findOptions = efo; > + } > + List<GenericValue> result = null; > + if (dynamicViewEntity == null) { > + result = delegator.findList(entityName, > makeWhereCondition(useCache), fieldsToSelect, orderBy, findOptions, useCache); > + } else { > + result = queryIterator().getCompleteList(); > + } > + if (filterByDate && useCache) { > + if (filterByDateMoment == null) { > + return EntityUtil.filterByDate(result); > + } else { > + return EntityUtil.filterByDate(result, filterByDateMoment); > + } > + } > + return result; > + } > + > + private EntityFindOptions makeEntityFindOptions() { > + EntityFindOptions findOptions = new EntityFindOptions(); > + if (resultSetType != null) { > + findOptions.setResultSetType(resultSetType); > + } > + if (fetchSize != null) { > + findOptions.setFetchSize(fetchSize); > + } > + if (maxRows != null) { > + findOptions.setMaxRows(maxRows); > + } > + if (distinct != null) { > + findOptions.setDistinct(distinct); > + } > + return findOptions; > + } > + > + private EntityCondition makeWhereCondition(boolean usingCache) { > + // we don't use the useCache field here because not all queries will > actually use the cache, e.g. findCountByCondition never uses the cache > + if (filterByDate && !usingCache) { > + if (filterByDateMoment == null) { > + return EntityCondition.makeCondition(whereEntityCondition, > EntityUtil.getFilterByDateExpr()); > + } else { > + return EntityCondition.makeCondition(whereEntityCondition, > EntityUtil.getFilterByDateExpr(filterByDateMoment)); > + } > + } > + return whereEntityCondition; > + } > +} > > Propchange: > ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityQuery.java > ------------------------------------------------------------------------------ > svn:eol-style = native > > Propchange: > ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityQuery.java > ------------------------------------------------------------------------------ > svn:keywords = "Date Rev Author URL Id" > > Propchange: > ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityQuery.java > ------------------------------------------------------------------------------ > svn:mime-type = text/plain > >