11.3. Advanced Object Filtering
Prev | Chapter 11. Query | Next

## 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 `declareParameters` method defines the names and types of the query's parameters. Declaring query parameters is analogous to declaring the parameter signature of a Java method, and uses the same syntax. Parameters acts as placeholders in the filter string, allowing you to supply new values on each execution. Parameters also allow for query by example, as demonstrated in Example 11.8, “Query By Example”.

  Parameters do not have to be declared. As you will see in the examples below, you can introduce implicit parameters into your queries simply by prefixing the parameter name with a colon in your JDOQL string. Wherever the type of an implicit parameter can't be inferred from the context, you can set the type by casting the parameter in the JDOQL.

  You cannot mix implicit and declared parameters in the same query. Each query must declare all of its parameters, or none of them.

- `declareVariables` names the local variables used in the query filter. It uses standard Java variable declaration syntax. Query variables are typically used to place conditions on elements of collections or maps.

  Like parameters, variables do not have to be declared. Whenever you use an unrecognized identifier where a variable would be appropriate, the JDO implementation will dynamically define an implicit variable with that name. In the case of an unconstrained variable, you must cast the variable in your JDOQL to supply its type. Unlike parameters, you can mix both declared and implicit variables in the same query.

- You can import classes into the query's namespace with the `declareImports` method. The method argument uses standard Java `import` syntax. By default, queries only recognize unqualified class names when the class is in the package of the candidate class, or when it is in `java.lang`. Using imports, you can save yourself the trouble of typing out full class names in your parameter and variable declarations, and in your your filter string (class names can appear in filter strings when you use the `instanceof` operator, access a static field, or perform a cast).

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.

Prev | Up | Next
11.2. JDOQL | Home | 11.4. Compiling and Executing Queries