9.1 Query Components
The JDO query facility applies a Boolean filter to a collection of
candidate instances and returns the instances that evaluate to
true. The collection of candidate instances can be
either an Extent or a
Collection. The class of candidate instances is
another query component. Instances are returned in the query result
only if they are instances of the candidate class.
Let's begin by examining a method that performs a
query that accesses Customer instances in the
Media Mania model. We assume that an application has started a
transaction and called queryCustomers( ), passing
the PersistenceManager instance and values to
filter the Customer instances to those whose
addresses are in a specific city and state.
public static void queryCustomers(PersistenceManager pm,
String city, String state) {
Extent customerExtent = pm.getExtent(Customer.class, true); [1]
String filter = "address.city == city && state == address.state"; [2]
Query query = pm.newQuery(customerExtent, filter); [3]
query.declareParameters("String city, String state"); [4]
query.setOrdering( [5]
"address.zipcode ascending, lastName ascending, firstName ascending");
Collection result = (Collection) query.execute(city, state); [6]
Iterator iter = result.iterator( );
while (iter.hasNext( )) { [7]
Customer customer = (Customer) iter.next( );
Address address = customer.getAddress( );
System.out.print(address.getZipcode( )); System.out.print(" ");
System.out.print(customer.getFirstName( )); System.out.print(" ");
System.out.print(customer.getLastName( )); System.out.print(" ");
System.out.println(address.getStreet( ));
}
query.close(result); [8]
}
This code performs a query on the Customer extent,
which we access on line [1]. When we create
the Query instance on line [3], we provide the Customer
extent as the collection of candidate instances to be evaluated in
the query. When you use an Extent, as we have
here, it also identifies the class of the candidate instances. We use
the candidate class to establish the namespace for the identifiers
used in the query filter. Line [2] specifies
the filter for the query. It uses the Customer
field address and navigates to the associated
Address instance to access the
city and state fields. The
city and state identifiers in
the filter are query parameters, which are declared on line [4]. We access all Customer
instances that live in a specific city and state. The Java
== operator expresses equality, and the Java
operator && performs a conditional AND
operation. You will find JDOQL very easy to learn, because it uses
Java operators and syntax. You also express your queries using the
identifiers in your object model. On line [5], we establish an ordering for the instances
that are in the query result. First we order customers based on their
ZIP code; we then order all customers in the same ZIP code by their
last name and then first name, all in ascending order. This ordering
specification is similar to SQL's
ORDER BY clause.
Line [6] executes the query. We pass the
city and state method
parameters to execute( ) as query parameters,
which are also named city and
state. It is not necessary for the method
parameters to have the same names as the query parameters, but we do
so to make it clear to anyone reading the code that they are
associated. Line [4] declares the query
parameters and their order. The order in this declaration establishes
the order that the query parameter values should be passed to
execute( ) on line [6].
The result of the query must be cast to a
Collection in JDO 1.0.1. The execute(
) method is defined to return Object, to
allow for future extensions that may return a single instance. In
general, you should call iterator( ) only on the
return value of execute( ). Once we have an
Iterator, we can iterate through all the returned
Customer instances. The code also navigates from
the returned Customer instance to its associated
Address instance. Once we are done with the query
result, we close it on line [8].
Every query requires three components:
- Class of candidate instances
-
This specifies the class of the instances that should be included in
the query result. All of the candidate instances should be of this
class or one of its subclasses. The class provides a scope for the
names in the query filter, similar to the scope established for field
names in a Java class definition. In the previous example, the
Customer extent established the class of candidate
instances when we called newQuery( ).
- Collection of candidate instances
-
The collection of candidate instances is either a
java.util.Collection or an
Extent. We used the Extent for
the Customer class in the previous example. We use
the Extent when we intend the query to be filtered
by the datastore, not by in-memory processing. The
Collection might be a previous query result,
allowing for subqueries. If you do not explicitly provide the
collection of candidate instances but you do provide the class of
candidate instances, the candidate collection defaults to the extent
of the class of candidate instances, including subclass instances.
Any instances in the collection of candidate instances that are not
of this class are silently ignored and are not included in the query
result. This can occur when the set of candidate instances is a
Collection containing instances of multiple
classes.
- Query filter
-
The query filter is a String that contains a
Boolean expression that is evaluated for each instance in the
candidate collection. The query result returns the candidate
instances that have a true result for the query
filter. If the query filter is not specified, the filter results in a
true value for all of the candidate instances. The
query filter in the previous example is specified on line [2].
The collection and class of the candidate instances and the query
filter can be initialized when a Query is
constructed by calling one of several newQuery( )
methods defined in the PersistenceManager
interface (as we did on line [3]). Once a
Query has been constructed, all of the query
components can be set; each has an associated set method.
A query may also include the following components:
- Parameters
-
A parameter
provides a means of passing a value to be used in the query filter
expression. Parameters serve a role similar to formal method
parameters in Java. The query in our example had query parameters
named city and state, declared
on line [4]. The declaration of query
parameters' name and type has the same syntax as
method parameters. You provide a value for the query parameters when
the query is executed.
- Variables
-
A variable is
used in a query filter to reference the elements of a collection. The
use and declaration syntax of query variables is similar to the local
variables in a method. Our example did not access elements of a
collection, so we did not use a query variable. A variable is bound
to the elements of a collection by a
contains(
) expression (covered later in this chapter). Some
implementations allow a variable that is not bound to a collection to
be associated with an Extent. In this case, the
variable is referred to as an unbound
variable, and it may represent any instance in
the extent of the class in the datastore.
- Import statements
-
Parameters and
variables can be of a class different from the candidate class; an
import statement declares their type names. Types supported by JDO
and defined in the java.lang package do not need
to be imported. This includes the String class,
the type of the query parameters in our example, so we did not need
to import any types. Examples of import are provided later in this
chapter.
- Ordering specification
-
You can specify the order of the instances returned in the query
result by providing an ordering specification, which is a list of
expressions with an indicator to specify whether the values should be
in ascending or descending order. We provided an ordering
specification on line [5] in our example.
You need to create and initialize these query components before you
execute a query. Query components can be initialized when a
Query is constructed or via a set method provided
for the query component. The order in which you initialize the query
components before the Query is executed does not
matter.
|