Core Java Data Objects
Let us now look at the slightly more advanced features of the query facility. These features allow the use of parameters in a query string, the use of declarative imports and variables , respectively. 6.5.1 Declaring parameters
Rather than hard coding the values for certain objects in the filter string, the filter can contain placeholders that can be replaced with different values. This allows a single query to be executed multiple times with new values for the placeholder. For repeated queries, vendors can optimize their underlying implementation specific to the datastore for optimizing performance. Conceptually, the usage of parameters in JDOQL is similar to the way parameters are passed in JDBC prepared statements. For example, in JDBC, the hard coded SQL would look like this:
[View full width]
[View full width] Statement stmt= con.createStatement("UPDATE AUTHORS SET BOOK= 'Core JDO" WHEREThis can be replaced with a PreparedStatment as follows :
PreparedStatement stmt = con.prepareStatement( "UPDATE AUTHORS SET BOOK =? WHERE NAME ==?"); The prepared statement can be executed repeatedly with new values replacing the "?" through the stmt.setXXX() methods . The database precompiles the SQL and executes faster. JDOQL works in a similar manner. Rather than containing "?" in the filter string, actual variable names may be used. The values for the variables are passed using the declareParameters() method in the Query interface, which takes a String as an argument. The syntax of the string is the same as that used in standard Java method signatures; i.e., it is a type declaration followed by the variable name. Let's look at an example of how this works. The code segment below from FilterExamples.java shows how to find all books with a given publisher name and a publication date earlier than the current date. The same query is then re-executed with new values for the publisher's name.
// find all books with a certain publisher name and date // First create the filter String filter = "publisher.name== pubname && PublicationDate < today"; Query query = pm.newQuery(Book.class, filter); Date today= new Date(); String pubname="Pet publishin Co"; // Declare any import statements (see next paragraph for // explaination) query.declareImports("import java.util.Date;") ; // Declare the parameters using the standard Java method // signature syntax query.declareParameters("Publisher pubname,Date today"); // execute the query with the given parameters results = (Collection) query.execute(pubname,today); if (results.isEmpty()) System.out.println("No results found"); else { Iterator iter = results.iterator(); while (iter.hasNext()) { Book book = (Book) iter.next(); System.out.println("Book found "+book); } } // repeat query execution with new value for publisher pubname="Books123.com"; results = (Collection) query.execute(pubname,today); if (results.isEmpty()) System.out.println("No results found"); else { Iterator iter = results.iterator(); while (iter.hasNext()) { Book book = (Book) iter.next(); System.out.println("Book found "+book); } } The Query interface has four convenient methods for execution that can use parameters. They take one, two or three objects as parameters. Usually, one of these overloaded methods will suffice. However, if the query takes a number of parameters, then the more generic form, which takes an array of objects, can be used. These methods are described in Table 6-4. Table 6-4. Query Execution Methods
6.5.2 Declaring imports
Sometimes, the class or interface definitions of parameters passed to the query may reside in a package other than the candidate class. These definitions can be imported on demand by the JDO implementation using the declareImports() method. The method takes a semicolon-separated list of import statements in the standard Java syntax. In the preceding example, the publicationDate was used as a parameter, but the Date class resides in the java.util package. The declareImports() method was used to pass the import declaration to tell the JDO runtime where to locate the parameter definition. The standard java.lang.* classes, the candidate class, and classes in the same package as the candidate class do not need to be explicitly imported. All imported classes must, however, be available in the class path . 6.5.3 Declaring variables
JDOQL uses the concept of variables to test whether some items in a multi-valued collection match specific criteria. If the filter involves iteration through a Collection , a variable can be used along with the Collection.contains() method to check whether a specific element in the collection matches the filter criteria. For example, the Author class contains a field named books that is of type java.util.Set . To check whether the Set (a subinterface of java.util.Collection ) contains any Book object with the title "Learn Java Today", the code would look like this:
String filter = "books .contains(myfavouritebook ) && " + "(myfavoritebook.title==\"Learn Java Today\")"; Query query = pm.newQuery(Author.class, filter); query.declareVariables("Book myfavoritebook"); Collection results = (Collection) query.execute(); The contains() method is passed a variable named myfavoritebook . This variable is then declared using the declareVariables() method, following the standard Java syntax for variable declarations (i.e., variableType variableName ). In the preceding example, the Collection is iterated and each element is set to the variable myfavouritebook. The condition ( myfavouritebook.title== \"Learn Java Today\" ) is then evaluated for each value and the result formed . Of course, this serial access is only conceptual, and the JDO implementation may use a strategy specific to the indexing features of the underlying data-store. For example, in the preceeding code segment, a vendor implementation may generate SQL for a relational datastore that looks like this:
[View full width]
[View full width] Select Distnct T0.Jdoid T0.Address, T0.Name From Author T0, Author_Books T1, Book T2 Where
6.5.4 Ordering results
Sometimes, it is desirable to have the results of a query ordered in a particular manner. For example, Author objects are logically ordered by name in ascending alphabetical order. The ordering criteria for a query can be specified as a comma-separated list of field names followed by the keywords " ascending " or " descending ". The results returned are ordered using the left-to-right evaluation of the ordering criteria. The declarations are passed to the Query interface using the setOrdering() method. For example, the code segment below from FilterExamples.java shows how a query can be executed to return all Books sorted in ascending order by title . If two books have the same title , then they are sorted by the next criteria, descending order of the publicationDate :
query = pm.newQuery(Book.class); query.setOrdering("title ascending, publicationDate decending"); results = (Collection) query.execute(); 6.5.5 Namespaces in a query
A namespace refers to a logical separation of type names, fields, and variables. A Query has two namespaces:
The setClass() method adds the name of the candidate class, and the declareImports() method adds the names of imported classes or interfaces to the type namespace. When the setClass() method is used to set the candidate class explicitly, the names of fields of the candidate class are also added to the namespace of the fields and variables for the Query . The same happens when the declare-Parameters() or declareVariables() methods are invoked. In other words, the names of the parameters and variables are added to the same namespace. A parameter name or variable name overrules and hides the field name set by the candidate class, if they are the same. The hidden field may be accessed using the operator shown in Table 6-3. The keyword in a filter always refers to an object of the candidate class. For example, consider the code snippet from FilterExamples.java . The filter looks for an Author with a name field that equals "Steven Jones" and declares a variable of type Book also called name:
String filter = "books.contains(name) && " + "(this.name==\"Steven Jones\"; Query query = pm.newQuery(Author.class, filter); query.declareVariables("Book name"); Collection results = (Collection) query.execute(); In this case, the persistent field name of the candidate class (Author) will be hidden by the declared variable name and the this keyword must be used to refer to it explicitly.
|