Implementing the CMP Entity Bean

This section discusses the implementation of the remote home interface OrderHome, remote interface Order, and the CMP bean class OrderEJB. We'll also discuss how to write the deployment descriptors in detail.

Defining the Home Interface

The home interface provides methods for creating and removing enterprise beans. In addition, for entity beans, the home interface also contains methods to find the instances of bean based on certain search criteria. The home interface for an entity bean may contain home business methods. Listing 11.1 shows the OrderHome remote home interface.

Listing 11.1. The Full Text of day11/OrderHome.java
package day11;

import java.util.*;
import java.rmi.*;
import javax.ejb.*;

public interface OrderHome extends EJBHome {
   /* Create methods */
   public Order create(String studentId,
    String status, double amount) throws CreateException, RemoteException;
   /* Finder methods */
   public Order findByPrimaryKey(String key)
      throws FinderException, RemoteException;
   public Collection findByStatus(String status)
      throws FinderException, RemoteException;
   /* Home methods */
   public double getTotalAmountOfAllOrders()
      throws FinderException, RemoteException;
}

To create the bean instance, a client calls the create() method of that home interface and passes appropriate values for studentId, status, and amount. The bean internally generates the orderId primary key.

The OrderHome home interface provides two finder methods: findByPrimaryKey() and findByStatus(). The findByPrimaryKey() method allows clients to locate an order bean by using the primary key. The single-object finder method findByPrimaryKey() returns the Order remote interface. The findByStatus() method allows clients to locate orders that are of a particular status, such as SUBMITTED. The multi-object finder method findByStatus()returns java.util.Collection. The container throws a FinderException from the implementation of a finder method to indicate an application-level error.

Note

With CMP beans, you do not write the ejbFind methods in your entity bean class. The finder methods are generated by the container provider tools.


The home interface also provides the home method getTotalAmountOfAllOrders(). This is a home method because it contains business logic that is not specific to an entity bean instance.

The remote home interface is a Java Remote Method Invocation (RMI) interface. So, method arguments and return types of a remote method must be legal types for the RMI over Internet Inter-ORB Protocol (RMI/IIOP), and the method must include java.rmi.RemoteException in its throws clause.

Defining the Component Interface

The remote interface Order is defined as shown in Listing 11.2.

Listing 11.2. The Full Text of day11/Order.java
package day11;

import java.rmi.*;
import javax.ejb.*;

public interface Order extends EJBObject {
   public String getOrderId()
      throws RemoteException;
   public String getStudentId()
      throws RemoteException;
   public void setStudentId(String studentId)
      throws RemoteException;
   public double getAmount()
      throws RemoteException;
   public void setAmount(double amount)
      throws RemoteException;
   public java.sql.Timestamp getOrderDate()
      throws RemoteException;
   public void setOrderDate(java.sql.Timestamp date)
      throws RemoteException;
   public String getStatus()
      throws RemoteException;
   public void setStatus(String status)
      throws RemoteException;
}

The remote interface contains business methods callable by the client. The Order interface contains access methods to the bean's persistent fields. Note that the Order interface does not contain the setOrderId() method because we don't allow clients to modify the order ID after an order is created.

The remote interface is a Java RMI interface. So, method arguments and return types of a remote method must be legal types for RMI/IIOP, and the method must include java.rmi.RemoteException in its throws clause.

Implementing the Enterprise Bean Class

Listing 11.3 shows the OrderEJB bean class.

Listing 11.3. The Full Text of day11/OrderEJB.java
package day11;

import java.util.*;
import java.io.*;
import java.rmi.*;
import javax.naming.*;
import javax.ejb.*;

