The filter is essentially a string passed to the query facility that is used by the query facility to refine the results returned. The syntax for the filter is specified using JDOQL or the JDO query language. This begs the question: Why use a new syntax? Why not use Structured Query Language (SQL) that is standard to all relational databases? One of the goals of JDO is to isolate the developer from the specifics of the underlying datastore. This means that the query language must be vendor- and implementation- agnostic . The vendor is free to choose SQL, Object Query Language (OQL), or whatever other query language the underlying data-store supports. To quote the specifications, the primary design goals for the query language were:
The other goal was to keep the query facility simple and developer-friendly. JDOQL achieves this by using the standard Java operators and syntax with which developers are already familiar. This feature has been acclaimed and criticized equally. On the one hand, JDOQL is easy to construct and program, but on the other hand, most developers are already familiar with SQL, and database operators are capable of producing complex, highly optimized SQL queries. For many architects , embedding queries in a middle-tier transparent persistence API such as JDO is an unwelcome step. However, this has more to do with the overall use of JDO in an enterprise than the query facility. As per its design goal, JDO needs to isolate the application from the underlying datastore. Using SQL would mean tying the implementation to some sort of relational implementation. However, it should be noted that JDOQL queries can return only JDO instances. There are no projections or simple type/value queries, or even COUNT queries, as in SQL.
JDOQL is one of many languages that an implementation can support and all implementations are required to support the specification-defined behavior of JDOQL. This keeps the application code portable. In reality, vendor implementations that use relational databases as the underlying store allow the use of SQL directly, as we see in Section 6.6.4.
"How is JDOQL specified?" you may ask. The syntax for specifying syntax, i.e., the meta-syntax is done using a standard notion like Backus Normal Form (BNF). BNF is a widely used and widely accepted notation for specifying the syntax of programming languages such as Java and C++ and query languages such as SQL, JDOQL, and so on. Fortunately, the only people who need to know BNF are the specification authors! Developers need only know how JDQL works. Interested readers can refer to Appendix C for the BNF notation of JDOQL. 6.4.1 Specifying filtersThe filter string defined in JDQL must evaluate to a Boolean condition. The string itself can be composed of multiple expressions that are separated using the standard Java syntax for AND, OR and NOT operators. The logical operators are explained further in Table 6-1. Each expression can use the standard Java operators used for comparison. These are explained further in Table 6-2. The Boolean expression specified through the filter can be used to:
For example, the following code segment from SimpleQueryWithFilter.java shows how a query can be constructed to find all Author objects where the author's name is "Snoop Smith": tx.begin(); // find all author objects where the name is Snoop Smith String filter = "name==\"Snoop Smith\" "; Query query = pm.newQuery(Author.class, filter); Collection results = (Collection) query.execute(); if (results.isEmpty()) System.out.println("No results found"); else { Iterator iter = results.iterator(); while (iter.hasNext()) { Author author = (Author) iter.next(); String name = author.getName(); System.out.println("Author's name is " + name); System.out.println("Author's address is " + author.getAddress()); System.out.println("Author's books are " ); Iterator books=author.getBooks().iterator(); while(books.hasNext()) System.out.println("Book:" +books.next()) ; } } query.closeAll(); tx.commit(); The filter and candidate collection can be specified when the query is constructed through one of the newQuery() methods or dynamically using the setFilter() and setCandidates() methods respectively. The setCandidates() method binds the candidate collection to the query instance. If the collection is modified after this invocation, it is up to the vendor implementation to allow the modified elements to participate in the query or throw a NoSuchElementException during execution of the Query for those elements. The elements in the candidate Collection must be persistent instances associated with the same PersistenceManager as the Query instance. Vendors can optionally choose to support transient instances in the collection, but relying on that feature would make the code non-portable to other vendor implementations. If there are multiple PersistenceManagers and any element in the collection is associated with a persistence manager other than the one under which the query is constructed, a JDOUserException is thrown when the query is executed. The code segment below from SimpleQueryWithFilterAndCandidates.java reworks the previous example to use a candidate collection and filter: tx.begin(); Author a1= new Author("Author Smith"); Author a2= new Author("Author Jones"); Author a3= new Author("Author Knowles"); Collection candidates = new ArrayList(); candidates.add(a1); candidates.add(a2); candidates.add(a3); pm.makePersistentAll(candidates); // find all author objects where the name is Author Smith String filter = "name==\"Author Smith\" "; Query query = pm.newQuery(Author.class, filter); query.setCandidates(candidates); Collection results = (Collection) query.execute(); The preceding example makes the candidate collection persistent. This is important for the code above to work because the candidate collection must represent objects in the datastore. If the vendor doesn't support transient instances in the candidate collection, then the code segment below throws a JDOUserException: Author a1= new Author("Author Smith"); Author a2= new Author("Author Jones"); Author a3= new Author("Author Knowles"); Collection candidates = new ArrayList(); candidates.add(a1); candidates.add(a2); candidates.add(a3); // find all author objects where the name is Author Smith String filter = "name==\"Author Smith\" "; Query query = pm.newQuery(Author.class, filter); query.setCandidates(candidates);
Table 6-1. Logical Operators
Table 6-2. Comparison Operators
Table 6-3. Special Operators
6.4.1.1 Differences between Java and JDOQL operatorsAlthough the JDOQL operators follow the syntax of the Java operators, there are some subtle and explicit differences between the operators as used in the query filters and as used in the Java language. 6.4.1.1.1 Equality and orderingIn Java, the equality operator " == " cannot be used between a primitive and its corresponding wrapper. JDOQL allows this operator to be used between primitives and their corresponding wrapper classes. For example, ( 10==objectten ) is a valid use, whereas objectten is an instance of Integer. The same concept extends to the ordering operators ( > , < , >= , <= ). In Java, you cannot use these operators between a primitive and a wrapper. For example, the syntax ( 10>objectten ) would be illegal in Java if the variable objectten referred to an instance of Integer and not a primitive. Like the equality operator, JDOQL allows for this type of usage between primitives and wrappers. The use of the equality and ordering operators extends to the String and Date objects. For example, if d1 and d2 represent two Date instances, ( d1>d2 ) would be illegal in Java, but valid as a part of the filter in JDOQL. 6.4.1.1.2 Assignment operatorsThe query filter is not allowed to change the value of a persistent field. This means that the assignment operators = , /= , += , and so on, and pre/post increment/decrement are not allowed. For example, the following string, although legal in Java, is illegal as a filter in JDO: String filter="author.name=\"Snoop Smith\"" ; 6.4.1.1.3 MethodsFor performance reasons, a JDO implementation is not required to instantiate an object in order to evaluate the query filter. Additionally, the query may execute on a server where the application is not available. This means that the method invocation as a part of the filter is not supported and is considered illegal. For example, the following filter is illegal: String filter="author.getName()=="Snoop Smith" ; the only exceptions when it comes to methods are the Collection interface and the String class. Two methods that can be invoked on a Collection instance in a filter:
Two methods can be invoked on a String instance in a filter:
6.4.1.1.4 NavigationThe term navigation in JDOQL is used to describe the ability to explicitly traverse and resolve references to fields in a persistence-capable class. As shown in Table 6-3, this is done using the period ( "." ) operator. JDOQL requires that navigation through a field with a null value must result in the subexpression being evaluated to false . In the above example, if the zipcode field in the candidate class were null, it would result in the filter evaluating to false. For example, the filter to determine whether an author resides in the zip 02929 would look like this: String filter = "address.zipcode==\"02929\"". In this case, the filter navigates to the field zipcode via the address reference. JDOQL also supports navigation through a Collection types using variables and the contains() method. This is covered further in Section 6.5.3. |