11.3. Advanced Object Filtering

In this section, we explore advanced topics in object filtering, supported by the following Query methods:

public void declareParameters (String parameters);
public void declareVariables (String variables);
public void declareImports (String imports);

The following examples will give you a feel for the query elements described above. They use the object model we defined in the previous section.

Example 11.6. Imports and Declared Parameters

Find the magazines with a certain publisher and title, where the publisher and title are supplied as parameters on each execution.

PersistenceManager pm = ...;
Company comp = ...;
String str = ...;

Query query = pm.newQuery (Magazine.class, 
    "publisher == pub && title == ttl");
query.declareImports ("import org.mag.pub.*");
query.declareParameters ("String ttl, Company pub");
List mags = (List) query.execute (str, comp);
for (Iterator itr = mags.iterator (); itr.hasNext ();)
    processMagazine ((Magazine) itr.next ());
query.close (mags);

There are two things to take away from this example. First, the import prevents us from having to qualify the Company class name when declaring our pub parameter (although in this case, it would have been easier to just write out the full class name in the parameter declaration). The unqualified Company name is not automatically recognized by the query because Company isn't in the same package as Magazine, the candidate class.

Second, notice that we supply values for the parameter placeholders when we execute the query. Parameters can be of any type recognized by JDO, though primitive parameters have to be supplied as instances of the appropriate wrapper type on execution. The Query interface includes several methods for executing queries with various numbers of parameters. When you use declared parameters, you should supply the parameter values in the same order that you declare the parameters.

Example 11.7. Implicit Parameters

The query below is exactly the same as our previous example, except this time we use implicit parameters.

PersistenceManager pm = ...;
Company comp = ...;
String str = ...;

Query query = pm.newQuery (Magazine.class, 
    "publisher == :pub && title == :ttl");
List mags = (List) query.execute (comp, str);
for (Iterator itr = mags.iterator (); itr.hasNext ();)
    processMagazine ((Magazine) itr.next ());
query.close (mags);

Here, we use colon-prefixed names to introduce new parameters without declarations. When we execute the query, we supply the parameter values in the order the implicit parameters first appear in the JDOQL. Later in the chapter, we'll see queries that consist of multiple JDOQL strings. Each string might introduce new implicit parameters. When deciding the proper order to supply the parameter values, start with the parameters in the result string, then those in the filter, then the grouping string, and finally the ordering string.

In the query above, we can infer the type of each parameter based on its context. This is almost always the case. There are times, however, when the context alone is not enough to determine the type of an implicit parameter. In these cases, use a cast to supply the parameter type:

PersistenceManager pm = ...;
Company comp = ...;

Query query = pm.newQuery (Magazine.class,
    "publisher.revenue == ((org.mag.pub.Company) :pub).revenue");
List mags = (List) query.execute (comp);
for (Iterator itr = mags.iterator (); itr.hasNext ();)
    processMagazine ((Magazine) itr.next ());
query.close (mags);

Example 11.8. Query By Example

Parameters do not need to be in the datastore to be useful. You can implement query by example in JDO by using an existing "example" object as a query parameter:

PersistenceManager pm = ...;
Magazine example = new Magazine ();
example.setPrice (100);
example.setTitle ("Fourier Transforms");
Query query = pm.newQuery (Magazine.class,
    "price == ex.price && title == ex.title");
query.declareParameters ("Magazine ex");
List mags = (List) query.execute (example);
for (Iterator itr = mags.iterator (); itr.hasNext ();)
    processMagazine ((Magazine) itr.next ());

Example 11.9. Variables

Find all magazines that have an article titled "Fourier Transforms".

PersistenceManager pm = ...;
Query query = pm.newQuery (Magazine.class, "articles.contains (art) " 
    + "&& art.title == 'Fourier Transforms'";
query.declareVariables ("Article art");
List mags = (List) query.execute ();
for (Iterator itr = mags.iterator (); itr.hasNext ();)
    processMagazine ((Magazine) itr.next ());
query.close (mags);

A variable represents any persistent instance of its declared type. So you can read the filter string above as: "The magazine's articles collection contains some article art, where art's title is 'Fourier Transforms'". Notice how we bind art to a particular collection with the contains method, then test its properties in an &&'d expression. This is a common pattern in JDOQL filters, and applies equally well to placing conditions on the keys and values of maps.

Of course, we don't have to declare art explicitly. The same query without declareVariables would work just as well:

PersistenceManager pm = ...;
Query query = pm.newQuery (Magazine.class, "articles.contains (art) " 
    + "&& art.title == 'Fourier Transforms'";
List mags = (List) query.execute ();
for (Iterator itr = mags.iterator (); itr.hasNext ();)
    processMagazine ((Magazine) itr.next ());
query.close (mags);

In this case the JDO implementation assumes art is an implicit variable because it does not match the name of any field in the candidate class. The new variable's type is set to the known element type of the collection that contains it.

Example 11.10. Unconstrained Variables

The example above uses a variable to represent any element in a collection. We refer to variables used to test collection or map elements as constrained or bound variables, because the values of the variable are limited by the collection or map involved. Many JDO implementations also support unconstrained variables. Rather than representing a collection or map element, an unconstrained variable represents any persistent instance of its class. Consider the following example:

Query query = pm.newQuery (Article.class, "mag.copiesSold > 10000 "
    + "&& mag.coverArticle == this");
query.declareVariables ("Magazine mag");
List arts = (List) query.execute ();
for (Iterator itr = arts.iterator (); itr.hasNext ();)
    processArticle ((Article) itr.next ());
query.close (arts);

What does this query do? Let's break it down. The first clause matches any magazine that has sold more than 10,000 copies. The second clause requires that the cover article of the magazine is the candidate instance being evaluated (notice the query's candidate class is Article in this example). So the query returns all articles that are the cover article for a magazine that has sold more than 10,000 copies. The unconstrained variable mag allowed us to overcome the fact that there was no direct relation from Article to Magazine (only the reverse).

Later in this chapter, we'll see how to use a projection to drastically simplify this query.

We can also execute this query without declaring the mag variable explicitly. Without a constraining contains clause, however, the type of an unconstrained, implicit variable is impossible to infer. Use a cast to supply the type:

Query query = pm.newQuery (Article.class, "((Magazine) mag).copiesSold > 10000 "
    + "&& mag.coverArticle == this");
List arts = (List) query.execute ();
for (Iterator itr = arts.iterator (); itr.hasNext ();)
    processArticle ((Article) itr.next ());
query.close (arts);

Up until now, we have focused on how to configure our queries with the right filter, but we have ignored how to actually execute the query. The next section corrects this oversight.

 

Skip navigation bar   Back to Top