7.1. Introducing Container-Managed Persistence

Previous to the EJB 2.0 specification, a bean developer could only use container-managed persistence with isolated entity beans—that is, beans with no relationship fields (foreign keys) to other entity beans. This restriction made CMP possible for simple entity beans only and prevented CMP use with real-world, enterprise applications.

That all changed with the EJB 2.0 specification. With the addition of local interfaces, bean developers can now specify entity bean relationships and build enterprise systems with data that models real-world requirements more effectively. As we explore the properties of entity beans with container-managed persistence, keep in mind that CMP and BMP entity beans still share a common structure. In fact, a BMP and CMP entity bean can have identical local home and local interface classes. The differences are in the details of the bean implementation class and the deployment descriptor that holds the necessary relationship and query descriptions.

Design Guideline

Although a bean developer may implement the remote interface and remote home interface for CMP entity beans, local and local home interfaces are required for container-managed entity bean relationships. In this chapter, we will use only local interfaces with our CMP entity bean examples.


Properties of CMP Entity Beans

Like BMP entity beans, CMP entity beans represent business data. They consist of a local interface with business methods and a local home interface with create, finder, and home methods. As with BMP, CMP bean developers must specify the underlying representation of the bean's persistent data. This persistent data constitutes the bean's Container-Managed Persistence (CMP) fields. CMP bean developers must also specify the bean's Container-Managed Relationship (CMR) fields. The CMR fields hold the local interface (or a collection thereof) of related entity beans. The bean implementation class of CMP entity beans defines abstract access methods to all its CMP and CMR fields. An entity bean's local interface may expose one or more of these access methods to clients. The governing business rules will typically dictate the required visibility of the CMP field and CMR field access methods.

Recall that our Customer EJB (BMP) example in Chapter 6 contains no relationships with other entity beans; consequently, that version of the Customer EJB does not have any access methods to relationship fields or database access code to implement entity bean relationships. In this chapter, however, we will show you relationships with our entity bean examples.

Like BMP entity beans, clients instantiate CMP entity beans through the create() method (which inserts data into the underlying data store) or one of the finder methods (which performs a select on the database). The remove() method performs a database delete operation.

The advantages of CMP include increased portability with the database access mechanism and a large reduction in the amount of code that the bean developer must write. Since database access is managed by the container, CMP entity beans do not contain any database access (SQL) code. Furthermore, as CMP implementations improve, performance improvements will undoubtedly follow.

However, CMP has some limitations. The current EJB Query Language (which bean developers must use to create custom finder methods) lacks the Order By directive. With CMP you cannot persist SQL Date types (but you can convert Java Date or Calendar objects to longs—we'll show you how). CMP entity beans require detailed deployment descriptor tags. You must describe the persistent data fields, relationships with other entity beans, and custom queries (using EJB QL). This task's level of difficulty and tedium depends on the elegance of the application server's tools. Finally, since the container generates the persistence code, it's more difficult to track down problems.

That all said, we think CMP offers a promising approach. CMP removes the burden of trying to optimize database updates for tricky concurrency scenarios. In the long run, this promise (and eventual realization) of optimization by the container is a true gain for enterprise businesses.

Abstract Persistence Schema

An entity bean's abstract persistence schema is the description of its persistent (CMP) fields and relationship (CMR) fields. The bean developer provides this description through a deployment tool or an administrative program provided by the application server. Through both code inspection and declarative statements provided by the bean developer, the deployment tool builds the appropriate schema in the entity bean's deployment descriptor. How is this done? To answer this question, let's examine three entity beans that model the business data of a virtual shopping application.

Figure 7-1 shows three entity beans (Customer EJB, Order EJB, and LineItem EJB), the business data that each bean models, and their relationship to each other. Note that a customer may have zero or more associated orders (0..*), but an order must have one customer. A line item has exactly one order. An order has one or more line items (1..*). A line item cannot exist without an order and an order cannot exist without a customer. A customer, however, may exist without an order. Also, an order must have at least one line item. Each entity bean has CMP fields and its own primary key (denoted PK).

Figure 7-1. Abstract Schema for the Music Collection Virtual Shopping Application


We've labeled the CMR relationship fields next to the navigational arrows. For example, orders designates the zero or more Order EJBs associated with a Customer EJB, and customer is the one Customer EJB associated with an Order EJB. Likewise, lineItems denotes the one or more LineItem EJBs associated with an Order EJB. Finally, order is the one Order EJB associated with a LineItem EJB.

Listing 7.1 shows a portion of the XML deployment descriptor for Customer EJB. As we explore CMP, we'll cover the details of this entity bean and the related Order EJB and LineItem EJB. For now, simply note the correspondence between the persistent data fields for Customer EJB shown in Figure 7-1 and their identification in the deployment descriptor (tag <cmp-field>). The <primkey-field> tag identifies persistent field customerID as the primary key. Also, compare the Customer EJB - Order EJB relationship shown in Figure 7-1 to its description in Listing 7.1 (tag <ejb-relation>). Finally, note that the abstract persistence schema name for Customer EJB is defined in the deployment descriptor (tag <abstract-schema-name>).

Listing 7.1. Deployment Descriptor for Customer EJB
<entity>
     <display-name>CustomerBean</display-name>
     <ejb-name>CustomerBean</ejb-name>
     <local-home>CustomerLocalHome</local-home>
     <local>CustomerLocal</local>
     <ejb-class>CustomerBean</ejb-class>

     <persistence-type>Container</persistence-type>
     <prim-key-class>java.lang.String</prim-key-class>
     <reentrant>False</reentrant>
     <cmp-version>2.x</cmp-version>
     <abstract-schema-name>customerEJB
      </abstract-schema-name>
     <cmp-field>
       <description>no description</description>
       <field-name>name</field-name>
     </cmp-field>

     <cmp-field>
       <description>no description</description>
       <field-name>password</field-name>
     </cmp-field>
     <cmp-field>
       <description>no description</description>
       <field-name>email</field-name>
     </cmp-field>

     <cmp-field>
       <description>no description</description>
       <field-name>customerID</field-name>
     </cmp-field>
     <cmp-field>
       <description>no description</description>
       <field-name>ordersPending</field-name>
     </cmp-field>

     <primkey-field>customerID</primkey-field>
     <security-identity>
       <description></description>
       <use-caller-identity></use-caller-identity>
     </security-identity>
   </entity>
<ejb-relation>
    <ejb-relation-name></ejb-relation-name>
    <ejb-relationship-role>
      <ejb-relationship-role-name>CustomerBean
       </ejb-relationship-role-name>
      <multiplicity>One</multiplicity>

      <relationship-role-source>
        <ejb-name>CustomerBean</ejb-name>
      </relationship-role-source>
      <cmr-field>
        <cmr-field-name>orders</cmr-field-name>
        <cmr-field-type>java.util.Collection
         </cmr-field-type>
      </cmr-field>
    </ejb-relationship-role>

    <ejb-relationship-role>
      <ejb-relationship-role-name>OrderBean
       </ejb-relationship-role-name>
      <multiplicity>Many</multiplicity>
      <cascade-delete />
      <relationship-role-source>
       <ejb-name>OrderBean</ejb-name>
      </relationship-role-source>
      <cmr-field>
       <cmr-field-name>customer</cmr-field-name>
      </cmr-field>
    </ejb-relationship-role>
  </ejb-relation>

Relationship Multiplicity

As you can see from Figure 7-1, entity bean relationships are not always one to one. The relationship between customer and order, for instance, is one to many, where “many” is zero or more. Conversely, the relationship between order and customer is many to one. We express relationship multiplicity as a specific one-way designation. That's because each relationship depends on your viewpoint.

Furthermore, multiplicity many can be either 0..* (meaning zero or more) or 1..* (meaning one or more). When multiplicity many allows zero, this implies that the collection of “many” objects may be empty. On the other hand, when multiplicity many specifies one or more, this implies that the collection of “many” objects must contain at least one element.

Multiplicity one implies always one, and multiplicity optional (0..1) means zero or one. Either of these can be used with the other (one to one, one to optional) or with many (one to many, optional to many). An example of a one-to-one relationship is a capital city for each state. Each state has exactly one capital city, and each capital city belongs to exactly one state. An example of an optional-to-optional relationship is a seat on an airline flight. Each seat may have at most one passenger, but it is also possible for the seat to be vacant (unassigned). The passenger may exist without a seat assignment (although we hope an assignment is eventually made).

Table 7.1 describes various entity bean relationships from entity bean A to entity bean B. We provide an example for each listed relationship. Note that for one or optional multiplicity (a singular multiplicity), the representing type is the local interface of the entity bean. For multiplicity many, the representing type is a collection of local interface entity bean objects. For each collection, you may choose either java.util.Collection (when duplicates are allowed) or java.util.Set (which disallows duplicate elements). We won't list all possibilities in the following table, but you can deduce other relationship multiplicities (i.e., optional to many where many is 1..*) from what we describe here. Also, we use a Collection for multiplicity many in our examples.

Table 7.1. Entity Bean Relationship Multiplicities
A to B Description Type of A Type of B
One to One capital city to state Each A is associated with one B, and each B is associated with one A. Local interface of A (CityLocal). Initialize at creation time. Local interface of B (StateLocal). Initialize at creation time.
One to Many (1..*) order to line item Each A is associated with one or more Bs. Each B must be associated with one A. Local interface of A (OrderLocal). Initialize at creation time. Collection of local interface to B (Collection of LineItemLocal). Collection must not be empty. Initialize at creation time.
One to Many (0..*) customer to order Each A is associated with zero or more Bs. Each B must be associated with one A. Local interface of A (CustomerLocal). Initialize at creation time. Collection of local interface to B (Collection of OrderLocal). Collection may be empty. Initialize when required by business rules.
Optional to Optional passenger to seat Each A may be associated with a B. Each B may be associated with an A. Local interface of A (PassengerLocal). Initialize when required by business rules. Local interface of B (SeatLocal). Initialize when required by business rules.
Optional to Many (0..*) library member to library book Each A is associated with zero or more Bs. B may or may not be associated with an A. Local interface of A (MemberLocal). Initialize when required by business rules. Collection of local interface to B (Collection of BookLocal). Collection may be empty. Initialize when required by business rules.
Many (0..*) to Many (0..*) students to courses Each A is associated with zero or more B. Each B is associated with zero or more A. Collection of local interface to A (Collection of StudentLocal). Collection may be empty. Initialize when required by business rules. Collection of local interface to B (Collection of CourseLocal). Collection may be empty. Initialize when required by business rules.

Choosing between the zero or more version of “many” and the one or more version of “many” depends on whether you want to allow an empty collection. For example, in Table 7.1 we've described the relationship between student and course to be many to many, where both “many” multiplicities are zero or more. Thus, a student may exist who has not signed up for any courses, and a course may exist with no enrolled students. This seems reasonable. Perhaps a student with no courses will eventually lose student status, and a course with no enrollment will eventually be cancelled. But, the business rules of the system will determine these scenarios. It's important for the bean developer to recognize that these associations are not necessarily established when the entity bean is created.

Relationship Navigability

Returning to Figure 7-1 on page 280, you'll note that double-headed arrows mark the relationship between entity beans. We can therefore navigate from Customer EJB to Order EJB and back. The Customer EJB and Order EJB CMR fields provide two-way navigation, or a bidirectional relationship. That is, if you have a Customer EJB, you can find the collection of Order EJBs associated with that customer. Similarly, given an Order EJB, you can determine the associated Customer EJB. The same two-way navigation exists between Order EJB and LineItem EJB. From the Order EJB you can determine the line items and from the LineItem EJB you can fetch the order.

Bean developers implement two-way navigation when it's important to find related records from a given entity bean. However, maintaining relationship fields requires extra work by the container. (Depending on implementation details, the container may create cross reference database tables using foreign keys that relate one entity's primary key to another entity's primary key. One to many relationships produce multiple entries in the cross reference table for the “one” end.)

As an alternative, bean developers may choose to implement one-way navigation, or a unidirectional relationship, for some of its relationship fields. One-way navigation means that you may determine a related entity bean in one direction only. For example, we may decide that to navigate from a LineItem EJB to its Order EJB is not necessary. Thus, while we can determine the line items that belong to an order, we can no longer navigate to the order from the line item.

Design Guideline

When you initialize or update a CMR field on one end of a bidirectional relationship, the container updates the other end within the same transaction. For example, when you create an Order EJB and initialize its customer CMR field, the container adds that Order EJB to the orders collection CMR field of the corresponding Customer EJB. Initializing or updating CMR fields results in corresponding database insert or update operations (within the current transaction).


Note that the relationship multiplicity is the same regardless of whether you implement one-way or two-way navigation. In other words, a one to many multiplicity still exists between Order EJBs and LineItem EJBs irrespective of providing a bidirectional or unidirectional relationship.

For our example, we implement a bidirectional relationship between both the Customer EJB and Order EJB, and the Order EJB and LineItem EJB.

Life Cycle Relationships

When does a relationship between two entity beans begin? When does it end? The answers depend on whether a singular multiplicity specifies optional (and thus allows zero) or one (requiring exactly one at all times). For multiplicity many, does the relationship allow zero elements in the collection, or must there be at least one? If there must always be at least one related entity bean in a relationship, then that relationship is initialized during the create process. If, on the other hand, multiplicity many allows zero elements in a collection or a singular relationship is optional, then the business rules determine when the relationship is initialized. For example, in our virtual shopping application, a user must sign in as a new or returning customer before placing orders. Thus, the application creates a customer entity bean before it can create an order for that customer. Therefore, we cannot initialize the relationship between customer and order at the time we create the Customer EJB.

However, when the application creates an Order EJB, the customer who placed the order is known. Since an order must have one customer associated with it, the application initializes the relationship to the “correct” Customer EJB when it creates the order.

The bean developer must therefore decide if a relationship exists throughout the life cycle of the entity bean. In the case where the relationship is required, singular multiplicity becomes one instead of optional and multiplicity many is one or more rather than zero or more. When business rules dictate that relationships begin and end with events other than creation and destruction, multiplicity reflects this with optional for singular multiplicity and zero or more for multiplicity many.

Cascading Deletes

What happens when we remove a customer from our system? That is, what happens when the application calls remove() for a Customer EJB? For all entity beans that have relationship fields with other entity beans, we must specify the actions in this scenario. Since our premise is that an order cannot exist without a customer, we must remove any related orders (there may be more than one) when the application removes a customer. Furthermore, since a line item cannot exist without an order, we must also remove all the related line items (there will be at least one) when removing an order. This sequence of cascading deletes occurs when the destruction of one bean dictates the destruction of related beans. For each relationship, bean developers specify if cascading deletes should apply.

Business logic may prevent the removal of a customer from the database if that customer has pending orders (and in turn prevent the destruction of possibly unfilled orders). If, for example, a controlling session facade has a business method removeCustomer(), the method should throw an application exception if that customer has orders pending, disallowing its removal.

On the other hand, it is perfectly acceptable to remove an order from the database (for example, if a customer cancels an order) and possibly leave a customer with no orders. We have already said the relationship between customer and order is one to many where many is zero or more. We, therefore, allow the removal of an order without removing the related customer. In fact, we cannot remove the related customer. The removal of the customer would cause all the customer's orders to be destroyed (through the cascading delete feature). For this reason, cascading deletes never apply to the opposite end of a “many” relationship.

Access Methods for Persistent Fields

Once we define an abstract persistent schema, we can design an entity bean so that the deployment tool can generate the correct deployment descriptor. Here's the approach.

For each CMP field, the bean developer provides abstract access methods inside the bean implementation class.

Return to Figure 7-1 on page 280. Each entity bean specifies its CMP fields. In the Customer EJB, for example, we have (among other persistent fields) a customerID and name, both Strings. Here are the corresponding abstract access methods for these CMP fields in the Customer EJB bean implementation class, CustomerBean.

public abstract class CustomerBean implements EntityBean {

  // Access methods for persistent fields
  public abstract String getCustomerID();
  public abstract void setCustomerID(String id);
  public abstract String getName();
  public abstract void setName(String name);
  . . .
}

Note that the bean developer provides getter and setter access methods for each CMP field. In fact, the bean developer must adhere to the following naming conventions and rules.

  • All access methods must be abstract.

  • Getter methods return the CMP's type (here, String).

  • Setter methods return void and take the CMP's type as its argument (here, String).

  • For getters, the method name get is followed by the CMP field name with its first letter in upper case.

  • For setters, the method name set is followed by the CMP field name with its first letter in upper case.

The container generates code to produce the Java persistent fields from these methods. For Customer EJB, for example, the container would generate these persistent fields.

String customerID;
String name;
. . .

Design Guideline

Note that CMP fields are virtual fields only and are accessed through the abstract getter and setter access methods. For example, the only way that a bean developer can read the Customer EJB CMP field name is through its getter method getName().


The deployment tool determines the field names and their data types from the abstract access methods for each persistent field.

Access Methods for Relationship Fields

Not all access methods are for persistent fields, however. Bean developers must provide abstract access methods for relationship (CMR) fields, too.

For each CMR field that requires navigability, the bean developer provides abstract access methods inside the bean implementation class. Relationships that specify many must provide arguments and returns types that are collections (either Collection or Set). Relationships that are singular must specify the local interface of the related entity bean for arguments and return types.

The naming conventions for CMR access methods are the same as CMP access methods. The argument and return types, however, must be the local interface of the related entity bean. For multiplicity many, argument and return types are either java.util.Collection or java.util.Set.

For example, the Customer EJB is an entity bean that represents a customer. By logging into the Music Collection virtual shopping web site, customers can choose recordings and submit orders. We represent each order with an Order EJB. In our system, customers may have zero or more orders. Thus, we write the following access methods to define this relationship.

public abstract class CustomerBean implements EntityBean {
  . . .
  // Access methods for relationship fields
  public abstract Collection getOrders();
  public abstract void setOrders(Collection orders);
  . . .
}

Since customers may have more than one order, we use type Collection. The container generates a Collection relationship field for the Customer EJB as follows.

Collection orders;

The orders collection contains zero or more OrderLocal (the Order EJB local interface) objects.

Now let's examine how the corresponding access method would appear in the Order EJB bean implementation class. Here, an order is associated with just one customer.

public abstract class OrderBean implements EntityBean {
  . . .
  // Access methods for relationship fields
  public abstract CustomerLocal getCustomer();
  public abstract void setCustomer(
             CustomerLocal customer);
  . . .
}

The container generates the following CMR field in the Order EJB.

CustomerLocal customer;

Design Guideline

Note that we define relationship fields for the classes on both ends of a bidirectional relationship (one to many for customer to orders). In the CustomerBean implementation class, we define access methods for CMR field orders. In the OrderBean implementation class, we define access methods for CMR field customer. To implement a unidirectional (one-way navigation) relationship, simply omit the access methods in the class that does not require navigation.


We can similarly specify the bidirectional one to many relationship between order and line items. In the OrderBean implementation class, we provide abstract access methods to CMR field lineItems, as follows.

public abstract class OrderBean implements EntityBean {
  . . .
  // Access methods for relationship fields
  public abstract Collection getLineItems();
  public abstract void setLineItems(Collection lineItems);
  . . .
}

Since this is the many end of the relationship, the argument and return type is a collection. In the LineItemBean implementation class, we provide the corresponding access methods to CMR field order. Since the multiplicity is singular, the argument and return type is the local interface (OrderLocal) of the related entity bean, Order EJB.

public abstract class LineItemBean implements EntityBean {
  . . .
  // Access methods for relationship fields
  public abstract OrderLocal getOrder();
  public abstract void setOrder(OrderLocal order);
  . . .
}

Custom Finder and Select Methods

Part of the EJB 2.0 specification defines an EJB Query Language. Similar to SQL, the EJB Query Language allows bean developers to implement bean finder methods and build customized select statements that are portable. Before we show you the EJB Query Language, let's discuss CMP entity bean finder and select methods.

Finder Methods

With CMP, the container implements all finder methods. The bean developer provides finder method definitions in the local home interface, but does not write the corresponding ejbFindXXX() methods in the bean implementation class. Finder methods specified in the local home interface return either a single object that implements the local interface or a collection of local interface objects.

All entity beans must provide finder method findByPrimaryKey() in the local home (or remote home) interface. In CMP, we provide access methods to the persistent field that represents the primary key. During the deployment process, the bean developer specifies which persistent field represents the primary key. This allows the container to generate the proper SQL select code to implement ejbFindByPrimaryKey(). What about the other finder methods?

Entity bean finder methods can be arbitrarily complex. A finder method, for instance, might find all customers with orders earlier than a specific date. Since CMP must implement all database access code (including SQL select statements for finder methods), a mechanism to specify arbitrarily complex finders becomes necessary. Let's look at an example.

Like the BMP version of Customer EJB, we need to include findByCustomerName() in the CMP version as well. Here's the definition of this custom finder method in the CustomerLocalHome interface.

public interface CustomerLocalHome extends EJBLocalHome {
  . . .
  // Custom finder methods
  public Collection findByCustomerName(String name)
    throws FinderException;
  . . .
}

This finder method returns a collection, since we cannot guarantee that only a single object will be found. With CMP, we specify an EJB QL statement so that the deployment tool can generate the correct SQL for the finder method. Whereas SQL may vary from database to database, all EJB 2.0-compliant application servers are required to interpret EJB QL. The container/deployment tool can then generate the SQL that is correct for its underlying database server.

You specify the EJB QL during the deployment process, usually with a tool provided by the application server. Then, the deployment tool builds XML <query> tags for each custom finder. Here is the XML <query> tag for findByCustomerName().

<query>
  <description></description>
  <query-method>
    <method-name>findByCustomerName</method-name>
    <method-params>
      <method-param>java.lang.String</method-param>
    </method-params>
  </query-method>
  <ejb-ql>select distinct object (c)
							from customerEJB c where c.name = ?1</ejb-ql>
</query>

Don't worry about the EJB QL query yet. We'll discuss it in detail in the next section. Note that with CMP, we must provide the EJB QL queries for custom finders, but this is still much less work than providing a complete BMP implementation.

To implement this finder, the deployment tool generates the appropriate SQL and builds additional XML tags at deployment time. The SQL code depends on specific application and database servers, and is not necessarily portable. Here is the corresponding SQL for findByCustomerName() on our machine using the Cloudscape database server and the Sun reference implementation application server. (We shade the display to indicate generated SQL.)

<sql-statement>
  <method>
    <ejb-name>CustomerBean</ejb-name>
    <method-intf>LocalHome</method-intf>
    <method-name>findByCustomerName</method-name>
    <method-params>
      <method-param>java.lang.String</method-param>
    </method-params>
  </method>
  <sql>SELECT DISTINCT "c"."customerID" FROM
							"CustomerBeanTable" "c" WHERE ("c"."name" = ? )</sql>
</sql-statement>

Select Methods

Although finder methods can be arbitrarily complex, they return only an object or a collection of objects implementing the local interface. It may also be necessary to perform database select queries that return other result sets (such as CMP fields or local interface objects of related entity beans). Bean developers can use select methods when return objects must be more general than the local interface return objects produced by finders. To provide select methods, the bean developer specifies a public abstract method with the prefix ejbSelect inside the bean implementation class. Exception FinderException must be included in the select method's throws clause. Like finders, select methods (as the name implies) may perform complex select queries on the database. Bean developers use EJB QL to specify the semantics of the query.

Think of select methods as private helper methods. This means clients do not call select methods directly. Consequently, the bean developer does not expose select methods in either the local or local home interface. You may use either a business method (specified in the local interface) or a home method (specified in the local home interface) to call ejbSelectXXX() methods.

Let's look at an example. Suppose that in CustomerLocalHome, we specify home method getTotalCustomers(), which returns the number of customers in a customer database.

public interface CustomerLocalHome extends EJBLocalHome {
  . . .
  // Home method
  public int getTotalCustomers() throws FinderException;
  . . .
}

To provide the underlying database select query that the implementation of this home method can call, we use an abstract select method called ejbSelectTotalCustomers() in the CustomerBean implementation class. Then, in the implementation of ejbHomeGetTotalCustomers(), we invoke method size() as shown to return the number of customers in the database.

public abstract class CustomerBean implements EntityBean {
  . . .
  // Select method
  public abstract Collection ejbSelectTotalCustomers()
     throws FinderException;

  // EJB Home Methods
  public int ejbHomeGetTotalCustomers()
           throws FinderException {
    // invoke method size() to get count
    return ejbSelectTotalCustomers().size();
  }
  . . .
}

Like finders, the deployment tool builds <query> tags for each select method. Here is the <query> tag for ejbSelectTotalCustomers().

<query>
  <description></description>
  <query-method>
    <method-name>ejbSelectTotalCustomers</method-name>
    <method-params />
  </query-method>
  <ejb-ql>select object (c)
							from customerEJB c</ejb-ql>
</query>

We'll discuss the EJB QL select statement shortly. And, like finders, the deployment tool generates the appropriate SQL and builds additional XML tags at deployment time. Here is the <sql-statement> tag for this select method (again, the generated SQL is not necessarily portable).

<sql-statement>
  <method>
  <ejb-name>CustomerBean</ejb-name>
  <method-intf>Bean</method-intf>
  <method-name>ejbSelectTotalCustomers</method-name>
  <method-params />
  </method>
  <sql>SELECT "c"."customerID" FROM
							"CustomerBeanTable" "c"</sql>
</sql-statement>

This particular select statement returns all customer records from the database in a collection.

Introducing the EJB Query Language

An entity bean is represented by its abstract persistence schema. The bean developer names an entity bean's abstract schema at deployment time. The schema name must be unique within the deployment descriptor. All related entity beans share the same deployment descriptor (and, therefore, the same JAR file).

Bean developers use the EJB Query Language (EJB QL) to define queries for custom finder and select methods with CMP entity beans. To access an entity bean, an EJB QL query references the entity bean's abstract schema. Some queries may need to access relationship fields to complete the query. Queries can navigate to abstract schemata of related entity beans through the bean's relationship fields.

Although much of EJB QL is a subset of SQL, EJB QL has syntax differences. EJB QL allows you to navigate related beans, whereas SQL joins only database tables. An EJB QL query consists of a Select clause, a From clause, and an optional Where clause. EJB QL keywords match equivalent SQL keywords and are not case sensitive. EJB QL identifiers are also case insensitive.

To see how EJB QL works, let's use the abstract schemata describing the Customer EJB, Order EJB, and LineItem EJB entity beans. Most of the examples we'll show you are hypothetical queries, but a few correspond to real methods in our code. You can refer to Figure 7-1 on page 280 as you work through the examples.

Select Clause

The Select clause defines object types returned by the query. The return type is an entity bean's local interface, remote interface, or persistent field. The return type can even be the local interface of a related entity bean.

The Select clause typically names an identification variable representing an object in the abstract schema. The syntax object (c), for example, names c as an identification variable. A Select clause can also name a path expression. In the Customer EJB schema, the path expression c.name is a persistent field (String). The path expression c.orders is a relationship field representing a collection of local interface objects (OrderLocal) of the related entity bean Order EJB.

Design Guideline

If a path expression represents a collection (because the multiplicity of a relationship is many), you cannot use it with a Select clause or subsequent expression. However, you can specify items from a collection using operator IN within a From clause.


From Clause

The From clause defines the source of the data. It also defines identification variables and path expressions used in the Select clause. All identification variables must be defined in the From clause using operators IN or AS, where AS is optional.

Our first example is the EJB QL statement we showed you earlier (see the deployment descriptor on page 294). This query implements the Customer EJB select method ejbSelectTotalCustomers().

select object (c) from customerEJB AS c

The following is equivalent since operator AS is optional.

select object (c) from customerEJB c

These queries use c as an identification variable. Both queries access the Customer EJB and return the entire collection of CustomerLocal objects.

Where Clause

The Where clause consists of conditional expressions that narrow the number of objects selected. Here is the EJB QL for custom finder findByCustomerName(String name) defined in the CustomerLocalHome interface (see the deployment descriptor on page 291).

select distinct object (c) from customerEJB c
where c.name = ?1

The distinct keyword eliminates duplicates. Identification variable c refers to the Customer EJB in our schema and c.name is a persistent field in this entity bean. The Where clause restricts the customers retrieved by checking their names. The ?1 corresponds to the string name passed to the findByCustomerName() method. This query returns a collection of CustomerLocal objects.

Navigating to Related Beans

In EJB QL, a path expression may navigate to related beans. This is typically used in a From clause with operator IN to specify a new identification variable. To see how this works, let's build a query from the Customer EJB schema that navigates to the Order EJB.

Here's a query that implements findByHigherAmount(double amt) in the CustomerLocalHome interface. Such a query could be used to find “big spenders.”

select distinct object (c) from customerEJB c, IN (c.orders)
AS o where o.totalAmount > ?1

This query returns all customers (a collection of CustomerLocal objects) with an order whose total amount is more than the amount passed to the finder method (?1). Identification variable c represents the Customer EJB abstract schema, and c.orders is a path expression that follows the IN operator. The relationship field (orders) is what names o as our related bean (Order EJB). In the Where clause, o.totalAmount is a persistent field of the Order EJB. The AS is optional in this query but adds clarity when used with the IN operator.

Here's a similar query that implements findByNameAndDate(String name, long date).

select distinct object (c) from customerEJB c, IN (c.orders)
AS o where c.name = ?1 and o.orderDate >= ?2

The Where clause uses keyword and to form a compound expression. This specifies the condition that name in the Customer EJB is equal to the first argument of findByNameAndDate(), and orderDate in the Order EJB is equal to or more recent than the second argument. As before, the query returns a collection of CustomerLocal objects.

Query Language Examples

Let's show you several more examples of EJB QL. Again, these queries implement finder and select methods that are hypothetical (don't look for them in our source code) and we describe them only to illustrate EJB QL.

Our first query example returns a collection of names (Strings) rather than a collection of CustomerLocal objects.

select distinct c.name from customerEJB c

Instead of a Select object, we use path expression c.name, which refers to persistent field name in our Customer EJB. This query returns a unique collection of customer names from the database. Such a query can implement select method ejbSelectNames(), for instance.

Design Guideline

A finder method must return its local interface object (or a collection thereof). That is, a finder in the CustomerLocalHome interface must return a CustomerLocal object (or a collection of CustomerLocal objects). If an EJB QL query returns something else (a related local interface object or a persistent data field), then you must use an ejbSelectXXX() method for its implementation.


Our next query implements findByHigherAmount(double amt) in the Order EJB home interface, OrderLocalHome.

select distinct object (o) from orderEJB o
where o.totalamount > ?1

In this query, o is an identification variable referring to Order EJB. The Where clause narrows the result set to objects whose persistent field totalAmount is greater than the argument of the finder method (?1). Note that the data retrieved from the query is a collection of OrderLocal objects.

In the next query, we start from the Order EJB and navigate to the Customer EJB. Here's a query that implements select method ejbSelectNameByHigherAmount(double amt).

select distinct c.name from orderEJB o, IN
(o.customer) AS c where o.totalamount > ?1

The path expression c.name refers to persistent field name in the Customer EJB. This is found by accessing the customer relationship field of the Order EJB (o.customer). The Where clause narrows the selection of orderEJB objects to those whose persistent field totalAmount is greater than the argument of ejbSelectNameByHigherAmount(). The data retrieved from this query is a collection of unique names (Strings). Note that this query cannot be used with a finder method.

The next query implements findByOrderDate(long date) in the CustomerLocalHome interface.

select distinct object (c) from customerEJB c,
IN (c.orders) AS o where o.orderDate > ?1

The path expression c.orders specifies relationship field orders in the Customer EJB, whereas o.orderDate specifies persistent field orderDate in the Order EJB. This query returns a collection of CustomerLocal objects.

Our last query example navigates to the LineItem EJB from the Customer EJB. This query implements findByTitleImagine() in the CustomerLocalHome interface.

select distinct object (c) from customerEJB c,
IN (c.orders) AS o, IN (o.lineitems) AS i
where i.title = 'Imagine'

The query selects customers who have orders that include a line item with the recording title 'Imagine.' The path expression c.orders accesses customer relationship field orders. The path expression o.lineitems accesses order relationship field lineitems. Since both expressions represent collections, operator IN is required, producing new identification variables o (for Order EJB) and i (for LineItem EJB). The path expression i.title accesses the persistent field title in the LineItem EJB. This query returns a collection of CustomerLocal objects.

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

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