EJB QL Examples

EJB QL is expressed in terms of the abstract persistence schema of an entity bean: its abstract schema name, CMP fields, and CMR fields. EJB QL uses the abstract schema names to identify beans, the CMP fields to specify values, and the CMR fields to navigate across relationships.

To discuss EJB QL, we will make use of the relationships among the Customer, Address, CreditCard, Cruise, Ship, Reservation, and Cabin EJBs defined in Chapter 7. Figure 8-1 is a class diagram that shows the direction and cardinality (multiplicity) of the relationships among these beans.

Titan Cruises class diagram

Figure 8-1. Titan Cruises class diagram

Simple Queries

The simplest EJB QL statement has no WHERE clause and only one abstract schema type. For example, you could define a query method to select all Customer beans:

SELECT OBJECT( c ) FROM Customer AS c

The FROM clause determines which entity bean types will be included in the select statement (i.e., provides the scope of the select). In this case, the FROM clause declares the type to be Customer, which is the abstract schema name of the Customer EJB. The AS c part of the clause assigns c as the identifier of the Customer EJB. This is similar to SQL, which allows an identifier to be associated with a table. Identifiers can be any length and follow the same rules that are applied to field names in the Java programming language. However, identifiers cannot be the same as existing <ejb-name> or <abstract-schema-name> values. In addition, identification variable names are not case-sensitive, so an identifier of customer would be in conflict with an abstract schema name of Customer. For example, the following statement is illegal because Customer is the abstract schema name of the Customer EJB:

SELECT OBJECT( customer ) FROM Customer AS customer

The AS operator is optional (its assumed) and is used in many, but not all, of the examples in this chapter. The following two statements are equivalent:

SELECT OBJECT(c) FROM Customer AS c

SELECT OBJECT(c) FROM Customer c

The SELECT clause determines the type of values that will be returned. In this case, it’s the Customer entity bean, as indicated by the c identifier.

The OBJECT() operator is required when the SELECT type is a solitary identifier for an entity bean. The reason for this requirement is pretty vague (and in the author’s opinion, the specification would have been better off without it), but it is required whenever the SELECT type is an entity bean identifier. The OBJECT() operator is not used if the SELECT type is expressed using a path, which is discussed below.

Simple Queries with Paths

EJB QL allows SELECT clauses to return any CMP or single CMR field. For example, we can define a simple select statement to return the last names of all of Titan’s customers, as follows:

SELECT c.lastName FROM Customer AS c

The SELECT clause uses a simple path to select the Customer EJB’s lastName CMP field as the return type. EJB QL uses the CMP and CMR field names declared in <cmp-field> and <cmr-field> elements of the deployment descriptor. This navigation leverages the same syntax as the Java programming language, specifically the dot (.) navigation operator. For example, the previous EJB QL statement is based on the Customer EJB’s deployment descriptor:

<ejb-jar>
    <enterprise-beans>
        <entity>
            <ejb-name>CustomerEJB</ejb-name>
            <home>com.titan.customer.CustomerHomeRemote</home>
            <remote>com.titan.customer.CustomerRemote</remote>
            <ejb-class>com.titan.customer.CustomerBean</ejb-class>
            <persistence-type>Container</persistence-type>
            <prim-key-class>java.lang.Integer</prim-key-class>
            <reentrant>False</reentrant>
            <abstract-schema-name>Customer</abstract-schema-name>
            <cmp-version>2.x</cmp-version>
            <cmp-field><field-name>id</field-name></cmp-field>
            <cmp-field><field-name>lastName</field-name></cmp-field>
            <cmp-field><field-name>firstName</field-name></cmp-field>

You can also use CMR field types in simple select statements. For example, the following EJB QL statement selects all the CreditCard EJBs from all the Customer EJBs:

SELECT c.creditCard FROM Customer c

In this case, the EJB QL statement uses a path to navigate from the Customer EJBs to their creditCard relationship fields. The creditCard identifier is obtained from the <cmr-field> name used in the relationship element that describes the Customer-CreditCard relationship:

<enterprise-beans>
    <entity>
        <ejb-name>CustomerEJB</ejb-name>
        ...
        <abstract-schema-name>Customer</abstract-schema-name>
    </entity>