public abstract class OrderEJB implements EntityBean
{
   protected EntityContext ctx;
   public abstract String getOrderId();
   public abstract void setOrderId(String orderId);
   public abstract String getStudentId();
   public abstract void setStudentId(String studentid);
   public abstract java.sql.Timestamp getOrderDate();
   public abstract void setOrderDate(java.sql.Timestamp timestamp);
   public abstract String getStatus();
   public abstract void setStatus(String status);
   public abstract double getAmount();
   public abstract void setAmount(double amount);
   /* Callback methods */
   public void setEntityContext(EntityContext ctx) {
      print("setEntityContext called");
      this.ctx = ctx;
   }
   public void unsetEntityContext() {
      print("unsetEntityContext called.
");
      this.ctx = null;
   }
   public void ejbActivate() {
      print("ejbActivate() called.
");
   }
   public void ejbPassivate() {
      print("ejbPassivate() called.
");
   }
   public void ejbStore() {
      print("ejbStore() called.
");
   }
   public void ejbLoad() {
      print("ejbLoad() called.
");
   }
   public void ejbRemove() throws RemoveException {
      print("ejbRemove() called.
");
   }
   public String ejbCreate( String studentId,
      String status, double amount) throws CreateException {
      print("ejbCreate() called.
");
      String orderId = getUniqueId();
      setOrderId(orderId);
      setStudentId(studentId);
      setStatus(status);
      setAmount(amount);
      setOrderDate(new java.sql.Timestamp(System.currentTimeMillis()));
      return null;
   }
   public void ejbPostCreate(String studentId,
      String courseId, double amount) throws CreateException {
      print("ejbPostCreate() called.
");
   }
   /* Home methods */
   public double ejbHomeGetTotalAmountOfAllOrders()
      throws FinderException {
      double totalAmount = 0.0;
      Collection col = ejbSelectAllOrderAmounts();
      Enumeration amounts=Collections.enumeration(col);
      while( amounts.hasMoreElements() ) {
         Double amount= (Double)amounts.nextElement();
         totalAmount += amount.doubleValue();
      }
      return totalAmount;
   }
   /* select methods. */
   public abstract Collection ejbSelectAllOrderAmounts()
      throws FinderException ;

   void print(String s) {
      System.out.println(s);
   }
   String getUniqueId(){
      return new Long(System.currentTimeMillis()).toString();
   }
}

The OrderEJB entity bean implements the javax.ejb.EntityBean interface and is defined as an abstract class. It consists of abstract accessor methods for the persistent fields orderId, studentId, orderDate, status, and amount. It implements the methods setEntityContext(), unsetEntityContext(), ejbActivate(), ejbPassivate(), ejbLoad(), ejbStore(), and ejbRemove(), as defined in the javax.ejb.EntityBean interface. The ejbCreate() method initializes the entity bean instance by assigning the input arguments to the persistent fields and generates a unique order ID. Note that the ejbCreate() method returns NULL. The bean implements the ejbPostCreate() method that corresponds to the ejbCreate() method.

The OrderEJB bean class uses the ejbSelectAllOrderAmounts() select method as a helper method to compute the total order amount in the ejbHomeGetTotalAmountOfAllOrders() home method. ejbSelectAllOrderAmounts() is defined as an abstract method, and the corresponding EJB QL (Enterprise JavaBeans Query Language) query is specified in the deployment descriptor.

Note

Select methods are query methods used within an entity bean instance. You may define zero or more select methods in your bean class. Each select method name starts with the prefix ejbSelect and is defined as an abstract method in an entity bean class. Every select method must have a corresponding EJB QL query string in the deployment descriptor. Select methods are not exposed to the client via the bean's home or component interface.

A select method is similar to a finder method. But unlike a finder method, a select method can return values that correspond to any persistent field or relationship field. Typically, select methods are used as helper methods within a business method.


Caution

Select methods are applicable only to container-managed persistence beans, and not to bean-managed persistence entity beans.


Declaring the Deployment Descriptor

Prior to EJB 2.0, queries for finder methods were written in a container-specific, proprietary way in the deployment descriptor. EJB 2.0 introduced a portable query language, based on the abstract schema, not on the more complex database schema. This portable query language provides a database- and vendor-independent way to specify queries in the deployment descriptor. In the following sections, we'll discuss the abstract persistent schema and EJB Query Language, and then we'll present the full listing of order deployment descriptors.

Abstract Persistent Schema

The abstract persistent schema defines an entity bean's persistent fields and relationship fields and determines the method for accessing them. The abstract schema is independent of the entity bean's implementation in a particular EJB container or particular database.

The following listing shows the abstract persistent schema for an Order entity bean:

<entity>
  <ejb-name>OrderEJB</ejb-name>
  . . .
  <cmp-version>2.x</cmp-version>
  <abstract-schema-name>Order</abstract-schema-name>
  <cmp-field>
    <field-name>orderId</field-name>
  </cmp-field>
  <cmp-field>
    <field-name>studentId</field-name>
  </cmp-field>
  <cmp-field>
    <field-name>orderDate</field-name>
  </cmp-field>
  <cmp-field>
    <field-name>status</field-name>
  </cmp-field>
   <cmp-field>
    <field-name>amount</field-name>
  </cmp-field>
  . . .
</entity>

The entity element declares an entity bean, and an ejb-name element within the entity element defines the entity bean's name (OrderEJB). The cmp-version element must be 2.x to take advantage of EJB 2.0 container-managed persistence. The abstract-schema-name declares the name of this abstract schema (Order). You'll later use this abstract schema name to specify EJB QL queries. The schema declares five container-managed persistent fields (cmp-field): orderId, studentId, orderDate, status, and amount. The names of these fields must match the abstract get and set methods in your entity bean class. For example, your entity bean class's methods must be getOrderId, setOrderId, getStudentId, setStudentId, getOrderDate, setOrderDate, getStatus, setStatus, getAmount, and setAmount.

Enterprise JavaBeans Query Language

EJB QL enables you to specify queries for entity beans with container-managed persistence in a database-independent, portable way. EJB QL uses an object-oriented, SQL-like syntax to specify queries for finder and select methods of a container-managed persistence entity bean. EJB QL queries are written with respect to entity bean's abstract schema. For example, the following line illustrates how the application uses an EJB QL query for the findOrdersByStatus(String status) method to find orders of a particular status, such as COMPLETE:

SELECT OBJECT(o) FROM Order AS o WHERE o.status = ?1

The preceding query returns Order objects, as indicated by the expression OBJECT(o) in the SELECT clause. In the FROM clause, the identifier o is analogous to an SQL correlation variable. The WHERE clause limits the orders to those whose status matches the value of the first parameter passed to the query, denoted by the expression ?1.

The container tools will translate such queries into the target language of the underlying database. For example, in case of a relational database, the container translates an EJB QL query to an SQL query. This allows the execution of queries to be shifted to the native language facilities provided by the database, instead of requiring queries to be executed on the runtime representation of the entity bean's state. As a result, query methods can be optimized as well as portable.

An EJB QL query consists of the following three clauses:

  • The SELECT clause determines the type of objects or values to be selected.

  • The FROM clause defines the scope of the query by declaring one or more identification variables. These identification variables may be referenced in the SELECT and WHERE clauses.

  • The optional WHERE is a conditional expression that restricts the objects or values retrieved by the query.

This section explains the details of EJB QL syntax. On Day 12, you'll learn more about EJB QL.

The FROM Clause

The FROM clause declares identification variable(s), based on the abstract schema, for navigating through the schema. These identification variables may be referenced in the SELECT and WHERE clauses.

For example, the following query restricts the domain of the query to Order entity beans only:

SELECT OBJECT(o) FROM Order AS o WHERE o.status = ?1

This query also declares an identification variable, o, whose type is the abstract schema type, Order. This variable is used in the SELECT and WHERE clauses.

The WHERE Clause

The WHERE clause consists of a conditional expression that is used to select or values that satisfy the expression. Therefore, the WHERE clause restricts the results of the query.

Input Parameters

You can use input parameters to base a query on the parameters supplied by the client. An input parameter is designated by a question mark (?) followed by an integer. For example, the first input parameter is ?1, the second is ?2, and so forth.

For example, the following query is used for the finder method findByStudent(String studentId):

SELECT OBJECT(o) FROM Order AS o WHERE o.student = ?1

The preceding query retrieves the orders that belong a particular student identified by studentId.

Note that the query specifies studentId as a java.lang.String as follows:

<query>
 <query-method>
  <method-name>findByStudent</method-name>
   <method-params>
   <method-param>java.lang.String</method-param>
   </method-params>
 </query-method>
 <ejb-ql>
 <![CDATA[ SELECT OBJECT(o) FROM Order AS o WHERE o.student = ?1
 ]]>
 </ejb-ql>
</query>

Caution

If an input parameter is NULL, comparison operations or arithmetic operations involving input parameter will return an unknown value.


Conditional Expressions

A WHERE clause consists of a conditional expression, which is evaluated from left to right within a precedence level.

Note

You may change the order of evaluation with parentheses.


BETWEEN Expressions

A BETWEEN expression determines whether an arithmetic expression falls within a range of values. For example, o.amount BETWEEN 100 and 200 is equivalent to o.amount >= 100 and o.amount <= 200. Similarly, o.amount NOT BETWEEN 100 and 200 is equivalent to o.amount < 100 OR o.amount > 200.

Caution

If the value of an arithmetic expression used in a BETWEEN expression is null, the value of the BETWEEN expression will be unknown.


IN Expressions

An IN expression determines whether a string belongs to a set of string literals. In the following example, if the status is Submitted, the expression is TRUE. If the status is Complete, it is FALSE.

o.status IN ('Submitted', 'Verified')

LIKE Expressions

A LIKE expression determines whether a wildcard pattern matches a string.

Some examples are

  • address.phone LIKE '12%3' is true for 12993 and false for 1234

  • asentence.word LIKE l_se is true for lose and false for loose

NULL Comparison Expressions

A NULL comparison expression tests whether a single-valued expression has a NULL value.

In the following example, if the status is NULL, the expression is TRUE. Otherwise, the expression is FALSE.

o.status is NULL

Functional Expressions

EJB QL includes the following built-in functions:

String Functions

  • CONCAT(String, String) concatenates the two given Strings and returns a String.

  • SUBSTRING(String, start, length) returns the substring of the given String beginning at position start and of length long.

  • LOCATE(String, String [, start]) returns the index of the first occurrence of a String within another String as an int. You can optionally specify the start index of the search.

  • LENGTH(String) returns an int indicating the length of the String.

Arithmetic Functions

  • ABS(number) returns the absolute value of a number.

  • SQRT(double) returns the square root of a number as a double.

The SELECT Clause

The SELECT clause denotes the query result. The SELECT clause determines the type of objects or values to be selected. The SELECT clause of an EJB QL query defined for a finder method always corresponds to the abstract schema type of the entity bean for which the finder method is defined.

For example, because Order bean defines the findAllOrders() method in the OrderHome interface, the objects returned by the following query have the Order remote interface type:

SELECT OBJECT(o) FROM Order AS o

Filter for Duplicates

The DISTINCT keyword eliminates duplicate return values. For example, to receive a unique order list, you would specify the query as follows:

SELECT DISTINCT OBJECT(o) FROM Order AS o

Note

If the method of the query returns a java.util.Collection, which allows duplicates, you must specify the DISTINCT keyword to eliminate duplicates. However, if the method returns a java.util.Set, the DISTINCT keyword is redundant because a java.util.Set may not contain duplicates.


Note

EJB 2.1, which is currently work in progress at the time of writing the book, provides enhancements to EJB QL. For CMP entity beans it supports new features namely order by and aggregate operations.

For ordering of results returned by a query, you can use ORDER BY clause in the SELECT statement. For example, the following query returns the list of orders in the ascending order of order date.

SELECT OBJECT(o) FROM Order o ORDER BY o.orderDate ASC

Query statements can use aggregate operators to return the aggregate values of the results. The five aggregate operators available in EJB 2.1 are AVG, SUM, COUNT, MIN, and MAX. These operators can be used in the SELECT clause of the query statement. For example, the following query returns the amount of all orders:

SELECT SUM(o.amount) FROM Order o

If DISTINCT keyword is used before an aggregate operator then the duplicate values are removed first before aggregate operator is applied.


Now that you have learned the fundamentals of the abstract persistent schema and EJB QL, let's examine the ejb-jar.xml deployment descriptor of our Order bean.

As shown in Listing 11.4, ejb-jar.xml declares OrderEJB as the name of the entity bean and specifies the home interface, remote interface, and bean class. It also declares the Order abstract persistent schema, and that orderId is the primary key using the primkey-field element. In addition, ejb-jar.xml specifies the EJB QL queries for all the finder and select methods (except the findByPrimaryKey() method). For example, the EJB QL for the findByStatus(String status) method is <![CDATA[SELECT OBJECT(o) FROM Order AS o WHERE o.status = ?1]]>.

Listing 11.4. The Full Text of day11/ejb-jar.xml
<?xml version="1.0"?>
<!DOCTYPE ejb-jar PUBLIC
'-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN'
'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>

<ejb-jar>
  <enterprise-beans>
    <entity>
      <ejb-name>OrderEJB</ejb-name>
      <home>day11.OrderHome</home>
      <remote>day11.Order</remote>
      <ejb-class>day11.OrderEJB</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>Order</abstract-schema-name>
      <cmp-field>
        <field-name>orderId</field-name>
      </cmp-field>
      <cmp-field>
        <field-name>studentId</field-name>
      </cmp-field>
      <cmp-field>
        <field-name>orderDate</field-name>
      </cmp-field>
      <cmp-field>
        <field-name>status</field-name>
      </cmp-field>
      <cmp-field>
        <field-name>amount</field-name>
      </cmp-field>
      <primkey-field>orderId</primkey-field>
      <query>
        <query-method>
          <method-name>findByStatus</method-name>
          <method-params>
            <method-param>java.lang.String</method-param>
          </method-params>
        </query-method>
        <ejb-ql>
          <![CDATA[SELECT OBJECT(o) FROM Order AS o
          WHERE o.status = ?1]]> 
        </ejb-ql>
      </query>
      <query>
        <query-method>
          <method-name>ejbSelectAllOrderAmounts</method-name>
          <method-params>
          </method-params>
        </query-method>
        <ejb-ql>
          <![CDATA[SELECT o.amount FROM Order AS o
          WHERE o.orderId IS NOT NULL]]>
        </ejb-ql>
      </query>
    </entity>
  </enterprise-beans>
</ejb-jar>

Listing 11.5 shows a weblogic-ejb-jar.xml deployment descriptor that is specific to WebLogic Server. The jndi-name element declares the JNDI name of the enterprise bean. So, the JNDI name of the OrderEJB is day11/Order.

Listing 11.5. The Full Text of day11/weblogic-ejb-jar.xml
<?xml version="1.0"?>
<!DOCTYPE weblogic-ejb-jar PUBLIC
'-//BEA Systems, Inc.//DTD WebLogic 7.0.0 EJB//EN'
'http://www.bea.com/servers/wls700/dtd/weblogic-ejb-jar.dtd'>

<weblogic-ejb-jar>
  <weblogic-enterprise-bean>
    <ejb-name>OrderEJB</ejb-name>
    <entity-descriptor>

      <persistence>
        <persistence-use>
          <type-identifier>WebLogic_CMP_RDBMS</type-identifier>
          <type-version>6.0</type-version>
          <type-storage>META-INF/weblogic-cmp-rdbms-jar.xml</type-storage>
        </persistence-use>
      </persistence>

    </entity-descriptor>
    <jndi-name>day11/Order</jndi-name>
  </weblogic-enterprise-bean>
</weblogic-ejb-jar>

Listing 11.6 shows the weblogic-cmp-rdbms.xml. It contains the WebLogic Server–specific deployment descriptors that define the container-managed persistence services. For example, you can define abstract schema to database element mapping. Typically the container tools allow you to map abstract schema to database schema at deployment time.

Listing 11.6. The Full Text of day11/weblogic-cmp-rdbms-jar.xml
<!DOCTYPE weblogic-rdbms-jar PUBLIC
'-//BEA Systems, Inc.//DTD WebLogic 7.0.0 EJB RDBMS Persistence//EN'
'http://www.bea.com/servers/wls700/dtd/weblogic-rdbms20-persistence-700.dtd'>
<weblogic-rdbms-jar>
 <weblogic-rdbms-bean>
  <ejb-name>OrderEJB</ejb-name>
  <data-source-name>jdbc.styejbDB</data-source-name>
  <table-map>
  <table-name>orders</table-name>
  <field-map>
   <cmp-field>orderId</cmp-field>
   <dbms-column>order_id</dbms-column>
  </field-map>
  <field-map>
   <cmp-field>studentId</cmp-field>
   <dbms-column>student_id</dbms-column>
  </field-map>
  <field-map>
   <cmp-field>orderDate</cmp-field>
   <dbms-column>order_date</dbms-column>
  </field-map>
  <field-map>
   <cmp-field>status</cmp-field>
   <dbms-column>status</dbms-column>
  </field-map>
  <field-map>
   <cmp-field>amount</cmp-field>
   <dbms-column>amount</dbms-column>
  </field-map>
  </table-map>
 </weblogic-rdbms-bean>
</weblogic-rdbms-jar>

In the case of JBoss, you need to write the deployment descriptors jboss.xml and jbosscmp-jdbc.xml. Listing 11.7 shows the deployment descriptor jboss.xml. It declares the JNDI name of OrderEJB entity bean as day11/Order.

Listing 11.7. The Full Text of day11/jboss.xml
<?xml version="1.0" encoding="UTF-8"?>
<jboss>
  <enterprise-beans>
    <entity>
      <ejb-name>OrderEJB</ejb-name>
      <jndi-name>day11/Order</jndi-name>
    </entity>
  </enterprise-beans>
</jboss>

Listing 11.8 shows the jbosscmp-jdbc.xml. It contains the abstract-schema-to-database-element mapping. In each cmp-field element, you specify the name of the cmp-field using the field-name element and the corresponding database column name using the column-name element.

Listing 11.8. The Full Text of day11/jbosscmp-jdbc.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jbosscmp-jdbc PUBLIC
   "-//JBoss//DTD JBOSSCMP-JDBC 3.0//EN"
   "http://www.jboss.org/j2ee/dtd/jbosscmp-jdbc_3_0.dtd">
<jbosscmp-jdbc>
   <defaults>
      <datasource>java:/DefaultDS</datasource>
      <datasource-mapping>Hypersonic SQL</datasource-mapping>
      <create-table>false</create-table>
      <remove-table>false</remove-table>
      <pk-constraint>true</pk-constraint>
      <preferred-relation-mapping>foreign-key</preferred-relation-mapping>
   </defaults>
   <enterprise-beans>
      <entity>
         <ejb-name>OrderEJB</ejb-name>
         <table-name>orders</table-name>
         <cmp-field>
            <field-name>orderId</field-name>
            <column-name>order_id</column-name>
         </cmp-field>
         <cmp-field>
            <field-name>studentId</field-name>
            <column-name>student_id</column-name>
         </cmp-field>
         <cmp-field>
            <field-name>orderDate</field-name>
            <column-name>order_date</column-name>
         </cmp-field>
         <cmp-field>
            <field-name>status</field-name>
            <column-name>status</column-name>
         </cmp-field>
         <cmp-field>
            <field-name>order</field-name>
            <column-name>amount</column-name>
         </cmp-field>
      </entity>
</jbosscmp-jdbc>

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

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