public class ERXQuery extends Object
NSArray<NSDictionary<String,Object>> records =
ERXQuery.create()
.select(keys)
.from(entity)
.where(qualifier)
.groupBy(groupings)
.having(havingQualifier)
.orderBy(sortings)
.fetch();
It allows you to use EOF/Wonder higher-level constructs (qualifiers, attributes,
orderings, key paths, ERXKeys, etc.) to create a query that looks like this:
SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ...
The ERXKeys and String objects correspond to keys and key paths to the attributes to fetch, i.e. "customer.name". The keys and key paths can also be relationships to objects, i.e. "customer" which translate into a fetch of foreign keys used to build object faults and return them in the results.
You may call the select() method multiple times to keep adding to the list of attributes to fetch.
// Using a single query against the order entity to count the number of
// orders and line items that match an order qualifier.
ERXQueryAttributes attributes = ERXQueryAttributes.create(orderEntity)
.add("itemCount", "COUNT(DISTINCT lineItems.lineItemID)", "intNumber")
.add("orderCount", "COUNT(DISTINCT orderID)", "intNumber");
ERXQuery query =
ERXQuery.create()
.select(attributes)
.from(orderEntity)
.where(qualifier);
// Fetch into a dictionary
NSDictionary<String,Object> row = query.fetch().lastObject();
int orderCount = ((Number) row.objectForKey("orderCount")).intValue();
int itemCount = ((Number) row.objectForKey("itemCount")).intValue();
// Fetch into object instances of the a custom Result class
Result result = query.fetch(editingContext, Result.class).lastObject();
int orderCount = result.orderCount();
int itemCount = result.itemCount();
The Result custom class would have to be defined as
shown below. The constructor may keep the mutable dictionary passed in to the
constructor or make an immutable copy from it as shown below.
public static class Result {
NSDictionary<String,Object> data;
public Result(EOEditingContext ec, NSMutableDictionary<String,Object>
row) {
data = row.immutableClone();
}
public int itemCount() {
return ((Number) data.objectForKey("itemCount")).intValue();
}
public int orderCount() {
return ((Number) data.objectForKey("orderCount")).intValue();
}
}
In general, fetching into a custom class can be done in several ways:
// If your custom class has a constructor that takes an editing context and
// a mutable dictionary then it is very simple:
NSArray<Foo>
objs = query.fetch(editingContext, Foo.class);
// Using java 8 or later you may use a lambda expression:
NSArray<Foo>
objs = query.fetch(editingContext, (ec, row) -> new Foo(ec, row));
// You may also create an implementation of the RecordConstructor
// functional interface and pass it into the fetch method:
ERXQuery.RecordConstructor<Foo>
recordConstructor =
new ERXQuery.RecordConstructor<Foo>
{
@Override
public Foo constructRecord(EOEditingContext ec, NSMutableDictionary<String,Object>
row) {
return new Foo(ec, row);
}
};
NSArray objs = query.fetch(editingContext, recordConstructor);
NSDictionary<String,Object> recordInitializationValues = new NSDictionary<>((Object)2017, "preferredYear");
NSArray<Foo> objs = query.fetch(editingContext, recordInitializationValues, Foo.class);
Foo aFoo = objs.lastObject();
int preferredYear = aFoo.preferredYear(); // i.e. 2017
One incovenience is that eogeneration templates do not generate ERXKeys for non-class properties. However, this problem could be overcome by enhancing the eogeneration templates to generate ERXKeys for derived non-class property attributes.
// Fetch last year's customer order totals exceeding $1000 in descending order
NSArray<OrderSummary>
lastYearTopSales =
ERXQuery.create()
.select(Order.CUSTOMER) // customer to-one
.select(Order.SUM_TOTAL_AMOUNT) // non-class property defined as SUM(totalAmount)
.from(Order.ENTITY_NAME)
.where(lastYearQualifier)
.groupBy(Order.CUSTOMER)
.having(Order.SUM_TOTAL_AMOUNT.greaterThan(1000.00))
.orderBy(Order.SUM_TOTAL_AMOUNT.desc())
.fetch(editingContext, OrderSummary.class);
// Peek at top sale record
OrderSummary topSale = ERXArrayUtilities.firstObject(lastYearTopSales);
if (topSale != null) {
System.out.println("Customer " + topSale.customer().fullName()
+ " ordered " + moneyFormatter.format(topSale.sumTotalAmount()));
}
It would be nice to enhance the eogeneration templates to also create a custom
class for fetching the results, i.e. WonderEntitySummary.java and _WonderEntitySummary.java
with the getters for attributes/relationships in the entity including derived non-class
properties. These templates would be used when the entity has a user info key with
ERXQuery.enabled=yes.
The current workaround used by ERXQuery is to temporarily add to the entity any
ad hoc attributes referenced by the qualifiers. This typically happens with the
havingQualifier which normally references the ad hoc attributes corresponding to
aggregated attributes. For example, "sumTotalAmount"
defined as
"SUM(totalAmount)"
could be used in a having qualifier:
// When grouping orders by customer and fetching sumTotalAmount we may want to have
// this having qualifier so that we only fetch the groups totaling more than 1000.
EOQualifier havingQualifier = ERXQ.greaterThan("sumTotalAmount", new BigDecimal(1000.0));
However, if you were to define your "sumTotalAmount"
attribute in your entity
as a derived non-class property with definition "SUM(totalAmount)"
then ERXQuery
doesn't have to add the attribute to the entity.
Modifier and Type | Class and Description |
---|---|
static class |
ERXQuery.DefaultRecordConstructor
This is the default constructor used by the fetch() and fetch(EOEditingContext)
methods.
|
static class |
ERXQuery.EntityModificationAction |
static class |
ERXQuery.Record
Convenience class for fetching data into records conforming to key value coding.
|
static interface |
ERXQuery.RecordConstructor<T>
Functional interface for constructing a record from a dictionary
with the data fetched from the database.
|
Modifier | Constructor and Description |
---|---|
protected |
ERXQuery() |
Modifier and Type | Method and Description |
---|---|
protected String |
addLimitClause(EOEntity entity,
String statement,
int limit)
Turns the SQL statement into something like this:
|
ERXQuery |
clientFetchLimit(int limit)
If specified then the fetch will be stopped/canceled after fetching
clientFetchLimit records.
|
protected void |
computeSelectAndGroupingAttributes() |
static ERXQuery |
create() |
protected String |
databaseProductName(EOEntity entity) |
static EOEntity |
destinationEntity(EOEntity rootEntity,
String keyPath)
Returns the destination entity for this report attribute.
|
static EOProperty |
destinationProperty(EOEntity rootEntity,
String keyPath)
Returns whether the property (either EOAttribute or EORelationship) referenced by
the last component in the key path.
|
protected EOAttribute |
existingOrNewAttributeForKey(String keyPath)
Called by getExpression() to get the attribute or create an
ad-hoc attribute for a key.
|
NSArray<NSDictionary<String,Object>> |
fetch()
Does the fetch and returns the values.
|
NSArray<NSDictionary<String,Object>> |
fetch(EOEditingContext ec)
Does the fetch and returns the values.
|
<T> NSArray<T> |
fetch(EOEditingContext ec,
Class<T> recordClass)
Returns fetch(ec, recordClass, NSDictionary.emptyDictionary())
|
<T> NSArray<T> |
fetch(EOEditingContext ec,
ERXQuery.RecordConstructor<T> recordConstructor)
Convenience method to return fetch(ec, NSDictionary.emptyDictionary(), recordConstructor)
|
NSArray<NSDictionary<String,Object>> |
fetch(EOEditingContext ec,
NSDictionary<String,Object> recordInitializationValues) |
<T> NSArray<T> |
fetch(EOEditingContext anEC,
NSDictionary<String,Object> recordInitializationValues,
Class<T> recordClass)
Use this method to fetch results into objects of the specified class.
|
<T> NSArray<T> |
fetch(EOEditingContext ec,
NSDictionary<String,Object> recordInitializationValues,
ERXQuery.RecordConstructor<T> recordConstructor)
Use this method to fetch either by using an implementation of the RecordConstructor
functional interface or by using a lambda expression as follows:
|
protected <T> NSArray<T> |
fetch(EOSQLExpression expression,
NSArray<EOAttribute> fetchAttributes,
EOEditingContext ec,
NSDictionary<String,Object> initValues,
ERXQuery.RecordConstructor<T> recordConstructor)
Core fetch method.
|
Object |
fetchValue()
Convenience method for fetching a single attribute from a query resulting in zero
or one record.
|
protected String |
formattedTimestampForInlineUse(EOSQLExpression sqlExpression,
Date timestamp,
EOAttribute attribute) |
protected String |
formatValueForAttributeForInlineUse(EOSQLExpression sqlExpression,
Object value,
EOAttribute attribute)
Formats the value for inline use.
|
ERXQuery |
from(EOEntity entity)
Specifies the EOEntity object to select from.
|
ERXQuery |
from(String entityName)
Specifies the name of EOEntity object to select from.
|
EOSQLExpression |
getExpression(EOEditingContext ec)
Returns a complete select expression as follows:
|
ERXQuery |
groupBy(Object... attributesOrKeys)
Use this to specify the attributes to group by.
|
ERXQuery |
having(EOQualifier qual)
Specifies the qualifier for the having clause.
|
ERXQuery |
orderBy(Object... orderingObjects)
Specifies the sort orderings used to build the order by clause.
|
double |
queryEvaluationTime() |
ERXQuery |
refreshingRefetchedObjects()
Specifies whether to refresh refetched objects referenced
by relationship keys, i.e.
|
protected EOQualifier |
restrictingQualifierForReferencedEntities()
Returns a qualifier by combining the restricting qualifiers (if any) in
the entities referenced.
|
ERXQuery |
select(Object... attributesOrKeys)
Specifies the attributes to fetch.
|
NSArray<EOAttribute> |
selectAttributes()
Returns the array containing the EOAttributes that ERXQuery used to
fetch the results.
|
ERXQuery |
selectCount()
Specifies whether to select count(*)
|
ERXQuery |
serverFetchLimit(int limit)
If specified then the query will be wrapped with something like
this, depending on the database product:
|
protected static void |
setupAdaptorChannelEOAttributes(EOAdaptorChannel adaptorChannel,
NSArray<EOAttribute> selectAttributes) |
protected static EOSQLExpressionFactory |
sqlExpressionFactory(EOEntity anEntity,
EOEditingContext ec)
Returns a new EOSQLExpressionFactory for the entity and editing context specified.
|
protected String |
sqlStringForAttribute(EOSQLExpression e,
EOAttribute a)
Returns the SQL string corresponding to attribute a.
|
String |
sqlStringForAttributeValue(EOSQLExpression e,
EOAttribute att,
Object value)
Uses the EOSQLExpression provided to get the SQL string for value and
corresponding attribute.
|
String |
sqlStringForOrderingAttribute(EOSQLExpression e,
EOAttribute orderingAttribute,
NSSelector selector)
Returns the SQL string for the ordering of attribute a using the specified selector.
|
protected String |
sqlWithBindingsInline(String sql,
EOSQLExpression expression)
Returns the SQL for the EOSQLExpression specified but with the place holder
characters (?) replaced with their corresponding value from the bindings and
formatted for in-line use.
|
protected String |
sqlWithBindingsInline2(String sql,
EOSQLExpression expression)
Returns the SQL for the EOSQLExpression specified but with the place holder
characters (?) replaced with their corresponding value from the bindings and
formatted for inline use.
|
protected RuntimeException |
unknownPropertyException(String keyPath) |
ERXQuery |
usingBindVariables()
Enables use of bind variables.
|
ERXQuery |
usingDistinct()
Specifies whether or not to use DISTINCT.
|
ERXQuery |
usingQueryHint(String value)
This string is inserted after the SELECT keyword in the generated SQL
padded with a space on both sides.
|
ERXQuery |
usingRelationshipAlias(String relationshipName,
String alias)
Sets the table alias to use for a given relationship name.
|
ERXQuery |
where(EOQualifier qual)
Specifies the main qualifier used to build the where clause.
|
protected String |
wrapped(String leftHandSide,
String statement,
String rightHandSide) |
protected EOEditingContext editingContext
protected EOEntity mainEntity
protected EOQualifier mainSelectQualifier
protected EOQualifier havingQualifier
protected NSMutableArray<EOSortOrdering> orderings
protected NSMutableDictionary<String,String> relationshipAliases
protected boolean usesDistinct
protected boolean isCountingStatement
protected String queryHint
protected boolean useBindVariables
protected NSMutableArray<String> fetchKeys
protected NSMutableArray<String> groupingKeys
protected NSMutableArray<EOAttribute> adHocAttributes
protected NSMutableArray<EOAttribute> adHocGroupings
protected NSMutableArray<EOAttribute> selectAttributes
protected NSMutableArray<EOAttribute> groupingAttributes
protected NSMutableDictionary<String,EOAttribute> attributesByName
protected NSMutableSet<er.extensions.eof.ERXQuery.RelationshipKeyInfo> relationshipKeysSet
protected boolean refreshRefetchedObjects
protected int serverFetchLimit
protected int clientFetchLimit
protected double queryEvaluationTime
public static ERXQuery create()
public ERXQuery selectCount()
public ERXQuery select(Object... attributesOrKeys)
attributesOrKeys
- list of attributes to select in the fetchpublic ERXQuery usingDistinct()
public ERXQuery refreshingRefetchedObjects()
public ERXQuery from(EOEntity entity)
entity
- the entity to fetchpublic ERXQuery from(String entityName)
entityName
- the entity to fetchpublic ERXQuery where(EOQualifier qual)
qual
- the qualifier for the fetchpublic ERXQuery groupBy(Object... attributesOrKeys)
attributesOrKeys
- list of attributes to use for the group bypublic ERXQuery orderBy(Object... orderingObjects)
orderingObjects
- sort orderspublic ERXQuery having(EOQualifier qual)
qual
- qualifier for the having clausepublic ERXQuery usingQueryHint(String value)
value
- query hintpublic ERXQuery usingBindVariables()
public ERXQuery serverFetchLimit(int limit)
SELECT * FROM ( query ) WHERE ROWNUM <= limit
limit
- max number of rows in resultpublic ERXQuery clientFetchLimit(int limit)
limit
- max number of rows in resultpublic NSArray<NSDictionary<String,Object>> fetch()
public NSArray<NSDictionary<String,Object>> fetch(EOEditingContext ec)
ec
- the editing context to use for the fetchpublic NSArray<NSDictionary<String,Object>> fetch(EOEditingContext ec, NSDictionary<String,Object> recordInitializationValues)
public <T> NSArray<T> fetch(EOEditingContext ec, Class<T> recordClass)
ec
- the editing context to use for the fetchrecordClass
- class to use for record entriespublic <T> NSArray<T> fetch(EOEditingContext anEC, NSDictionary<String,Object> recordInitializationValues, Class<T> recordClass)
anEC
- the editing context to use for the fetchrecordInitializationValues
- values to add as record entries to resultrecordClass
- class to use for record entriespublic <T> NSArray<T> fetch(EOEditingContext ec, NSDictionary<String,Object> recordInitializationValues, ERXQuery.RecordConstructor<T> recordConstructor)
public class Foo {
NSMutableDictionary<String,Object>
data;
// Constructor
public Foo(EOEditingContext ec, NSMutableDictionary<String,Object>
row) {
data = row;
}
}
EOEditingContext editingContext = ERXEC.newEditingContext();
ERXQuery query = ...;
// This assumes Java <= 7
ERXQuery.RecordConstructor<Foo>
recordConstructor =
new ERXQuery.RecordConstructor<Foo>
(){
@Override
public Foo constructRecord(EOEditingContext ec, NSMutableDictionary<String,Object>
row) {
return new Foo(ec, row);
}
};
NSArray<Foo>
foos = query.fetch(editingContext, recordConstructor);
// This assumes Java >= 8
NSArray<Foo>
foos = query.fetch(editingContext, (ec, row) -> new Foo(ec, row));
ec
- the editing context to use for the fetchrecordInitializationValues
- values to add as record entries to resultrecordConstructor
- constructor for record entriesrecordClass
- class to use for record entriespublic <T> NSArray<T> fetch(EOEditingContext ec, ERXQuery.RecordConstructor<T> recordConstructor)
ec
- the editing context to use for the fetchrecordConstructor
- constructor for record entriesprotected <T> NSArray<T> fetch(EOSQLExpression expression, NSArray<EOAttribute> fetchAttributes, EOEditingContext ec, NSDictionary<String,Object> initValues, ERXQuery.RecordConstructor<T> recordConstructor)
NSArray<T>
expression
- the SQL expression for the fetchfetchAttributes
- attributes to fetchec
- the editing context to use for the fetchinitValues
- values to add as record entries to resultrecordConstructor
- constructor for record entriesprotected static void setupAdaptorChannelEOAttributes(EOAdaptorChannel adaptorChannel, NSArray<EOAttribute> selectAttributes)
public Object fetchValue()
public ERXQuery usingRelationshipAlias(String relationshipName, String alias)
relationshipName
- name of relationshipalias
- alias name to usepublic EOSQLExpression getExpression(EOEditingContext ec)
SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ...The
WHERE
clause is constructed from the qualifier (if any) passed
into the where()
method and any required joins necessary to access
any referenced properties.
The GROUP BY
clause is constructed from the grouping attributes
specified by calling any of the groupBy()
methods.
The HAVING
clause is constructed if a qualifier is specified by
calling the having()
method.
The ORDER BY
clause is constructed from attributes passed in to
the orderBy()
method.
ec
- the editing context to use for SQL constructionprotected String addLimitClause(EOEntity entity, String statement, int limit)
"SELECT * FROM ( " + statement + " ) WHERE ROWNUM <= " + limit;
entity
- entity on which to fetchstatement
- SQL statementlimit
- max number of rows in resultpublic NSArray<EOAttribute> selectAttributes()
protected void computeSelectAndGroupingAttributes()
protected EOAttribute existingOrNewAttributeForKey(String keyPath)
keyPath
- a key pathprotected RuntimeException unknownPropertyException(String keyPath)
public static EOEntity destinationEntity(EOEntity rootEntity, String keyPath)
rootEntity
- starting entitykeyPath
- a key pathpublic static EOProperty destinationProperty(EOEntity rootEntity, String keyPath)
rootEntity
- starting entitykeyPath
- a key pathprotected static EOSQLExpressionFactory sqlExpressionFactory(EOEntity anEntity, EOEditingContext ec)
anEntity
- an entityec
- an editing contextprotected EOQualifier restrictingQualifierForReferencedEntities()
protected String sqlStringForAttribute(EOSQLExpression e, EOAttribute a)
e
- an SQL expressiona
- attribute for SQL stringpublic String sqlStringForOrderingAttribute(EOSQLExpression e, EOAttribute orderingAttribute, NSSelector selector)
e
- an SQL expressionorderingAttribute
- attribute to sort byselector
- a sort ordering selector to useprotected String sqlWithBindingsInline(String sql, EOSQLExpression expression)
sql
- SQL string to convertexpression
- an SQL expressionprotected String sqlWithBindingsInline2(String sql, EOSQLExpression expression)
sql
- SQL string to convertexpression
- an SQL expressionpublic String sqlStringForAttributeValue(EOSQLExpression e, EOAttribute att, Object value)
e
- an SQL expressionatt
- an attributevalue
- value to use with attributeprotected String formatValueForAttributeForInlineUse(EOSQLExpression sqlExpression, Object value, EOAttribute attribute)
sqlExpression
- an SQL expressionvalue
- value to use with attributeattribute
- an attributeprotected String formattedTimestampForInlineUse(EOSQLExpression sqlExpression, Date timestamp, EOAttribute attribute)
public double queryEvaluationTime()
Copyright © 2002 – 2024 Project Wonder.