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


Reply via email to