</enterprise-beans>
...
<relationships>
    <ejb-relation>
        <ejb-relation-name>Customer-CreditCard</ejb-relation-name>

        <ejb-relationship-role>
            <ejb-relationship-role-name>
                Customer-has-a-CreditCard
            </ejb-relationship-role-name>
            <multiplicity>One</multiplicity>
            <relationship-role-source>
                <ejb-name>CustomerEJB</ejb-name>
            </relationship-role-source>
<cmr-field>
                    <cmr-field-name>creditCard</cmr-field-name>
               </cmr-field>
        </ejb-relationship-role>
        <ejb-relationship-role>
        ...

Paths can be as long as required. It’s common to use paths that navigate over one or more CMR fields to end at either a CMR or CMP field. For example, the following EJB QL statement selects all the city CMP fields of all the Address EJBs in each Customer EJB:

SELECT c.homeAddress.city FROM Customer c

In this case, the path uses the abstract schema name of the Customer EJB, the Customer EJB’s homeAddress CMR field, and the Address EJB’s city CMP field. Using paths in EJB QL is similar to navigating through object references in the Java language.

To illustrate more complex paths, we’ll need to expand the class diagram. Figure 8-2 shows that the CreditCard EJB is related to a CreditCompany EJB that has its own Address EJB.

Expanded class diagram for CreditCard EJB

Figure 8-2. Expanded class diagram for CreditCard EJB

Using these relationships, we can specify a more complex path that navigates from the Customer EJB to the CreditCompany EJB to the Address EJB. The following EJB QL statement selects all the addresses of all the credit card companies:

SELECT c.creditCard.creditCompany.address FROM Customer AS c

The EJB QL statement could also navigate all the way to the Address bean’s CMP fields. For example, the following statement selects all the cities in which the credit card companies that distribute credit cards used by Titan’s customers are based:

SELECT c.creditCard.creditCompany.address.city FROM Customer AS c

Note that these EJB QL statements return address CMR fields or city CMP fields only for those credit card companies responsible for cards owned by Titan’s customers. The address information of any credit card companies whose cards are not currently used by Titan’s customers won’t be included in the results.

Paths cannot navigate beyond CMP fields. For example, imagine that the Address EJB uses a ZipCode class as its zip CMP field:

public class ZipCode implements java.io.Serializable {
    public int mainCode;
    public int codeSuffix;
    ...
}

It would be illegal to attempt to navigate to one of the ZipCode class’s instance fields:

// this is illegal
SELECT c.homeAddress.zip.mainCode FROM Customer AS c

CMP fields cannot be further decomposed and navigated by paths. All CMP fields are considered opaque.

The paths used in SELECT clauses of EJB QL statements must always end with a single type. They may not end with a collection-based relationship field. For example, the following is not legal because the CMR field reservations is a collection-based relationship field:

// this is illegal
SELECT c.reservations FROM Customer AS c

In fact, it’s illegal to navigate across a collection-based relationship field. The following EJB QL statement is also illegal, even though the path ends in a single-type relationship field:

SELECT c.reservations.cruise FROM Customer AS c

If you think about it, this limitation makes sense. You cannot use a navigation operator (.) in Java to access elements of a java.util.Collection object either. For example, you cannot do the following (assume getReservations() returns a java.util.Collection type):

// this is illegal in the Java programming language
customer.getReservations().getCruise();

Referencing the elements of a collection-based relationship field is possible in EJB QL, but it requires the use of an IN operator and an identification assignment in the FROM clause, which are discussed next.

The IN Operator

Many relationships between entity beans are collection-based, and being able to access and select from these relationships is important. We’ve seen that it is illegal to select elements directly from a collection-based relationship. To overcome this limitation, EJB QL introduces the IN operator, which allows an identifier to represent individual elements in a collection-based relationship field.

The following query uses the IN operator to select the elements from a collection-based relationship. It returns all the reservations of all the customers:

SELECT OBJECT( r ) 
FROM Customer AS c,  IN( c.reservations ) AS r

The IN operator assigns the individual elements in the reservations CMR field to the identifier r. Once we have an identifier to represent the individual elements of the collection, we can reference them directly and even select them in the EJB QL statement. We can also use the element identifier in path expressions. For example, the following EJB QL statement will select every cruise for which Titan’s customers have made reservations:

