11.5. Limits and Ordering

We have seen how you tell the query which objects you want. This section shows you how to also tell it how many objects you want, and what order you want them in.

public void setUnique (boolean unique);
public void setRange (long start, long end);

Use the setUnique method when you know your query matches at most a single object. A unique query always returns either the one candidate instance that matched the filter, or null; it never returns a Collection. In fact, unique queries enforce the one-result rule by throwing a JDOUserException if more than one candidate survives the filtering process.

While setUnique is designed for queries with only one result, setRange is designed for those with many. Most applications that deal with large amounts of data only process or display a fixed number of records at a time. For example, a web app might show the user 20 results per page, though many thousands of records exist in the database. Using setRange, you can retrieve the exact range of objects you're interested in. The method behaves much like List.subList or String.subString: it uses 0-based indexes, and the end index is exclusive. Unlike these methods, however, setRange won't throw an exception when your range extends beyond the available elements. If 50 records exist and you ask for the range 40, 60, you'll receive a collection containing the 41st (remember indexes are 0-based) through 50th result objects. If you ask for the range 60, 80, you'll get an empty collection.

Whenever you don't specify a range on a query, it defaults to a start index of 0 and an end index of Long.MAX_VALUE, which represents no limit.

public void setOrdering (String ordering);

Ranges are meaningless if the result order isn't constant between query executions, and many datastores don't guarantee consistent natural ordering. setOrdering gives you control over the order of results, so you don't have to rely on the vagaries of the datastore. The method's argument is a comma-separated list of field names or expressions, each followed by the keyword ascending or descending. Those keywords can be abbreviated as asc and desc respectively. Results are ordered primarily by the first (left-most) expression. Wherever two results compare equal with that expression, the next ordering expression is used to order them, and so on.

Example 11.11. Unique

Find the magazine "Spaces" published by Manning. We know that this query can only match 0 or 1 magazines, so we set the unique flag to avoid having to extract the result object from a Collection.

PersistenceManager pm = ...;
Query query = pm.newQuery (Magazine.class, 
    "publisher.name == 'Manning' && title == 'Spaces'");
query.setUnique (true);
processMagazine ((Magazine) query.execute ());

Example 11.12. Result Range and Ordering

Order all magazines by price and return the 21st through 40th results.

PersistenceManager pm = ...;
Query query = pm.newQuery (Magazine.class);
query.setOrdering ("price ascending");
query.setRange (20, 40);
Collection mags = (Collection) query.execute ();
for (Iterator itr = mags.iterator (); itr.hasNext ();)
    processMagazine ((Magazine) itr.next ());
query.close (mags);