Core Java Data Objects

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:

  • Query language neutrality: The underlying query language might be a relational query language such as SQL, an object database query language such as OQL, or a specialized API to a hierarchical database or mainframe EIS system.

  • Optimization to specific query language: The Query interface is capable of optimizations; therefore, the interface has enough user -specified information to allow for the JDO implementation to exploit data source specific query features.

  • Accommodation of multi- tier architectures: Queries may be executed entirely in memory, or may be delegated to a back end query engine. The JDO Query interface provides for both types of query execution strategies.

  • Large result set support: Queries might return massive numbers of JDO instances that match the query. The JDO Query architecture provides for processing the results within the resource constraints of the execution environment.

  • Compiled query support: Parsing queries may be resource- intensive , and in many applications can be done during application development or deployment, prior to execution time. The query interface allows for compiling queries and binding run-time parameters to the bound queries for execution.

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.

Portability

Using a query language other than JDOQL may make the application non-portable across vendor implementations .

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.

Query language string

A query can be constructed for other vendor-supported languages using the newQuery (String language, Object query) method in the PersistenceManager . The value of the string to specify JDOQL is javax.jdo.query.JDOQL . For other languages, it is a vendor-specific string.

"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 filters

The 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:

  • Specify the criteria for the query.

  • Determine whether an instance in the candidate collection is a member of the result set that is specified by the query.

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);

Transient instance support

Support for transient instances in the collection is optional, and vendors may or may not implement it.

Table 6-1. Logical Operators

Operator

Description

Supported data types

Example

SQL Equivalent

!

Logical compliment

Boolean, boolean

String filter = "!(employed==true)"

NOT

&&

Conditional AND

Boolean, boolean

String filter = "(name==\"John\") && (age==30)"

AND

Conditional OR

Boolean, boolean

String filter = "(name==\"John\") (name==\"Johnathan\")"

OR

The "&" and "" operators

JDOQL filters cannot change any fields. They are non-mutating. Therefore, the Java operators "&" and "" are should not be used.

Table 6-2. Comparison Operators

Operator

Description

Supported data types

Example

SQL Equivalent

==

Equals

byte, short, int, long, char, Byte, Short, Integer, Long, Character float, double, Float, Double, BigDecimal, BigInteger, Boolean, boolean, Date, any class instance or array

Name equals John:= String filter = "(name==\"John\")"

==

!=

Not equal

byte, short, int, long, char, Byte, Short, Integer, Long, Character float, double, Float, Double, BigDecimal, BigInteger Boolean, boolean, Date, any class instance or array

Name does not equal John:=

String filter =" (name!=\"John\")"

<>

>

Greater than

byte, short, int, long, char, Byte, Short, Integer, Long, Character float, double, Float, Double, BigDecimal, BigInteger, Date, String

Age is greater than 30:= String filter = "age > 30"

>

<

Less than

byte, short, int, long, char, Byte, Short, Integer, Long, Character float, double, Float, Double, BigDecimal, BigInteger, Date, String

Age is less than 30:=

String filter = "age<30"

<

>=

Greater than or equal

byte, short, int, long, char, Byte, Short, Integer, Long, Character float, double, Float, Double, BigDecimal, BigInteger, Date, String

Age is greater than or equal to 30:= String filter = "age >= 30"

>=

<=

Less than or equal

byte, short, int, long, char, Byte, Short, Integer, Long, Character float, double, Float, Double, BigDecimal, BigInteger, Date, String

Age is less than or equal to 30:= String filter = "age<=30"

<=

+

Binary addition

byte, short, int, long, char, Byte, Short, Integer, Long, Character float, double, Float, Double, BigDecimal, BigInteger

The persons age is 2 yrs below the minimum age String filter ="age+2>=minage"

+

-

Binary subtraction

byte, short, int, long, char, Byte, Short, Integer, Long, Character float, double, Float, Double, BigDecimal, BigInteger

The persons age is 2 years more than the minimum age String filter ="age-2>minage"

-

~

Negation (operator is followed by a primitive)

byte, short, int, long, char, Byte, Short, Integer, Long, Character

Assume field x is negative, change to positive value

String filter="age==(~ x)"

(-expr) - 1

*

Multiplication

byte, short, int, long, char, Byte, Short, Integer, Long, Character float, double, Float, Double, BigDecimal, BigInteger

Profile is 2% of price String filter=".02 * price"

*

/

Division

byte, short, int, long, char, Byte, Short, Integer, Long, Character float, double, Float, Double, BigDecimal, BigInteger

Profile is 2% of price String filter="(2/100) * price"

/

Table 6-3. Special Operators

Operator

Description

Example

()

Used to cast object types.

Author names start with 'S": String filter = "((String)name).startsWith(\"S\") "; Note that the cast in the above filter is not explicitly required but is only meant to show usage.

. (period)

Used to reference fields as in the Java language

Author names start with 'S" : String filter="name.startsWith(\"S\")" Author living in zip code 02929: String filter = "address. zipcode ==\"02929\"";

this

Used to access the hidden fields. this refers to an object of the candidate class.

String filer = "this.name == name"; More details on this in Section 6.5.5

6.4.1.1 Differences between Java and JDOQL operators

Although 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 ordering

In 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 operators

The 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 Methods

For 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:

  • isEmpty() : This returns true if the collection contains no elements or if the reference is null.

  • contains(Object obj) : This returns true if the collection contains the object passed as the parameter. Note that the reference passed must be a persistence-capable object. The JDO implementation uses the JDO identity, as opposed to the equals() method, to check whether the object exists in the collection.

Two methods can be invoked on a String instance in a filter:

  • startsWith(String str) : If the persistence field (a String instance) starts with the specified parameter, then the method returns true. For example, the name is a persietent field in the Author class. A valid filter could be String filter="name.startsWith(\"S\")" .

  • endsWith(String str) : This is similar to the startsWith() method, except that it checks whether the field ends with the specified parameter.

6.4.1.1.4 Navigation

The 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.

Категории