SELECT r.cruise 
FROM Customer AS c, IN( c.reservations ) AS r

The identifiers assigned in the FROM clause are evaluated from left to right. Once you declare an identifier, you can use it in subsequent declarations in the FROM clause. Notice that the identifier c, which was declared first, was subsequently used in the IN operator to define the identifier r.

Tip

The OBJECT() operator is used for single identifiers in the select statement and not for path expressions. While this convention makes little sense, it is nonetheless required by the EJB 2.0 specification. As a rule of thumb, if the select type is a solitary identifier of an entity bean, it must be wrapped in an OBJECT() operator. If the select type is a path expression, this is not necessary.

Identification chains, in which subsequent identifications depend on previous identifications, can become very long. The following EJB QL statement uses two IN operators to navigate two collection-based relationships and a single CMR relationship. While not necessarily useful, this statement demonstrates how a query can use IN operators across many relationships:

SELECT cbn.ship
FROM Customer AS c, IN ( c.reservations ) AS r, 
IN( r.cabins ) AS cbn

The IN OperatorPlease refer to Workbook Exercise 8.1, Simple EJB QL Statements. This workbook is available free, in PDF format, at http://www.oreilly.com/catalog/entjbeans3/workbooks.

Using DISTINCT

The DISTINCT keyword ensures that the query does not return duplicates. This is especially valuable when applied to EJB QL statements used by find methods. Find methods in CMP 2.0 have only one return type, java.util.Collection, which may return duplicates. However, if the keyword DISTINCT is used, the query results of the find method will not contain duplicates.

For example, the following find method and its associated query will return duplicates:

// the find method declared in the remote or local home interface
public java.util.Collection findAllCustomersWithReservations() 

// the EJB QL statement associated with the find method
SELECT OBJECT( cust ) FROM Reservation res, IN (res.customers) cust

The previous find method and associated EJB QL statement will return all Customer EJBs with reservations, but if a Customer EJB has more than one reservation, there will be duplicate references to that Customer EJB in the result Collection. Using the DISTINCT keyword will ensure that each Customer EJB is represented only once in the result Collection:

SELECT DISTINCT OBJECT( cust ) FROM Reservation res, 
IN (res.customers) cust

The DISTINCT keyword can also be used with select methods. It works the same way for select methods that have a return type of Collection. If the select method’s return type is java.util.Set, the distinct (no duplicates) result will be returned whether the DISTINCT keyword is used or not.

The Set type is explicitly defined as having no duplicates. Using the Set return type in combination with the DISTINCT keyword is redundant, but it is not illegal.

The WHERE Clause and Literals

You can also use literal values in EJB QL to narrow the scope of the elements selected. This is accomplished through the WHERE clause, which behaves in much the same way as the WHERE clause in SQL.

For example, you could define an EJB QL statement to select all the Customer EJBs that use a specific brand of credit card. The literal in this case is a String literal. Literal strings are enclosed by single quotes. Literal values that include a single quote, like the restaurant name “Wendy’s,” use two single quotes to escape the quote: 'Wendy''s'. The following statement returns customers that use the American Express credit card:

SELECT OBJECT( c ) FROM Customer AS c 
WHERE c.creditCard.organization = 'American Express'

Path expressions are always used in the WHERE clause in the same way they’re used in the SELECT clause. When making comparisons with a literal, the path expression must evaluate to a CMP field; you can’t compare a CMR field with a literal.

In addition to literal strings, literals can be exact numeric values (long types) and approximate numeric values (double types). Exact numeric literal values are expressed using the Java integer literal syntax (321, -8932, +22). Approximate numeric literal values are expressed using Java floating point literal syntax in scientific (5E3, -8.932E5) or decimal (5.234, 38282.2) notation.

For example, the following EJB QL statement selects all the ships that weigh 100,000.00 metric tons:

SELECT OBJECT( s ) 
FROM Ship AS s
WHERE s.tonnage = 100000.00

Boolean literal values use TRUE and FALSE. Here’s an EJB QL statement that selects all the customers who have good credit:

