Chapter 11. Query

11.1. Object Filtering
11.2. JDOQL
11.3. Advanced Object Filtering
11.4. Compiling and Executing Queries
11.5. Limits and Ordering
11.6. Projections
11.7. Aggregates
11.8. Result Class
11.8.1. JavaBean Result Class
11.8.2. Generic Result Class
11.9. Single-String JDOQL
11.10. Named Queries
11.10.1. Named Query DTD
11.10.2. Named Query Examples
11.11. Conclusion

The javax.jdo.Query interface serves two functions: object filtering and data retrieval. This chapter examines each in turn.

[Note]Note

Much of the functionality we discuss in this chapter is new to JDO 2. Though Kodo supports all of the features defined in the following sections, many JDO implementations may not. Additionally, because official JDO 2 jars are not yet available, you will have to cast your query objects to kodo.query.KodoQuery to access any JDO 2 APIs. The UML diagram above depicts these APIs in bold. For simplicity, casts have been left out of the example code throughout the chapter.

We describe all JDO 2 features as they appear in the JDO 2 Early Draft specification and subsequent JCP Expert Group proposals. Some of these features may change before the JDO 2 specification is finalized.

11.1. Object Filtering

JDO queries evaluate a group of candidate objects against a set of conditions, eliminating the objects that don't match. We refer to this as object filtering. The original group of objects might be a Collection of instances you've already retrieved, or an Extent. Recall from Chapter 10, Extent that an Extent represents all persistent instances of a certain class, optionally including subclasses.

Conditions are specified in the JDO Query Language (JDOQL). The filtering process might take place in the datastore, or might be executed in memory. JDO does not mandate any one mechanism, and many implementations use a mixture of datastore and in-memory execution depending on the circumstances.

Basic object filtering utilizes the following Query methods:

public void setClass (Class candidateClass);
public void setCandidates (Extent candidates);
public void setCandidates (Collection candidates);
public void setFilter (String filter);
  • setClass names the candidate class. Candidate objects that are not assignable to the candidate class cannot match the filter.

  • The two setCandidates methods specify the set of objects to evaluate. If you supply an Extent, you don't need to also call setClass; the query inherits the Extent's candidate class. Reciprocally, if you call setClass but do not call either setCandidates method, the query candidates default to the Extent of the candidate class, including subclasses.

  • setFilter accepts a JDOQL string establishing the conditions an object must meet to be included in the query results. As you'll see shortly, JDOQL looks exactly like a Java boolean expression using the candidate class' persistent fields and relations. When you don't set a filter explicitly, it defaults to the simplest possible boolean expression: true. In this case, all candidate objects assignable to the candidate class match the query.

Let's see an example of basic object filtering in action. Our example draws on the following object model, which we continue to use throughout the chapter.

Example 11.1. Filtering

The following code processes all Magazine objects in the database whose price is greater than 10 dollars.

PersistenceManager pm = ...;
Query query = pm.newQuery ();
query.setClass (Magazine.class);
query.setCandidates (pm.getExtent (Magazine.class, true));
query.setFilter ("this.price > 10");
Collection mags = (Collection) query.execute ();
for (Iterator itr = mags.iterator (); itr.hasNext ();)
    processMagazine ((Magazine) itr.next ());
query.close (mags);

The code above is technically correct, but could be much more concise. First, remember that setting a candidate class defaults the candidates to the extent of that class, and vice versa. So we don't need both the setClass and setCandidates calls. We can also get rid of the this qualifier on price in our filter string, since unqualified field names are already assumed to belong to the current object, just as in Java code. Finally, we can take advantage of the fact that PersistenceManager overloads the newQuery method to tighten our code further. Here is the revised version:

PersistenceManager pm = ...;
Query query = pm.newQuery (Magazine.class, "price > 10");
Collection mags = (Collection) query.execute ();
for (Iterator itr = mags.iterator (); itr.hasNext ();)
    processMagazine ((Magazine) itr.next ());
query.close (mags);

The filter above is rather basic. Before moving on to more advanced queries, though, we need to dive into the details of JDOQL.