JDO : JDOQL Declarative API

As shown earlier, there are two primary ways of defining a JDOQL query.
In this guide we describe the declarative approach, defining the individual components of the query
via an API. You can also refer to the API javadoc
,
We firstly need to look at a typical declarative JDOQL Query.

In this Query, we create it to return objects of type mydomain.MyClass (or subclasses),
and set the filter to restrict to instances of that type which have the field field2
less than some threshold value, which we don't know at that point.
We've specified the query like this because we want to pass the threshold value in dynamically.
We then import the type of our threshold parameter, and the parameter itself, and set the
ordering of the results from the Query to be in ascending order of some field field1.
The Query is then executed, passing in the threshold value. The example is to highlight the typical
methods specified for a Query. Clearly you may only specify the Query line if you wanted something
very simple. The result of the Query is cast to a List since in this case it returns a List of
results.

setClass()

Set the class of the candidate instances of the query. The class specifies the class of the
candidates of the query. Elements of the candidate collection that are of the specified class
are filtered before being put into the results.

setUnique()

Specify that only the first result of the query should be returned, rather than a collection.
The execute method will return null if the query result size is 0.

Sometimes you know that the query can only every return 0 or 1 objects. In this case you can
simplify your job by adding

query.setUnique(true);

In this case the return from the execution of the Query will be a single Object, so you've no
need to use iterators, just cast it to your candidate class type.
Note that if you specify unique and there are more results than just 1 then it will throw a JDOUserException.

setResult()

Specifies what type of data this query should return. If this is unset or set to null, this
query returns instances of the query's candidate class. If set, this query will return
expressions, including field values (projections) and aggregate function results.

The normal behaviour of JDOQL queries is to return a List of Objects of the type of the candidate
class. Sometimes you want to have the query perform some processing and return things like count(),
min(), max() etc. You specify this with

query.setResult("count(param1), max(param2), param3");

In this case the results will be List<Object[]> since there are more than 1 column in each
row. If you have only 1 column in the results then the results would be List<Object>.
If you have only aggregates (sum, avg, min, max, count) in the result clause then there will be
only 1 row in the results and so the results will be of the form Object[] (or Object if only 1
aggregate).
Please refer to JDOQL Result Clauses for more details.

setResultClass()

Specify the type of object in which to return each element of the result of invoking execute().
If the result is not set or set to null, the result class defaults to the candidate class of
the query. If the result consists of one expression, the result class defaults to the type of
that expression. If the result consists of more than one expression, the result class defaults
to Object[].

When you perform a query, using JDOQL or SQL the query will, in general, return a List of objects.
These objects are by default of the same type as the candidate class. This is good for the majority
of situations but there are some situations where you would like to control the output object.
This can be achieved by specifying the Result Class.

Can be a user defined class, that has either a constructor taking arguments of the same type
as those returned by the query (in the same order), or has a public put(Object, Object) method,
or public setXXX() methods, or public fields.

Where you have a query returning a single field, you could specify the Result Class to be
one of the first group for example. Where your query returns multiple fields then you can set the
Result Class to be your own class. So we could have a query like this

In this case our query is returning 2 fields (a Double and a String), and these map onto the constructor
arguments, so DataNucleus will create objects of the Price class using that constructor. We could
have provided a class with public fields instead, or provided setXXX methods or a
put method. They all work in the same way.

setRange()

Set the range of results to return. The execution of the query is modified to return only a
subset of results. If the filter would normally return 100 instances, and fromIncl is set
to 50, and toExcl is set to 70, then the first 50 results that would have been returned are
skipped, the next 20 results are returned and the remaining 30 results are ignored.
An implementation should execute the query such that the range algorithm is done at the data
store.

Sometimes you have a Query that returns a large number of objects. You may want to just display
a range of these to your user. In this case you can do

query.setRange(10, 20);

This has the effect of only returning items 10 through to 19 (inclusive) of the query's results.
The clear use of this is where you have a web system and you're displaying paginated data, and
so the user hits page down, so you get the next "n" results.