SELECT OBJECT( c ) FROM Customer AS c 
WHERE c.hasGoodCredit = TRUE

The WHERE Clause and Input Parameters

Query methods (find and select methods) that use EJB QL statements may specify method arguments. Input parameters allow those method arguments to be mapped to EJB QL statements and are used to narrow the scope of the query. For example, the ejbSelectByCity() method is designed to select all the customers that reside in a particular city and state:

public abstract class CustomerBean implements javax.ejb.EntityBean {
    ...
    public abstract Collection ejbSelectByCity(String city,String state)
        throws FinderException;
    ...
}

The EJB QL statement for this method would use the city and state arguments as input parameters:

SELECT OBJECT( c ) FROM Customer AS c
WHERE c.homeAddress.state = ?2 
AND c.homeAddress.city = ?1

Input parameters use a ? prefix followed by the argument’s position, in order of the query method’s parameters. In this case, city is the first argument listed in the ejbSelectByCity() method and state is the second. When a query method declares one or more arguments, the associated EJB QL statement may use some or all of the arguments as input parameters.

Input parameters are not limited to simple CMP field types; they can also be EJB object references. For example, the following find method, findByShip(), is declared in the Cruise bean’s local home interface:

public interface CruiseLocalHome extends javax.ejb.EJBLocalHome {
    public Collection findByShip( ShipLocal ship ) 
        throws FinderException;
}

The EJB QL statement associated with this method would use the ship argument to locate all the cruises scheduled for the specified Ship bean:

SELECT OBJECT( crs ) FROM Cruise AS crs 
WHERE crs.ship = ?1

When an EJB object is used as an input parameter, the container bases the comparison on the primary key of the EJB object. In this case, it searches through all the Cruise EJBs looking for references to a Ship EJB with the same primary key value as the one the Ship EJB passed to the query method.

The WHERE Clause and Operator Precedence

The WHERE clause is composed of conditional expressions that reduce the scope of the query and limit the number of items selected. Several conditional and logical operators can be used in expressions; they are listed below in order of precedence. The operators at the top of the list have the highest precedence and are evaluated first:

  1. Navigation operator (.)

  2. Arithmetic operators:

    +, - unary
    *, / multiplication and division
    +, - addition and subtraction
  3. Comparison operators:

    =, >, >=, <, <=, <> (not equal)
    LIKE, BETWEEN, IN, IS NULL, IS EMPTY, MEMBER OF
  4. Logical operators:

    NOT, AND, OR

The WHERE Clause and CDATA Sections

EJB QL statements are declared in XML deployment descriptors. XML uses the greater than (>) and less than (<) characters as delimiters for tags, so using these symbols in the EJB QL statements will cause parsing errors unless CDATA sections are used. For example, the following EJB QL statement causes a parsing error, because the XML parser interprets the > symbol as an incorrectly placed XML tag delimiter:

<query>
    <query-method>
        <method-name>findWithPaymentGreaterThan</method-name>
        <method-params>
            <method-param>java.lang.Double</method-param>
        </method-params>
    </query-method>
    <ejb-ql>
        SELECT OBJECT( r ) FROM Reservation r
        WHERE r.amountPaid  > ?1
    </ejb-ql>
</query>

To avoid this problem, you must place the EJB QL statement in a CDATA section:

<query>
    <query-method>
        <method-name>findWithPaymentGreaterThan</method-name>
        <method-params>
            <method-param>java.lang.Double</method-param>
        </method-params>
    </query-method>
    <ejb-ql> 
        <![CDATA[
        SELECT OBJECT( r ) FROM Reservation r
        WHERE r.amountPaid  > 300.00
        ]]>
    </ejb-ql>
</query>

The CDATA section takes the form <![CDATA[ literal-text ]]>. When an XML processor encounters a CDATA section, it doesn’t attempt to parse the contents enclosed by the CDATA section; instead, the parser treats the contents as literal text.[33]

The WHERE Clause and Arithmetic Operators

The arithmetic operators allow a query to perform arithmetic in the process of doing a comparison. In EJB QL, arithmetic operators can be used only in the WHERE clause, not in the SELECT clause.

The following EJB QL statement returns references to all the Reservation EJBs that will be charged a port tax of more than $300.00:

SELECT OBJECT( r ) FROM Reservation r
WHERE (r.amountPaid * .01)  > 300.00

The rules applied to arithmetic operations are the same as those used in the Java programming language, where numbers are widened or promoted in the process of performing a calculation. For example, multiplying a double and an int value requires that the int first be promoted to a double value. (The result will always be that of the widest type used in the calculation, so multiplying an int and a double results in a double value.)

String, boolean, and EJB object types cannot be used in arithmetic operations. For example, using the addition operator with two String values is considered an illegal operation. There is a special function for concatenating String values, which is covered later in Section 8.3.19.

The WHERE Clause and Logical Operators

Logical operators such as AND , OR , and NOT operate the same way in EJB QL as their corresponding logical operators in SQL.

Logical operators evaluate only Boolean expressions, so each operand (i.e., each side of the expression) must evaluate to true or false. Logical operators have the lowest precedence so that all the expressions can be evaluated before they are applied.

The AND and OR operators may not, however,behave like their Java language counterparts && and ||. EJB QL does not specify whether the righthand operands are evaluated conditionally. For example, the && operator in Java evaluates its righthand operand only if the lefthand operand is true. Similarly, the || logical operator evaluates the righthand operand only if the lefthand operand is false. We can’t make the same assumption for the AND and OR operators in EJB QL. Whether these operators evaluate righthand operands depends on the native query language into which the statements are translated. It’s best to assume that both operands are evaluated on all logical operators.

NOT simply reverses the Boolean result of its operand; expressions that evaluate to the Boolean value of true become false, and vice versa.

The WHERE Clause and Comparison Symbols

Comparison operators, which use the symbols =, >, >=, <, <=, and <>, should be familiar to you. The following statement selects all the Ship EJBs whose tonnage CMP field is greater than or equal to 80,000 tons but less than or equal to 130,000 tons:

SELECT OBJECT( s ) FROM Ship s
WHERE s.tonnage >= 80000.00 AND s.tonnage <= 130000.00

Only the = and <> (not equal) operators may be used on String, boolean, and EJB object references. The greater-than and less-than symbols (>, >=, <, <=) can be used only on numeric values. It would be illegal, for example, to use the greater-than or less-than symbols to compare two Strings. There is no mechanism to compare Strings in this way in EJB QL.

The WHERE Clause and Equality Semantics

While it’s legal to compare an exact numeric value (short, int, long) to an approximate numeric value (double, float), all other equality comparisons must compare the same types. You cannot, for example, compare a String value of 123 to the Integer literal 123. However, you can compare two String types for equality.

You can also compare EJB objects for equality, but these too must be of the same type. To be more specific, they must both be EJB object references to beans from the same deployment. As an example, the following method finds all the Reservation EJBs made by a specific Customer EJB:

public interface ReservationHomeLocal extends EJBLocalHome {
    public Collection findByCustomer(CustomerLocal customer) 
        throws FinderException;
    ...
}

The matching EJB QL statement uses the customer argument as an input parameter:

SELECT OBJECT( r ) 
FROM Reservation r, IN ( r.customers ) cust
WHERE  cust = ?1

It’s not enough for the EJB object used in the comparison to implement the CustomerLocal interface; it must be of the same bean type as the Customer EJB used in the Reservation’s customers CMR field. In other words, they must be from the same deployment. Once it’s determined that the bean is the correct type, the actual comparison is performed on the beans’ primary keys. If they have the same primary key, they are considered equal.

You cannot use java.util.Date objects in equality comparisons. To compare dates you must use the long millisecond value of the date, which means that the date must be persisted in a long CMP field and not a java.util.Date CMP field. The input value or literal must also be a long value.

The WHERE Clause and BETWEEN

The BETWEEN clause is an inclusive operator specifying a range of values. In this example, we use it to select all ships weighing between 80,000 and 130,000 tons:

SELECT OBJECT( s ) FROM Ship s
WHERE s.tonnage BETWEEN 80000.00 AND 130000.00

The BETWEEN clause may be used only on numeric primitives (byte, short, int, long, double, float) and their corresponding java.lang.Number types (Byte, Short, Integer, etc.). It cannot be used on String, boolean, or EJB object references.

