7.2. The Three Entity Beans

We're now ready to examine the entity beans for LineItem EJB, Order EJB, and Customer EJB. Because we're using container-managed persistence, the implementation code is much shorter than comparable code with bean-managed persistence. The trade-off is that we'll spend more effort on the deployment descriptor since more information is now declarative.

As we present code for each entity bean, we'll also present the relevant declarative information. This information appears in the XML deployment descriptor stored in the JAR file with the entity beans. We package the three entity beans in the same JAR file along with the session facade bean, CustomerSession EJB, which we will show you later in this chapter.

The Big Picture

Let's begin with our familiar architectural component picture. Figure 7-2 shows the components of our enterprise application using CMP entity beans to provide customer and order persistence. The Session Facade stateless session bean, CustomerSession EJB, provides the interface to the entity beans for general clients. The CustomerSession EJB accesses the entity beans using local and local home interfaces. Because the entity beans have relationship fields, we show their interactions with arrows as well. CMP requires that relationships be implemented using local interfaces. Thus, we label the relationship arrows with local access.

Figure 7-2. Architectural Overview of the Music Shopping Cart Enterprise Application with Customer, Order, and LineItem Persistence


While access to the Music Collection Database is through the MusicDAO (as before), access to the Customer Orders Database is through container-managed persistence (CMP), designated by the thick arrow labeled CMP. Since the EJB container provides this service, we show the CMP access from the container to the database.

Note that we package the three entity beans in one JAR file since they must share the same deployment descriptor. CMR fields in one bean require a shared scope for abstract schema names, CMP field names, and CMR field names in other beans. We also place the CustomerSession EJB in the same JAR file, although this is not required.

Structure of CMP Entity Beans

CMP entity bean structure is not too different from BMP entity bean structure. Both allow client access through remote home and local home interfaces to create, finder, and home methods. Both allow access through remote and local interfaces to invoke business methods or CMP field access methods. However, the bean developer cannot expose CMR field access methods in the remote interface; these are restricted to local clients. Also, the bean developer does not expose the setter access method to the primary key field in the component (remote or local) interface of an entity bean. Once a primary key field is set, it should not change.

When you use CMP, the bean implementation class is always abstract. Thus, the container generates the concrete implementation class for you. This generated class will contain the code for the abstract CMP field methods, the abstract CMR methods, and the required database access methods the container builds from the declarative information in the deployment descriptor. This includes methods to maintain (and persist) the CMR and CMP fields as well as the implementation of finder and select methods. In short, the bean implementation class provided by the bean developer is short, because so much of its code is generated!

Line Item Entity Bean

Let's start with the simplest bean, LineItem EJB. In addition to the primary key field, a LineItem EJB holds a recording title and the number of recording titles (which is at least one). LineItem EJBs are always associated with an Order EJB. This association is maintained as a relationship field. If fact, you'll see that when we create a LineItem EJB, we associate it with an Order EJB during the create process. Figure 7-3 (a repetition of Figure 7-1 for your convenience) shows the three entity beans, their CMP fields, and their CMR fields (labeled on the arrows). The LineItem EJB database record consists of a lineItemID (the primary key), a recording title, and a quantity.

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


Figure 7-4 shows the classes and interfaces of the LineItem EJB with local interfaces. The local home interface holds the create() methods, finder methods, and home methods. The local interface contains the business methods, including any CMP or CMR field access methods the bean developer wants to expose to clients. The abstract bean implementation class (LineItemBean) implements the EntityBean interface and contains the code for the EJB methods, business methods, and the abstract access methods for CMP and CMR fields. Note that the ejbFindXXX() methods do not appear in LineItemBean because the container and deployment tool generate their implementations using the EJB QL statements. Unlike BMP, a container-generated class extends the bean implementation class (LineItemBean). Let's examine LineItem EJB's local home interface first.

Figure 7-4. Class Diagram Showing the EJB Classes for a CMP Entity Bean


Local Home Interface

Listing 7.2 contains the source for LineItemLocalHome.java, the local home interface. Here we have the required findByPrimaryKey() method, which returns a LineItemLocal object. We also have a create() method, which requires a title and quantity as arguments. Note that create() also specifies an OrderLocal object in its argument list. When we show you the bean implementation code, you'll see that this argument allows the LineItem EJB to initialize its relationship field with the Order EJB during the create process.

Finally, we specify custom finder method findByTitle(), which returns a collection. Since the LineItem database table will have LineItem EJB records from all the orders, the title field may not be unique. The LineItem EJB does not have any home methods.

Note that with CMP, the bean developer does not implement any of the finder methods. Instead, we use the EJB Query Language during deployment to specify how to generate the appropriate SQL code. We'll show you both the EJB Query Language and the resultant SQL for the finder methods when we examine the deployment descriptor.

Listing 7.2. LineItemLocalHome.java
// LineItemLocalHome.java
import javax.ejb.*;
import java.util.*;

public interface LineItemLocalHome extends EJBLocalHome {

  // Method create() includes OrderLocal to
  // initialize relationship field order
  public LineItemLocal create(String title,
      int quantity, OrderLocal order)
      throws CreateException;

  public LineItemLocal findByPrimaryKey(String id)
        throws FinderException;

  // custom finder method
  public Collection findByTitle(String title)
        throws FinderException;
}

Local Interface

Listing 7.3 contains the source for file LineItemLocal.java, the local interface for the LineItem EJB. Here we have the business methods. These typically include data access methods to one or more of the CMP fields and any CMR fields. In the LineItem EJB, we include getters for persistent fields lineItemID, title, and quantity. The only setter method is for persistent field quantity.

Design Guideline

The local interface defines setters and getters for persistent data fields and relationship fields that are exposed to external clients only. Thus, we don't provide “setters” for lineItemID and title, since these fields do not change once they are set in home interface method create(). We do provide setters for persistent field quantity, since we need to update this data field from the CustomerSession EJB client. Finally, we don't provide setOrder(). We take care of initializing this relationship field during the create process (see ejbPostCreate() on page 306.)


We also provide method getOrder() for the singular relationship field order, which returns OrderLocal (Order EJB's local interface object). These setter and getter methods are defined as abstract in the bean implementation code since the container is responsible for generating their code.

Listing 7.3. LineItemLocal.java
// LineItemLocal.java
import javax.ejb.*;

public interface LineItemLocal extends EJBLocalObject {

  // access methods for persistent fields
  public String getLineItemID();
  public String getTitle();
  public int getQuantity();
  public void setQuantity(int quantity);

  // access methods for relationship fields
  public OrderLocal getOrder();
}

Primary Key Generation

Every entity bean has a unique primary key, at least within the database table in which the field is constrained to be a primary key. Therefore, we must either pass the primary key as an argument to ejbCreate(), or generate one within ejbCreate(). In this CMP entity bean example (like our BMP entity bean in the previous chapter), we choose to generate the primary key inside ejbCreate().

Recall that in the BMP example, we include a primary key generation method dbGetKey() in the DAO class (see page 222). Since the Data Access Object pattern is not necessary with CMP, we create a small Java class DBUtil to invoke the Cloudscape KeyGen.getUniversalKeyStringValue() method. Each of our three entity beans invokes this method to obtain a unique primary key. Listing 7.4 contains the source for DBUtil.java. Note that we make method dbGetKey() static so we can invoke it with a class name rather than an object.

Listing 7.4. DBUtil.java
// DBUtil.java
import COM.cloudscape.util.KeyGen;
public class DBUtil {

  // Cloudscape propriety routine to generate primary key
  public static String dbGetKey() {
    return KeyGen.getUniversalKeyStringValue();
  }
}

Design Guideline

Since all three entity beans require primary key generation, we move the call to getUniversalKeyStringValue() out of the bean implementation class and encapsulate it within a utility class. This isolates the proprietary routine. Installing an alternate scheme for primary key generation would require re-implementing method dbGetKey(). See page 220 in the previous chapter for additional issues regarding primary key generation.


Bean Implementation

Listing 7.5 contains the source for the LineItem EJB bean implementation code in file LineItemBean.java. You'll notice quite a number of differences between this (relatively compact) CMP code and the code for a BMP entity bean.

Listing 7.5. LineItemBean.java
// LineItemBean.java
import java.util.*;
import javax.ejb.*;
import javax.naming.*;

public abstract class LineItemBean implements EntityBean {

  // EntityBean variables
  private EntityContext context;

  // Access methods for persistent fields

  public abstract String getLineItemID();
  public abstract void setLineItemID(String id);

  public abstract String getTitle();
  public abstract void setTitle(String title);

  public abstract int getQuantity();
  public abstract void setQuantity(int quantity);

  // access methods for relationship fields

  public abstract OrderLocal getOrder();
  public abstract void setOrder(OrderLocal order);

  // EntityBean methods

  public String ejbCreate(String title, int quantity,
      OrderLocal order) throws CreateException {
    String newKey =
        DBUtil.dbGetKey();
    setLineItemID(newKey);
    setTitle(title);
    setQuantity(quantity);
    return newKey;
  }

  public void setEntityContext(EntityContext context) {
    this.context = context;
  }
  public void ejbPostCreate(String title, int quantity,
         OrderLocal order) {
    // associate this LineItem EJB with Order EJB
    setOrder(order);
  }

  public void unsetEntityContext() {
    context = null;
  }
  public void ejbActivate() { }
  public void ejbPassivate() { }

  public void ejbLoad() { }
  public void ejbStore() { }

  public void ejbRemove() { }
} // LineItemBean

Note that class LineItemBean is abstract and contains abstract methods for the CMP and CMR fields. The container provides an implementation for these abstract methods when it extends LineItemBean to create a new class.

An entity bean must implement an ejbCreate() method for each create() method in the local home interface. Since we have just one create() method in the local home interface, there is only one ejbCreate() in LineItemBean. Method ejbCreate() initializes the persistent data fields. Because these fields are virtual, ejbCreate() calls the setter accessor methods to initialize them. This is where we invoke class DBUtil's static method getKey() to obtain a primary key. As with BMP entity beans, CMP versions of ejbCreate() return the primary key to the container.

One of the parameters for ejbCreate() is OrderLocal order. Although it is not possible to set a CMR field in ejbCreate(), you can set it in ejbPostCreate(). Recall that these two methods have identical parameters and the container calls ejbPostCreate() after ejbCreate(). Inside ejbPostCreate(), we initialize the CMR field from the LineItem EJB to Order EJB by invoking setOrder() with the order argument. The container initializes the other end of the relationship within the same transaction. Thus, after we invoke setOrder() in ejbPostCreate(), the container adds this newly created LineItem to Order EJB's CMR field lineItems, invoking the collection's add() method.

Design Guideline

You cannot set a relationship field within ejbCreate() because the entity instance is not yet fully initialized. However, to set a relationship field during the create process, you may invoke CMR accessor methods in ejbPostCreate(). Remember that with two-way navigation, the container sets the other end of the relationship for you.


The ejbLoad(), ejbStore(), and ejbRemove() methods are all empty. No code is required unless the entity bean requires specific processing right after the load, before the store, or right before the delete. The container loads the bean's CMP fields from the database and then invokes ejbLoad(). Conversely, the container invokes ejbStore() before storing the CMP fields to the database. The container calls ejbRemove() before it deletes the associated database row.

Generated SQL for Container Methods

Now it's time to look at how the deployment process and the container maintain the relationship fields and persistence of our LineItem EJB. The SQL code we show here is specific to the application server, EJB container, and database server that we use (the Sun Microsystems reference implementation). Therefore, other application servers may generate different SQL. However, it helps to understand how the application server is doing its job.

CMP Implementation

Note that the generated SQL code uses database table name "LineItemBeanTable" to store LineItem EJB's persistent data. This name is generated by the Sun Microsystems Reference Implementation deployment tool. Currently, the only way to change the table name is to edit the generated XML deployment descriptor tags. Unfortunately, you must make this modification each time the deployment tool regenerates SQL.


Let's examine the SQL generated for the container methods. The deployment tool generates SQL when the bean developer specifies the persistent data fields and indicates which field represents the primary key. Here is a selection of the generated code for the LineItem EJB. We show all generated SQL with a shaded background. (The -- before a text line is an SQL comment.)

-- create row SQL Query
INSERT INTO "LineItemBeanTable" ("lineItemID", "quantity",
"title") VALUES ( ?, ?, ? )
-- delete row SQL Query
DELETE FROM "LineItemBeanTable" WHERE "lineItemID" = ?

-- findByPrimaryKey
SELECT "lineItemID" FROM "LineItemBeanTable" WHERE
"lineItemID" = ?

-- load row
SELECT "quantity", "title" FROM "LineItemBeanTable" WHERE
"lineItemID" = ?

-- store row
UPDATE "LineItemBeanTable" SET "quantity" = ?, "title" = ?
WHERE "lineItemID" = ?

EJB Query for Custom Finders

The LineItem EJB has one custom finder method, findByTitle(). In order for the container to generate code for this finder method, we must specify the proper select method for the custom finder. Note that this specification is intended to be portable for all EJB-compliant application servers. The EJB Query Language text appears in the deployment descriptor. We also show the generated SQL code (which is dependent on the application server and is not intended to be portable). Here is the EJB QL query for findByTitle(String title) followed by the generated (shaded) SQL code.

select distinct object (i) from lineitemEJB i
where i.title = ?1

-- Generated SQL Query for findByTitle()
SELECT DISTINCT "i"."lineItemID" FROM "LineItemBeanTable" "i"
WHERE ("i"."title" = ?)

The EJB QL query specifies object i (entity bean object LineItem EJB) for the select data return type. The corresponding SQL query specifies the primary key (lineItemID) as its data return type. Why? Recall that although finder methods return entity bean objects (such as LineItemLocal), the corresponding ejbFindXXX() method returns primary keys. It's the responsibility of the container to return the entity bean instance to the client.

Container-Managed Persistence

The deployment descriptor contains information about the persistent data fields the container must maintain. Listing 7.6 contains portions of the deployment descriptor for LineItem EJB that describes its persistent fields, as well as its local and local home interfaces, its bean implementation class, the persistent mechanism (Container, 2.x), and its abstract schema. The abstract schema name (lineitemEJB) is the identifier we use with the EJB Query Language to specify the LineItem database table.

The LineItem EJB persistent fields include lineItemID, quantity, and title. Field lineItemID is identified as the primary key.

Listing 7.6. Generated XML Tags for LineItem CMP
<entity>
     <display-name>LineItemBean</display-name>
     <ejb-name>LineItemBean</ejb-name>
     <local-home>LineItemLocalHome</local-home>
     <local>LineItemLocal</local>
     <ejb-class>LineItemBean</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>lineitemEJB
      </abstract-schema-name>

     <cmp-field>
       <description>no description</description>
       <field-name>lineItemID</field-name>
     </cmp-field>

     <cmp-field>
       <description>no description</description>
       <field-name>quantity</field-name>
     </cmp-field>

     <cmp-field>
       <description>no description</description>
       <field-name>title</field-name>
     </cmp-field>

     <primkey-field>lineItemID</primkey-field>
     <security-identity>
       <description></description>
       <use-caller-identity></use-caller-identity>
     </security-identity>
   </entity>

Container-Managed Relationships

The deployment descriptor also contains information about the relationships that the bean developer provides during the deployment process. Here we show you the CMR fields that apply to our LineItem EJB.

Listing 7.7 contains portions of the deployment descriptor that describe the CMR fields and multiplicity for LineItem EJB and its relationship with Order EJB. The deployment descriptor defines a relationship between OrderBean (one) and LineItemBean (many). OrderBean contains CMR field lineItems (a collection) and LineItemBean contains CMR field order (an OrderLocal type). The <cascade-delete/> tag means that LineItem EJBs are deleted when the related OrderBean EJB is removed. Note that this is a bidirectional relationship because the deployment descriptor describes both ends. Also, from the container's point of view, there is no difference between zero or more and one or more in a many multiplicity. The difference shows up in the business rules implemented by the bean developer.

Listing 7.7. Generated XML Tags for LineItem-Order CMR
<ejb-relation>
     <ejb-relation-name></ejb-relation-name>
     <ejb-relationship-role>
       <ejb-relationship-role-name>OrderBean
        </ejb-relationship-role-name>
       <multiplicity>One</multiplicity>

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

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

CMP Implementation

The Sun Microsystems Reference Implementation deployment tool generates SQL to create a cross-reference database table to persist the relationship data. Here is the generated SQL to create the table that persists the relationship between the Order EJB and LineItem EJB ("CustomerBean_orders_ OrderBean_customerTable"). Remember, this is application server dependent!


         <sql-statement>
          <operation>createTable</operation>
          <sql>CREATE TABLE
"CustomerBean_orders_OrderBean_customerTable"
("_CustomerBean_customerID" VARCHAR(255) ,
"_OrderBean_orderID" VARCHAR(255), CONSTRAINT
 "pk_CustomerBean_orders_OrderBean_customerTabl" PRIMARY KEY
("_OrderBean_orderID") )</sql>
        </sql-statement>

Order Entity Bean

The Order EJB contains bookkeeping information about a customer's order. It holds status information, the order date, ship date, dollar amount, and of course a primary key. The Order EJB also has CMR fields with both the Customer EJB and the LineItem EJB. Recall that the multiplicity between Customer EJB and Order EJB is one to many (where many is zero or more) and the multiplicity between Order EJB and LineItem EJB is one to many (where many is one or more). Therefore, given an Order EJB, we can determine the related Customer EJB. We can also access its collection of related LineItem EJBs. Let's look at the local home interface first.

Local Home Interface

Listing 7.8 contains the source for OrderLocalHome.java, the local home interface for Order EJB. The three static final integers represent status codes for the integer orderStatus persistent data field. By placing these definitions inside the local home interface, we make them more readily accessible. We only need an object that implements the local home interface to access them.

Note that the parameters for create() include the Order EJB CMP fields, as well as the CMR field for Customer EJB. Similar to the LineItem EJB, we set Order EJB's relationship field to Customer EJB during the create process (in ejbPostCreate()).

Design Guideline

We set Order EJB's CMR field to Customer EJB during the Order EJB create process because the related Customer EJB already exists. The container will update the associated Customer EJB CMR field for us.


The local home interface also includes the required findByPrimaryKey() and custom finder findByOrderDatePrevious(). This finder method returns a collection of OrderLocal objects whose order date is the same or earlier than the provided argument and whose order status matches the status provided. Because this is a custom finder, we must specify EJB QL statements in the deployment descriptor. This allows the container to generate the correct SQL code for its implementation. There are no home methods for Order EJB.

Listing 7.8. OrderLocalHome.java
// OrderLocalHome.java
import java.util.*;
import javax.ejb.*;

public interface OrderLocalHome extends EJBLocalHome {

  public static final int InProcess = 1;
  public static final int Shipped = 2;
  public static final int Closed = 3;

  public OrderLocal create(double totalAmount,
      int orderStatus, long orderDate, long shipDate,
      CustomerLocal customer) throws CreateException;

  public OrderLocal findByPrimaryKey(String id)
        throws FinderException;

public Collection findByOrderDatePrevious(
  long orderDate, int orderStatus)
  throws FinderException;
}

Local Interface

Listing 7.9 contains the source for the local interface in OrderLocal.java. The local interface includes getter methods for all the Order EJB CMP fields. There is also one setter method (setOrderStatus()) for CMP field orderStatus, since we need to update this persistent field from the CustomerSession EJB client. We also expose CMR getter methods getCustomer() and getLineItems(). Finally, the local interface includes business method shipOrder().

Listing 7.9. OrderLocal.java
// OrderLocal.java
import java.util.*;
import javax.ejb.*;

public interface OrderLocal extends EJBLocalObject {
  // Access methods for persistent data fields
  public String getOrderID();
  public double getTotalAmount();
  public int getOrderStatus();
  public long getOrderDate();
  public long getShipDate();
  public void setOrderStatus(int status);

  // Access method for relationship fields
  public CustomerLocal getCustomer();
  public Collection getLineItems();
  // Business methods
  public void shipOrder(long shipDate);
}

Bean Implementation

Now that you've seen the bean implementation class for LineItem EJB, the Order EJB should look similar. Listing 7.10 shows the source for OrderBean.java. Class OrderBean is abstract, as are the access methods for the CMP and CMR fields. Note that Order EJB has virtual relationship fields for both Customer EJB (customer) and the collection of LineItem EJBs (lineItems).

Business method shipOrder() sets the shipDate and orderStatus persistent fields. Again, we cannot access these persistent fields directly; we must use their access methods. Also, method shipOrder(), like all business methods, executes within a transaction. This means all CMP and CMR fields will be updated (in the entity bean instance and in the database) within a single transaction, maintaining the required properties of atomicity, consistency, isolation, and durability (ACID).

Design Guideline

Although Order EJB's shipOrder() is a business method, its only client is the Session Facade CustomerSession EJB. Thus our design funnels all entity bean clients through the Session Facade as depicted in Figure 7-2 on page 300. See “Session Facade Pattern” on page 329.


Method ejbCreate() initializes the persistent data fields using the class setter methods. We get a primary key from DBUtil static method dbGetKey(). After calling ejbCreate(), the container invokes ejbPostCreate(), where we set the customer CMR field (and the container takes care of the “other” end of the bidirectional relationship). Also, the container will initialize the lineItems CMR field during the LineItem EJB create process.

The remaining EJB methods are empty (but required), except setEntityContext() and unsetEntityContext(), which access the entity bean's context. Remember, the container is responsible for generating code for the finder methods, so you won't find any implementation code here.

Listing 7.10. OrderBean.java
// OrderBean.java
import java.util.*;
import javax.ejb.*;

public abstract class OrderBean implements EntityBean {

  // EntityBean variables
  private EntityContext context;

  // Access methods for persistent fields

  public abstract String getOrderID();
  public abstract void setOrderID(String id);

  public abstract double getTotalAmount();
  public abstract void setTotalAmount(double amount);

  public abstract int getOrderStatus();
  public abstract void setOrderStatus(int status);

  public abstract long getOrderDate();
  public abstract void setOrderDate(long date);
  public abstract long getShipDate();
  public abstract void setShipDate(long date);

  // Access methods for relationship fields

  public abstract CustomerLocal getCustomer();
  public abstract void setCustomer(
               CustomerLocal customer);

  public abstract Collection getLineItems();
  public abstract void setLineItems(Collection lineItems);

  // Business methods
  public void shipOrder(long shipDate) {
    setShipDate(shipDate);
    setOrderStatus(OrderLocalHome.Shipped);
  }

  // EntityBean methods

  public String ejbCreate(double totalAmount,
    int orderStatus, long orderDate, long shipDate,
    CustomerLocal customer)
    throws CreateException {

    String newKey = DBUtil.dbGetKey();
    setOrderID(newKey);
    setTotalAmount(totalAmount);
    setOrderStatus(orderStatus);
    setOrderDate(orderDate);
    setShipDate(shipDate);
    return newKey;
  }

  public void ejbPostCreate(double totalAmount,
         int orderStatus, long orderDate, long shipDate,
         CustomerLocal customer) {
    setCustomer(customer);
  }
  public void setEntityContext(EntityContext context) {
    this.context = context;
  }

  public void unsetEntityContext() {
    context = null;
  }

  public void ejbActivate() { }
  public void ejbPassivate() { }
  public void ejbLoad() { }
  public void ejbStore() { }
  public void ejbRemove() { }

} // OrderBean

Generated SQL for Container Methods

Let's now examine the generated code for the Order EJB container methods. Again, remember that this SQL code is dependent on your application server and its underlying database server. So, while this code may not necessarily be portable, it helps you understand what the container is doing behind the scenes. Thus, when you invoke a business method, the container performs a load row and store row. When you create an entity bean, the container inserts a database row. And, when you delete an entity bean, it deletes the corresponding database row.

-- create row
INSERT INTO "OrderBeanTable" ("orderDate",
"orderID", "orderStatus", "shipDate", "totalAmount")
VALUES ( ?, ?, ?, ?, ? )

-- delete row
DELETE FROM "OrderBeanTable"
WHERE "orderID" = ?

-- findByPrimaryKey()
SELECT "orderID" FROM "OrderBeanTable"
WHERE "orderID" = ?
-- load row
SELECT "orderDate", "orderStatus", "shipDate",
"totalAmount" FROM "OrderBeanTable"
WHERE "orderID" = ?

-- store row
UPDATE "OrderBeanTable" SET "orderDate" = ?,
"orderStatus" = ?, "shipDate" = ?, "totalAmount" = ?
WHERE "orderID" = ?

EJB Query for Custom Finders

The Order EJB has one custom finder method, findByOrderDatePrevious(). In order for the container to generate code for this finder method, we specify the proper select statement for the custom finder.

Here is the EJB QL text for findByOrderDatePrevious(long orderDate, int orderStatus) followed by the generated SQL code.

select distinct object (o) from orderEJB o where
o.orderDate <= ?1 and o.orderStatus = ?2

-- Generated SQL Query for findByOrderDatePrevious()
SELECT DISTINCT "o"."orderID" FROM "OrderBeanTable" "o" WHERE
(("o"."orderDate" <= ?) AND ("o"."orderStatus"= ?))

In simpler words, this says “select distinct Order objects from abstract schema orderEJB where the order date is less than or equal to the first parameter, and the orderStatus equals the second parameter.”

Container-Managed Persistence

Listing 7.11 contains deployment descriptor information for Order EJB and its CMP fields. It identifies the local and local home interfaces, as well as its bean implementation class (OrderBean). It also identifies the persistent mechanism as container managed, its abstract schema as orderEJB, and its primary field as orderID.

Listing 7.11. Generated XML Tags for Order CMP
<entity>
     <display-name>OrderBean</display-name>
     <ejb-name>OrderBean</ejb-name>
     <local-home>OrderLocalHome</local-home>
     <local>OrderLocal</local>
     <ejb-class>OrderBean</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>orderEJB</abstract-schema-name>

     <cmp-field>
       <description>no description</description>
       <field-name>orderDate</field-name>
     </cmp-field>
     <cmp-field>
       <description>no description</description>
       <field-name>shipDate</field-name>
     </cmp-field>

     <cmp-field>
       <description>no description</description>
       <field-name>orderStatus</field-name>
     </cmp-field>
     <cmp-field>
       <description>no description</description>
       <field-name>totalAmount</field-name>
     </cmp-field>

     <cmp-field>
       <description>no description</description>
       <field-name>orderID</field-name>
     </cmp-field>
     <primkey-field>orderID</primkey-field>
     <security-identity>
       <description></description>
       <use-caller-identity></use-caller-identity>
     </security-identity>
   </entity>

Container-Managed Relationships

Order EJB has CMR fields with both LineItem EJB and Customer EJB. Listing 7.7 on page 311 shows the deployment information for the relationship between Order EJB and LineItem EJB, so we won't repeat it here. Listing 7.12 contains deployment descriptor information that describes the CMR fields for the Order EJB (customer) and Customer EJB (collection orders) relationship.

Listing 7.12. Generated XML Tags for Order-Customer CMR
<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>
 </relationships>
</ejb-jar>

Customer Entity Bean

Our third and final entity bean is Customer EJB. We hope you'll find this entity bean's implementation interesting, since we already presented it using bean-managed persistence in the previous chapter. With the CMP implementation, we now can compare code. This means the bean implementation code will differ dramatically. The differences in the local and local home interfaces, however, are slight. These differences are not due to the persistent mechanism that we use; rather we've added a CMR field (with Order EJB). The local interface has methods to access and update this relationship field.

The local home interface has one small difference. In the BMP version, home method getTotalCustomers() does not have FinderException in its throws clause. In the CMP version we have to add FinderException in the throws clause because the implementation invokes a CMP select method. Select methods must always specify FinderException in their throws clauses. In general, however, choosing either CMP or BMP does not affect the local or local home interfaces.

Local Home Interface

Listing 7.13 contains the local home interface for Customer EJB. It is essentially unchanged from CustomerLocalHome.java in the previous chapter (see Listing 6.2 on page 209).

Because there are two custom finder methods (findByCustomerName() and findAll()) we'll need to provide declarative EJB QL statements so that the container can generate the correct SQL code for their implementations. These statements appear in the deployment descriptor, which we'll show you shortly.

Listing 7.13. CustomerLocalHome.java
// CustomerLocalHome.java
import java.util.Collection;
import javax.ejb.*;

public interface CustomerLocalHome extends EJBLocalHome {

  // Create method
  public CustomerLocal create(String name,
    String password, String email)
    throws CreateException;
  // Finder methods
  public CustomerLocal findByPrimaryKey(String customerID)
    throws FinderException;

  // Custom finder methods
  public Collection findByCustomerName(String name)
    throws FinderException;

  // Return all Customer EJBs in the database
  public Collection findAll()
    throws FinderException;

  // Home methods
  // Return the number of Customers in the database
  public int getTotalCustomers()
    throws FinderException;
}

Local Interface

Listing 7.14 contains the local interface for Customer EJB. It is identical to Listing 6.4 on page 211, except we've included the access method for the relationship field, getOrders(). We've also added business methods addOrder() and dropOrder() to maintain the relationship field with Order EJB.

Design Guideline

Although our CustomerSession EJB client doesn't invoke addOrder() or dropOrder() at this time, we include them as business methods anyway. This makes managing orders of Customer EJBs easy to implement later on.


Listing 7.14. CustomerLocal.java
// CustomerLocal.java
import javax.ejb.EJBLocalObject;
import java.util.*;
public interface CustomerLocal extends EJBLocalObject {
  // access methods for persistent fields
  public String getCustomerID();
  public String getName();
  public String getPassword();
  public void setPassword(String newPassword);

  public String getEmail();
  public void setEmail(String newEmail);
  public boolean getOrdersPending();
  public void setOrdersPending(boolean pending);

  // access methods for relationship fields
  public Collection getOrders();

  // business methods
  public void addOrder(OrderLocal order);
  public void dropOrder(OrderLocal order);
}

Bean Implementation

Listing 7.15 contains the bean implementation code for Customer EJB. Since you've already seen CMP implementations for Order EJB and LineItem EJB, most of it should be self-explanatory. Remember there is no implementation code for the finder methods, as these will be generated by the container. The custom finder methods will be generated from the query code we provide in the deployment descriptor. Likewise, select method ejbSelectTotalCustomers() is abstract, since its code will also be generated from our query statements in the deployment descriptor.

Business methods addOrder() and dropOrder() update the orders CMR field that holds the collection of Order EJBs. We use the getOrders() access method since we cannot manipulate the (virtual) CMR field directly.

Note that the implementation for ejbPostCreate() is empty. That's because at the time we create a Customer EJB, there are no orders in existence for this customer. Therefore, we don't set the relationship field here in the bean implementation code. (It's set by the container when we initialize the “other” end of the relationship in Order EJB's ejbPostCreate() method.)

Listing 7.15. CustomerBean.java
// CustomerBean.java
import java.util.*;
import javax.ejb.*;

public abstract class CustomerBean implements EntityBean {

  // EntityBean variables
  private EntityContext context;

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

  public abstract String getPassword();
  public abstract void setPassword(String password);

  public abstract String getEmail();
  public abstract void setEmail(String email);

  public abstract boolean getOrdersPending();
  public abstract void setOrdersPending(boolean pending);

  // Access methods for relationship fields
  public abstract Collection getOrders();
  public abstract void setOrders(Collection orders);

  // Select methods
  public abstract Collection ejbSelectTotalCustomers()
    throws FinderException;

  // Business methods
  public void addOrder(OrderLocal order) {
    try {
      Collection orders = getOrders();
      orders.add(order);
    } catch (Exception ex) {
      throw new EJBException(ex.getMessage());
    }
  }
  public void dropOrder(OrderLocal order) {
    try {
      Collection orders = getOrders();
      orders.remove(order);
    } catch (Exception ex) {
      throw new EJBException(ex.getMessage());
    }
  }

  // EJB Home Methods
  public int ejbHomeGetTotalCustomers()
           throws FinderException {
    return ejbSelectTotalCustomers().size();
  }

  // EntityBean methods

  public String ejbCreate(String customerName,
    String password, String email)
           throws CreateException {

    String newKey = DBUtil.dbGetKey();
    setCustomerID(newKey);
    setName(customerName);
    setPassword(password);
    setEmail(email);
    setOrdersPending(false);
    return newKey;
  }

  public void setEntityContext(EntityContext context) {
    this.context = context;
  }

  public void unsetEntityContext() {
    context = null;
  }

  public void ejbActivate() { }
  public void ejbPassivate() { }
  public void ejbLoad() { }
  public void ejbStore() { }
  public void ejbPostCreate(String customerName,
    String password, String email) { }

  public void ejbRemove() { }

} // CustomerBean

Note that the ejbHomeGetTotalCustomers() method implements the home method getTotalCustomers() from the CustomerLocalHome interface. To return the number of customers in the database, we invoke ejbSelectTotalCustomers() with the size() method, since this select method returns a collection.

public int ejbHomeGetTotalCustomers()
           throws FinderException {
  return ejbSelectTotalCustomers().size();
}

CMP Limitation

The implementation of ejbHomeGetTotalCustomers() is admittedly an inefficient way to count the number of records in the database. However, we are limited by the EJB Query Language in composing our queries. We are also limited in how we can access the result set. Recall that in our BMP example, we implement ejbHomeGetTotalCustomers() with a scrollable result set. This is more efficient, since we can position the result set cursor at the last row before getting the row number. With CMP, we cannot access the result set directly.


Generated SQL for Container Methods

After we specify the Customer EJB persistent data fields and the primary key, the deployment tool generates the SQL code to maintain database persistence. Here is the SQL used to perform CMP for our Customer EJB. This code is not meant to be portable; another application server might generate slightly different SQL, depending on the bundled database server.

-- create row
INSERT INTO "CustomerBeanTable" ("customerID",
"email", "name", "ordersPending", "password")
VALUES ( ?, ?, ?, ?, ? )

-- delete row
DELETE FROM "CustomerBeanTable" WHERE
"customerID" = ?

-- findByPrimaryKey
SELECT "customerID" FROM "CustomerBeanTable"
WHERE "customerID" = ?

-- load row
SELECT "email", "name", "ordersPending",
"password" FROM "CustomerBeanTable" WHERE
"customerID" = ?

-- store row
UPDATE "CustomerBeanTable" SET "email" = ?,
"name" = ?, "ordersPending" = ?, "password" = ?
WHERE "customerID" = ?

EJB Query for Finder and Select Methods

All custom finder methods and select methods require that we specify database lookup code through the EJB Query Language. For Customer EJB, query statements for ejbSelectTotalCustomers(), findAll(), and findByCustomerName() are necessary. We also show SQL code generated by the deployment tool. The deployment tool puts the query statements in the deployment descriptor. Thus, the EJB QL query statements are portable with any EJB-compliant application server. Here is the EJB QL for ejbSelectTotalCustomers(), followed by its corresponding generated SQL (shaded).

select object (c) from customerEJB c

-- Generated SQL Query for ejbSelectTotalCustomers()
SELECT "c"."customerID" FROM
"CustomerBeanTable" "c"

Next is the EJB QL for findAll() and its corresponding generated SQL.

select object (c) from customerEJB c

-- Generated SQL Query for findAll()
SELECT "c"."customerID" FROM
"CustomerBeanTable" "c"

Finally, we show you the EJB QL for findByCustomerName(String name) and its generated SQL.

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

-- SQL Query for findByCustomerName()
SELECT DISTINCT "c"."customerID" FROM
"CustomerBeanTable" "c" WHERE ("c"."name" = ?)

Container-Managed Persistence

Listing 7.16 contains deployment descriptor information for Customer EJB, including its interface names, abstract schema, persistence type, CMP field names, and primary key.

Listing 7.16. Generated XML Tags for Customer CMP
<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>

Container-Managed Relationships

Customer EJB's CMR field is a collection of Order EJBs (Collection orders). You've already seen the deployment descriptor information for this relationship (Listing 7.12 on page 320), so we won't repeat it here.

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

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