setRange is implemented efficiently for MySQL, Postgresql, HSQL (using the LIMIT SQL
keyword) and Oracle (using the ROWNUM keyword), with the query only finding the objects required
by the user directly in the datastore. For other RDBMS the query will retrieve all objects up
to the "to" record, and will not pass any unnecessary objects that are before the "from" record.

setFilter()

Set the filter for the query. The filter specification is a String containing a Boolean
expression that is to be evaluated for each of the instances in the candidate collection.
If the filter is not specified, then it defaults to "true", which has the effect of filtering
the input Collection only for class type.

declareImports()

Set the import statements to be used to identify the fully qualified name of variables or
parameters. Parameters and unbound variables might come from a different class from the
candidate class, and the names need to be declared in an import statement to eliminate
ambiguity. Import statements are specified as a String with semicolon-separated statements.

In JDOQL you can declare parameters and variables. Just like in Java it is often convenient to just
declare a variable as say Date, and then have an import in your Java file importing the
java.util.Date class. The same applies in JDOQL. Where you have defined parameters or variables
in shorthand form, you can specify their imports like this

Just like in Java, if you declare your parameters or variables in fully-specified form (for
example "java.util.Date myDate") then you do not need any import.

The JDOQL uses the imports declaration to create a type namespace for the query.
During query compilation, the classes used in the query, if not fully qualified, are searched
in this namespace. The type namespace is built with the following:

primitives types

java.lang.* package

package of the candidate class

import declarations (if any)

To resolve a class, the JDOQL compiler will use the class fully qualified name to load it, but
if the class is not fully qualified, it will search by prefixing the class name with the
imported package names declared in the type namespace.
All classes loaded by the query must be acessible by either the candidate class classloader,
the PersistenceManager classloader or the current Thread classloader. The search algorithm for
a class in the JDOQL compiler is the following:

if the class is fully qualified, load the class.

if the class is not fully qualified, iterate each package in the type namespace and try to
load the class from that package. This is done until the class is loaded, or the type
namespace package names are exausted. If the class cannot be loaded an exception is thrown.

Note that the search algorithm can be problematic in performance terms if the class is not
fully qualified or declared in imports using package notation. To avoid such problems, either
use fully qualified class names or import the class in the imports declaration. The 2 queries
below are examples of good usage:

declareParameters()

Declare the list of parameters query execution. The parameter declaration is a String containing
one or more query parameter declarations separated with commas. Each parameter named in the
parameter declaration must be bound to a value when the query is executed.

When using explicit parameters you need to declare them and their types. With the declarative API
you do it like this

declareVariables()

Declare the unbound variables to be used in the query. Variables might be used in the filter,
and these variables must be declared with their type. The unbound variable declaration is a
String containing one or more unbound variable declarations separated with semicolons.

With explicit variables, you declare your variables and their types. In declarative JDOQL it
is like this

query.declareVariables("mydomain.Product prod");

Multiple variables can be declared using semi-colon (;) to separate variable declarations.

query.declareVariables("String var1; String var2");

setOrdering()

Set the ordering specification for the result Collection. The ordering specification is a
String containing one or more ordering declarations separated by commas. Each ordering
declaration is the name of the field on which to order the results followed by one of the
following words: "ascending" or "descending". The field must be declared in the candidate
class or must be a navigation expression starting with a field in the candidate class.

With JDOQL you can specify the ordering using the normal JDOQL syntax for a parameter, and then
add ascending or descending (UPPER or lower case are both valid) are to give the
direction. In addition the abbreviated forms of asc and desc (again, UPPER and
lower case forms are accepted) to save typing. For example, you may set the ordering as follows

query.setOrdering("productId DESC");

setGrouping()

Set the grouping expressions, optionally including a "having" clause. When grouping is
specified, each result expression must either be an expression contained in the grouping,
or an aggregate evaluated once per group.