Using the NOT logical operator in conjunction with BETWEEN excludes the range specified. For example, the following EJB QL statement selects all the ships that weigh less than 80,000 tons or greater than 130,000 tons but excludes everything in between:

SELECT OBJECT( s ) FROM Ship s
WHERE s.tonnage NOT BETWEEN 80000.00 AND 130000.00

The net effect of this query is the same as if it had been executed with comparative symbols, like this:

SELECT OBJECT( s ) FROM Ship s
WHERE s.tonnage < 80000.00 OR s.tonnage > 130000.00

The WHERE Clause and IN

The IN conditional operator used in the WHERE clause is not the same as the IN operator used in the FROM clause. In the WHERE clause, IN tests for membership in a list of literal string values and can be used only with operands that evaluate to string values. For example, the following EJB QL statement uses the IN operator to select all the customers who reside in a specific set of states:

SELECT OBJECT( c ) FROM Customer c
WHERE c.homeAddress.state IN ('FL', 'TX', 'MI', 'WI', 'MN')

Applying the NOT operator to this expression reverses the selection, excluding all customers who reside in the list of states:

SELECT OBJECT( c ) FROM Customer c
WHERE c.homeAddress.city NOT IN ('FL', 'TX', 'MI', 'WI', 'MN')

If the field tested is null, the value of the expression is “unknown”, which means it cannot be predicted.

The WHERE Clause and IS NULL

The IS NULL comparison operator allows you to test whether a path expression is null. For example, the following EJB QL statement selects all the customers who do not have home addresses:

SELECT OBJECT( c ) FROM Customer c
WHERE c.homeAddress IS NULL

Using the NOT logical operator, we can reverse the results of this query, selecting all the customers who do have home addresses:

SELECT OBJECT( c ) FROM Customer c
WHERE c.homeAddress IS NOT NULL

When null fields appear in comparison operations such as IN and BETWEEN, they can have side effects. In most cases, evaluating a null field in a comparison operation (other than IS NULL) produces in an UNKNOWN result. Unknown evaluations throw the entire EJB QL result set into question; since we cannot predict the outcome of the EJB QL statement, it is unreliable. One way to avoid this situation is to require that fields used in the expressions have values. This requires careful programming. To ensure that an entity bean field is never null, you must initialize the field when the entity is created. For primitive values, this not a problem; they have default values, so they cannot be null. Other fields, such as single CMR fields and object-based CMP fields such as String, must be initialized in the ejbCreate() and ejbPostCreate() methods.

The WHERE Clause and IS EMPTY

The IS EMPTY operator allows the query to test if a collection-based relationship is empty. Remember from Chapter 7 that a collection-based relationship will never be null. If a collection-based relationship field has no elements, it will return an empty Collection or Set.

Testing whether a collection-based relationship is empty has the same purpose as testing whether a single CMR field or CMP field is null: it can be used to limit the scope of the query and items selected. For example, the following query selects all the cruises that have not booked any reservations:

SELECT OBJECT( crs ) FROM Cruise crs
WHERE crs.reservations IS EMPTY

The NOT operator reverses the result of IS EMPTY. The following query selects all the cruises that have at least one reservation:

SELECT OBJECT( crs ) FROM Cruise crs
WHERE crs.reservations IS NOT EMPTY

It is illegal to use IS EMPTY against collection-based relationships that have been assigned an identifier in the FROM clause:

// illegal query
SELECT OBJECT( r ) 
FROM Reservation r, IN( r.customers ) c
WHERE 
r.customers IS NOT EMPTY AND
c.address.city = 'Boston'

While this query appears to be good insurance against UNKNOWN results, it’s not. In fact, it’s an illegal EJB QL statement, because the IS EMPTY operator cannot be used on a collection-based relationship identified in an IN operator in the FROM clause. Because the relationship is specified in the IN clause, only those Reservation EJBs that have a nonempty customers field will be included in the query; any Reservation EJB that has an empty CMR field will be excluded because its customers elements cannot be assigned the c identifier.

The WHERE Clause and MEMBER OF

The MEMBER OF operator is a powerful tool for determining whether an EJB object is a member of a specific collection-based relationship. The following query determines whether a particular Customer (specified by the input parameter) is a member of any of the Reservation-Customer relationships:

SELECT OBJECT( crs ) 
FROM Cruise crs, IN (crs.reservations) res, Customer cust
WHERE  
cust = ?1 
  AND
cust MEMBER OF res.customers

Applying the NOT operator to MEMBER OF has the reverse effect, selecting all the cruises on which the specified customer does not have a reservation:

SELECT OBJECT( crs ) 
FROM Cruise crs, IN (crs.reservations) res, Customer cust
WHERE  
cust = ?1 
  AND
cust NOT MEMBER OF res.customers

Checking whether an EJB object is a member of an empty collection always returns false.

The WHERE Clause and LIKE

The LIKE comparison operator allows the query to select String type CMP fields that match a specified pattern. For example, the following EJB QL statement selects all the customers with hyphenated names, like “Monson-Haefel” and “Berners-Lee”:

SELECT OBJECT( c ) FROM Customer c
WHERE c.lastName LIKE '%-%'

You can use two special characters when establishing a comparison pattern: % (percent) stands for any sequence of characters, and _ (underscore) stands for any single character. You can use % and _ characters at any location within a string pattern. If a % or _ actually occurs in the string, you can escape it with the character. The NOT logical operator reverses the evaluation so that matching patterns are excluded.

The following examples show how the LIKE clause would evaluate String type CMP fields:

  • phone.number LIKE '617%'

    True for “617-322-4151”
    False for “415-222-3523”
  • cabin.name LIKE 'Suite _100'

    True for “Suite A100”
    False for “Suite A233”
  • phone.number NOT LIKE '608%'

    True for “415-222-3523”
    False for “608-233-8484”
  • someField.underscored LIKE '\_%'

    True for “_xyz”
    False for “abc”
  • someField.percentage LIKE '\%%'

    True for “% XYZ”
    False for “ABC”

The LIKE operator cannot be used with input parameters. This is an important point that is confusing to many new EJB developers. The LIKE operator compares a String type CMP field to a String literal. As it is currently defined, it cannot be used in a comparison with an input parameter, because an input parameter is, by definition, unknown until the method is invoked. Only the input parameter’s type is known at deployment time. How would you compare a CMP field, whose value varies, with an arbitrary input parameter using a comparison pattern? It’s just not possible. The comparison pattern is composed of special comparison characters (% and _ ) as well as regular characters. The comparison pattern must be known at deployment time in order to match it against the variable CMP fields at runtime.

The WHERE Clause and Functional Expressions

EJB QL has six functional expressions that allow for simple String manipulation and two basic numeric operations. The String functions are:

CONCAT(String1, String2)

Returns the String that results from concatenating String1 and String2.

LENGTH(String)

Returns an int indicating the length of the string.

LOCATE(String1, String2 [, start])

Returns an int indicating the position at which String1 is found within String2. If it’s present, start indicates the character position in String2 at which the search should start.

SUBSTRING(String1, start, length)

Returns the String consisting of length characters taken from String1, starting at the position given by start.

The start and length parameters indicate positions in a String as integer values. You can use these expressions in the WHERE clause to help refine the scope of the items selected. Here is an example of how the LOCATE and LENGTH functions might be used:

SELECT OBJECT( c ) 
FROM Customer c
WHERE 
LENGTH(c.lastName) > 6
  AND
LOCATE( c.lastName, 'Monson') > -1

This EJB QL statement selects all the customers with Monson somewhere in their last name, but specifies that the name must be longer than 6 characters. Therefore, “Monson-Haefel” and “Monson-Ares” evaluate to true, but “Monson” returns false because it has only 6 characters.

The arithmetic functions in EJB QL are:

ABS(number)

Returns the absolute value of a number (int, float, or double)

SQRT(double)

Returns the square root of a double

The WHERE Clause and Functional ExpressionsPlease refer to Workbook Exercise 8.2, Complex EJB QL Statements. This workbook is available free, in PDF format, at http://www.oreilly.com/catalog/entjbeans3/workbooks.



[33] To learn more about XML and the use of CDATA sections, see XML in a Nutshell by Elliotte Rusty Harold and W. Scott Means (O’Reilly).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset