You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ofbiz.apache.org by Hans Bakker <ma...@antwebsystems.com> on 2014/10/02 02:48:32 UTC
Re: svn commit: r1627940 - /ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityQuery.java
Hi Scott,
this is indeed nice, although a duplicate of an existing feature....yes
every advantage often has a disadvantage. :-)
However, can you please add a junit test? These days in the continuous
integration age i do not think it is acceptable anymore adding new
functions to the existing API without Junit test?
Regards,
Hans
On 27/09/14 16:22, lektran@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
>
>