Query Variables


Variables are used to refer to the range of objects within an extent. When the query filter uses a variable, it evaluates to true only when there is at least one object in the extent of the variable such that all the subexpressions that use the variable evaluate to true. For example, the following query filter uses the variable someBar:

 "allMyBars.contains(someBar) && someBar.size == 3" 

The filter selects a Foo object only when there is at least one Bar object that meets two criteria: the Bar object is a member of the foo's allMyBars collection, and its size is 3. Variables are typically used to navigate collection fields within the query.

In the preceding example, the only Bar objects that need to be considered are the objects that are members of the allMyBars collection field. When the range is limited to the members of a collection field, the variable is a constrained variable. Otherwise, the variable is an unconstrained variable that ranges over the entire extent of the variable's application data class. The distinction is important because JDO implementations are required to support constrained variables, but they are not required to support unconstrained variables.

The application declares variables in the string passed to the declareVariables method. The variable someBar is declared in the following string:

 "Bar someBar;" 

The syntax of the variable string is the same as the syntax for local variable declarations in Java with two exceptions: only one variable can be declared for each type declaration, and the trailing semicolon is optional. For example, if the query filter required a Foo variable and a Bar variable, either of the following two variable declaration strings work equally well:

 "Foo foo; Bar bar;" "Foo foo; Bar bar" 

Notice the missing semicolon in the second string.

If two Foo variables are required, then the following declaration string works:

 "Foo x; Foo y;" 

On the other hand, the following string would not be accepted, because each variable needs to be matched with its type:

 "Foo x, y;" 

All variables for a query must be declared in one variable string. The variable strings do not accumulate.

In most cases, the type of a variable will be an application data class. In any event, a variable type must be a persistence-capable type.

The names of variables must be distinct, and they cannot duplicate parameter names. If a variable name is the same as a field name in the candidate class, it hides the field name, which can still be accessed using the this.fieldname syntax.

Multiple variables may be used in a query. During filter evaluation, there is no requirement that different objects be used for different variables of the same type. For example, to find all Foo objects that have at least two children, use the following variable declaration:

 "Foo childA; Foo childB;" 

and this query filter:

 "myChildren.contains(childA) && (myChildren.contains(childB) && childA != childB)" 

Without the inequality expression, the same child could satisfy both the first contains expression and the second contains expression.

Some JDO implementations support unconstrained variables. In this case, the unconstrained variable ranges over the corresponding Extent of its application data class. If the class does not have a corresponding Extent, then the subexpression using the variable always evaluates to false. For example, when the JDO implementation supports unconstrained variables, you can find all the Foo objects that have the same name by declaring the variable

 "Foo y;" 

and using this query filter:

 "name == y.name && this != y" 

The Special Syntax and Semantics of the contains Method

Informally, a variable is constrained if it is limited to the objects contained in a persistent collection field. Formally, the definition is based on the syntax of the query filter. A variable is constrained if each OR expression that uses the variable also uses a contains query method to limit the variable's range to the members of a collection field. The contains expression must be the left term of an AND expression where the variable is used in the right term. The following query filter, used earlier:

 "allMyBars.contains(someBar) && someBar.size == 3" 

satisfies the requirements, but the following filter, which commutes the && operator, does not:

 "someBar.size == 3 && allMyBars.contains(someBar)" 

Implementations that require constrained variables accept only the first form of the query.

Other query filters may appear to satisfy the conditions but, in fact, do not. For example, the following filter appears to satisfy the condition:

 "books.contains(someBook) && someBook.categories.contains(someCat) &&       someCat.name == \"Travel\"" 

In fact, the conditions for a constrained variable are not met, since the AND operator associates to the left. As a result, the expression

 books.contains(someBook) && someBook.categories.contains(someCat) 

is the left term for the last expression.

 someCat.name == "Travel" 

Because the left term is an AND expression instead of the required contains expression, JDO implementations that require constrained variables do not accept this query filter.

On the other hand, when parentheses are added as shown in the following example, JDO accepts the query filter because the requirements for a constrained variable are met:

 "books.contains(someBook) &&       (someBook.categories.contains(someCat) && someCat.name == \"Travel\")" 

The Semantics of Constrained Variables

Semantically, a constrained variable is an existence expression. Consider again the example that follows:

 "allMyBars.contains(someBar) && someBar.size == 3" 

This JDOQL expression means, "There exists a someBar that is a member of allMyBars such that someBar.size equals 3."

For a practical example of the semantics in action, consider the two Foo and four Bar objects presented in Table 2-3. The preceding query filter selects every Foo object that has at least one Bar object of size 3 in its allMyBars collection. For the objects in Table 2-3, foo1 is selected because of bar2, and foo2 is selected because of bar3.

Table 2-3: The State of Two Foo and Four Bar Objects

Object

allMyBars

Size

foo1

bar1, bar2

foo2

bar3, bar4

bar1

2

bar2

3

bar3

3

bar4

4

The preceding JDOQL expression can be negated as shown in the following expression:

 "!(allMyBars.contains(someBar) && someBar.size == 3)" 

In JDO 1.0, there was some confusion on the semantics of this negated query expression. JDO 1.0.1 resolved this confusion. Starting with JDO 1.0.1, this negated expression means, "There does not exist a someBar that is a member of allMyBars such that someBar.size equals 3."

Without delving into the propositional calculus that underlies this logic, you can see intuitively that the following for-each statement is semantically equivalent to the negated existence statement, "For each someBar that is a member of allMyBars, someBar.size is not equal to 3."

Regardless of which semantic statement you prefer, the meaning of the negated query expression is the same. When the query expression is evaluated, foo1 is rejected because of bar2, and foo2 is rejected because of bar3. The query results in this case are empty.

DeMorgan Rules Do Not Apply to Constrained Variables

In Boolean logic, DeMorgan rules allow semantically neutral transformations of some negated Boolean expressions. For example, if A and B are Boolean expressions, then the expressions

     !(A && B)     !A || !B 

are semantically equivalent, that is to say, either both are true or both are false. The same is the case for the following pair:

     !(A || B)     !A && !B 

In the case of JDOQL, the expressions that use constrained variables cannot be transformed in either a syntactically or semantically neutral way by the DeMorgan rules. Using DeMorgan's rules, the negated query expression

 "!(allMyBars.contains(someBar) && someBar.size == 3)" 

transforms to the following query expression:

 "!allMyBars.contains(someBar) || !(someBar.size == 3)" 

The transformed query expression no longer contains a constrained variable, since the syntactic rules discussed earlier for a constrained variable have been broken. As a result, those JDO implementations that do not support unconstrained variables reject this query filter based on its syntax.

For those implementations that support unconstrained variables, the semantics of the transformed filter have also changed. The left side of the OR is true when there is a Bar object anywhere in the extent of Bar objects that is not a member of this Foo object's allMyBars collection. The right side of the OR is true, when there is a Bar object anywhere in the extent of Bar objects whose size is not 3. In the case of the objects in Table 2-3, foo1 is selected because of bar1, bar3, or bar4, and foo2 is selected because of bar1, bar2, or bar4. As you can see, the transformed query filter does not select the Foo objects that the untransformed filter selected.

Because DeMorgan rules fail for JDOQL expressions that use constrained variables, you must take care when constructing complex, and particularly negated, expressions that navigate collections. Fortunately for application developers, complex JDOQL filters are the exception rather than the rule.




Using and Understanding Java Data Objects
Using and Understanding Java Data Objects
ISBN: 1590590430
EAN: 2147483647
Year: 2005
Pages: 156
Authors: David Ezzio

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net