Oracle8i Enterprise JavaBeans Developer's Guide and Reference Release 3 (8.1.7) Part Number A83725-01 |
Library | Service | Contents | Index

## Creating Entity Beans

The steps for creating an entity bean are the same as for a session bean. The difference is contained in the methods and data within the bean class. There are two types of entity beans: bean-managed persistent and container-managed persistent. This section discusses a bean-managed persistent bean. The "Container-Managed Persistence" gives an example of a container-managed persistent bean.

To create an entity bean, you perform the following steps:

1. Create a remote interface for the bean. The remote interface declares the methods that a client can invoke. It must extend `javax.ejb.EJBObject`.

2. Create a home interface for the bean. The home interface must extend `javax.ejb.EJBHome`. It defines the `create` and finder methods, including `findByPrimaryKey`, for your bean.

3. Define the primary key for the bean. The primary key identifies each entity bean instance. The primary key must either be a well-known class, such as `java.lang.String`, or be defined within its own class.

4. Implement the bean. This includes the following:

   1. The implementation for the methods declared in your remote interface.

   2. An empty constructor for the bean.

   3. The methods defined in the `javax.ejb.EntityBean` interface.

   4. The methods that match the methods declared in your home interface. This includes the following:

      - The `ejbCreate` and `ejbPostCreate` methods with parameters matching those of the `create` method defined of the home interface.

      - An `ejbFindByPrimary` key method which corresponds to the `findByPrimaryKey` method of the home interface.

      - Any other finder methods that were defined in the home interface.

5. If the persistent data is saved to or restored from a database, you must ensure that the correct tables exist for the bean.

6. Create the bean deployment descriptor. The deployment descriptor specifies properties for the bean through XML properties. See "Deploying an EJB" for more details.

7. Create an ejb-jar file containing the bean, the remote and home interfaces, and the deployment descriptor. The ejb-jar file must define all beans within your application. Refer to "Create a JAR File" for more details.

### Home Interface

Similar to session beans, the entity bean's home interface must contain a `create` method, which the client invokes to create the bean instance. Each `create` method can have a different signature.

For an entity bean, you must develop a `findByPrimaryKey` method. Because of the persistent data associated with the instance, each entity bean instance is uniquely identified by a primary key. The type for the unique key is defined by the developer. For example, the customer bean's primary key is the customer number. The purchase order's primary key is a purchase order number. The primary key can be anything--as long as it is unique.

When the entity bean is first created, the `ejbCreate` method creates a primary key to identify the bean. A unique primary key is created and initialized within the `ejbCreate` method in the bean class. From this time onward, this bean is associated with this primary key. Thus, you can retrieve the bean by providing the primary key object to the `findByPrimaryKey` method.

Optionally, you can develop other finder methods to find the bean. These methods are named `find<``name``>`.

Note: The return type for all finder methods within the home interface must be either the entity bean's remote interface or an `Enumeration` of objects that implement the entity bean's remote interface. Returning a `Collection` is not supported. The return type for all finder methods implemented within the bean class returns the primary key or an `Enumeration` of primary keys. The container retrieves the appropriate entity bean remote interface for each primary key returned on any `ejbFind<``name``>` method.

#### Example 4-1 Purchase Order Home Interface

To demonstrate an entity bean, we are creating a bean that manages a purchase order. The entity bean contains a list of items ordered by the customer.

The home interface extends `javax.ejb.EJBHome` and defines the `create` and `findByPrimaryKey` methods.

    package purchase;

    import javax.ejb.*;
    import java.rmi.RemoteException;
    import java.sql.SQLException;

    public interface PurchaseOrderHome extends EJBHome
    {
      // Create a new PO
      public PurchaseOrder create() throws CreateException, RemoteException;

      // Find an existing one
      public PurchaseOrder findByPrimaryKey (String POnumber)
        throws FinderException, RemoteException;
     }

### Remote Interface

The entity bean remote interface is the interface that the customer sees and invokes methods upon. It extends `javax.ejb.EJBObject` and defines the business logic methods. For our purchase order entity bean, the remote interface contains methods for adding items to the purchase order, for retrieving a list of all items within the purchase order, and computing the full price for the purchase order.

    package purchase;

    import javax.ejb.EJBObject;
    import java.rmi.RemoteException;
    import java.sql.SQLException;
    import java.util.Vector;

    public interface PurchaseOrder extends EJBObject  {
      // Price the purchase order
      public float price() throws RemoteException;

      // getContents returns a Vector of LineItem objects in the purchase
      public Vector getContents() throws RemoteException;

      //Add items to the purchase
      public void addItem (int sku, int count) throws RemoteException;
    }

### Primary Key

Each entity bean instance has a primary key that uniquely identifies it from other instances. You can define your primary key in one of two ways:

- Define the type of the primary key to be a well-known type, such as `java.lang.String`. If the primary key is a well-known data type, define the type in the `<prim-key-class>` in the deployment descriptor.

- Define the type of the primary key as a serializable object within <`name`>`PK` class. If the primary key is a complex data type, define the primary key in a class that is serializable. This class is declared in the `<prim-key-class>` element in the deployment descriptor.

#### Defining Primary Key as Well-known Type

Define your primary key to be a well-known type by defining the data type of the primary key within the deployment descriptor.

The purchase example defines its primary key as a `java.lang.String`.

    <enterprise-beans>
          <entity>
             <ejb-name>test/purchase</ejb-name>
             <home>purchase.PurchaseOrderHome</home>
             <remote>purchase.PurchaseOrder</remote>
             <ejb-class>purchaseServer.PurchaseOrderBean</ejb-class>
             <persistence-type>Bean</persistence-type>
             <prim-key-class>java.lang.String</prim-key-class>
             <reentrant>False</reentrant>
          </entity>
    ...
    </enterprise-beans>

#### Defining the Primary Key in a Class

If your primary key is more complex than a simple data type, your primary key must be a class that is serializable of the name `<``name``>PK`. Within this class, you should implement the `equals` and `hashCode` methods to provide for an implementation specific to this primary key.

The customer example declares its primary key--a customer identifier--within the `PurchaseOrderPK.java`.

    package purchase;

    public class PurchaseOrderPK implements java.io.Serializable
    {
      public int orderid;

      public boolean equals(Object obj) {
        if ((obj instanceof PurchaseOrderPK) &&
            (((PurchaseOrderPK)obj).orderid == this.orderid))
          return true;
        return false;
      }

      public int hashCode() {
        return orderid;
      }
    }

The class that defines the primary key is declared within the deployment descriptor, as follows:

    <enterprise-beans>
          <entity>
             <ejb-name>test/purchase</ejb-name>
             <home>purchase.PurchaseOrderHome</home>
             <remote>purchase.PurchaseOrder</remote>
             <ejb-class>purchaseServer.PurchaseOrderBean</ejb-class>
             <persistence-type>Bean</persistence-type>
             <prim-key-class>purchase.PurchaseOrderPK</prim-key-class>
             <reentrant>False</reentrant>
          </entity>
    ...
    </enterprise-beans>

#### Manage the Primary Key

The `ejbCreate` method is responsible primarily for the creation of the primary key. This involves creating the primary key, creating the persistent data representation for the key, initializing the key to a unique value, and returning this key to the invoker. The `ejbFindByPrimaryKey` method is responsible for verifying that the primary key is still unique and returns it again to the container.

In the purchase order example, these methods perform the following:

- The `ejbCreate` method initializes the primary key, `ponumber`, to the next available number in the purchase order number sequence.

- The `ejbFindByPrimaryKey` method is implemented to check that the purchase order number is valid and returns this number to the container.

      // The create methods takes care of generating a new PO and returns
      // its primary key
      public String ejbCreate () throws CreateException, RemoteException
      {
        String ponumber = null;
        try {
          //retrieve the next available purchase order number
          #sql { select ponumber.nextval into :ponumber from dual };
          //assign this number as this instance's identification number
          #sql { insert into pos (ponumber, status) values (:ponumber, 'OPEN') };
        } catch (SQLException e) {
          throw new PurchaseException (this, "create", e);
        }
        return ponumber;
      }

      // The ejbFindByPrimaryKey method verifies that the POnumber exists. This
      // method must return the primary key to the container.. which in turn
      // retrieves the instance based on the primary key. So.. this method must
      // only verify that the primary key is valid.
      public String ejbFindByPrimaryKey (String ponumber)
          throws FinderException, RemoteException
      {
          try {
            int count;
            #sql { select count (ponumber) into :count from pos
                     where ponumber = :ponumber };

            // There has to be one
            if (count != 1)
              throw new FinderException ("Inexistent PO: " + ponumber);
          } catch (SQLException e) {
            throw new PurchaseException (this, "findByPrimaryKey", e);
          }
          // The ponumber is the primary key
          return ponumber;
      }

### Entity Bean Class

The entity bean class implements the following methods:

- An empty constructor for creating the bean instance.

- The target methods for the methods declared in the home interface, which includes the `ejbCreate` method and any finder methods, including `ejbFindByPrimaryKey`.

- The business logic methods declared in the remote interface.

- The methods declared in the `EntityBean` interface.

The following code implements methods of an entity bean called `PurchaseOrderBean`.

#### 1. Declaring Variables

The purchase order bean declares a vector to store all of the items within the customer's shopping cart. In addition, to retrieve environment information for the entity bean, an entity context is defined.

    #sql iterator ItemsIter (int skunumber, int count, String description,
                             float price);

    public class PurchaseOrderBean implements EntityBean {
      EntityContext ctx;
      Vector items;         // The items in the PO (instances of LineItem)

#### 2. Implementing Remote Interface Methods

The following is the implementation for the bean methods that were declared in the remote interface: `price`, `getContents`, and `addItem`.

      public float price() throws RemoteException {
        float price = 0;
        Enumeration e = items.elements ();
        while (e.hasMoreElements ()) {
          LineItem item = (LineItem)e.nextElement ();
          price += item.quantity * item.price;
        }

        // 5% discount if buying more than 10 items
        if (items.size () > 10)
          price -= price * 0.05;

        // Shipping is a constant plus function of the number of items
        price += 10 + (items.size () * 2);

        return price;
      }

      // The getContents methods has to load the descriptions
      public Vector getContents() throws RemoteException {
        return items;
      }

      // The add Item method gets the price and description
      public void addItem (int sku, int count) throws RemoteException {
        try {
          String description;
          float price;
          #sql { select price, description into :price, :description
                 from skus where skunumber = :sku };
          items.addElement (new LineItem (sku, count, description, price));
        } catch (SQLException e) {
          throw new PurchaseException (this, "addItem", e);
        }
      }

#### 3. Implementing EntityBean Interface Methods

Once you have implemented the business logic methods, you also must provide the following:

- A public constructor for the bean instance--This constructor takes no arguments. The container invokes the constructor to create an instance of the entity bean class. The constructor can be an empty implementation.

- An `ejbCreate` method for each `create` method defined in the home interface.

- An `ejbFind<``name``>` method for each of the `find<``name``>` methods defined in the home interface. This includes at least an `ejbFindByPrimary` key method that returns the primary key to the container.

- An implementation for the `EntityBean` methods--These methods are callback methods that the container calls when necessary. Most of the callback methods pertain to managing the persistence of the entity bean's data.

##### Public Constructor

The public constructor is called by the container to create the bean instance. The `ejbCreate` and `ejbPostCreate` methods are invoked to intialize this instance. The following is the purchase order constructor.

    //provide an empty constructor for the creating the instance
    public void PurchaseOrderBean() {}

##### The Create Methods: ejbCreate and ejbPostCreate

As shown in Figure 4-2, the `ejbCreate` and `ejbPostCreate` methods are invoked when the corresponding `create` method--the methods all have the same arguments--is invoked. Typically, the `ejbCreate` method initializes all of the persistent data; the `ejbPostCreate` does any initialization that involves the entity's context. The context information is not available at `ejbCreate` time, but is available at `ejbPostCreate` time.

The following example shows the `ejbCreate` and `ejbPostCreate` for the purchase order example. The `ejbCreate` method initializes the primary key, which is the purchase order number, and returns this key to the invoker. The purchase order line item vector is initialized within the `ejbPostCreate`.

    // The create methods takes care of generating a new PO and returns
    // its primary key
    public String ejbCreate () throws CreateException, RemoteException
    {
      String ponumber = null;
      try {
        //retrieve the next available purchase order number
        #sql { select ponumber.nextval into :ponumber from dual };
        //assign this number as this instance's identification number
        #sql { insert into pos (ponumber, status) values (:ponumber, 'OPEN') };
      } catch (SQLException e) {
        throw new PurchaseException (this, "create", e);
      }
      return ponumber;
    }

    // create a vector to contain the purchase order line items. since this
    // is performed only once and needed for the lifetime of the object, it is
    // appropriate to create the vector in either ejbCreate or ejbPostCreate.
    public void ejbPostCreate () {
        items = new Vector ();
    }

##### The Finder Methods

All entity beans must provide an `ejbFindByPrimaryKey` method. You can also have other types of finder methods. Since the developer must implement any finder method declared within the home interface, there is no limitation on how many of these types of methods you can have. The only restrictions is that any finder method, other than the `ejbFindByPrimaryKey` method, must return either a reference to the remote interface or an `Enumeration` containing multiple references to remote interfaces. The `ejbFindByPrimaryKey` method must return the primary key.

Note: The return type cannot be a `Collection`, as it is not currently supported.

In order to provide other finder methods, you must do the following:

1. Declare the method as `find<``name``>` in the home interface.

2. Implement the method as `ejbFind<``name``>` in the bean class.

The following is the `ejbFindByPrimaryKey` method for the purchase order example. It verifies that the primary key is valid. If so, it returns the key to the container. The container retrieves the correct bean instance for this key and returns the reference to the client.

    // The findByPrimaryKey method verifies that the POnumber exists. This
    // method must return the primary key to the container.. which in turn
    // retrieves the instance based on the primary key. So.. this method must
    // only verify that the primary key is valid.
    public String ejbFindByPrimaryKey (String ponumber)
        throws FinderException, RemoteException
    {
        try {
          int count;
          #sql { select count (ponumber) into :count from pos
                   where ponumber = :ponumber };

          // There has to be one
          if (count != 1)
            throw new FinderException ("Inexistent PO: " + ponumber);
        } catch (SQLException e) {
          throw new PurchaseException (this, "findByPrimaryKey", e);
        }
        // The ponumber is the primary key
        return ponumber;
    }

##### The EntityBean Methods: Load and Store

The main difference between entity and session beans is that entity beans possess persistent data that must be managed. When data is defined as persistent, it must be continually saved to or restored from a resource, such as a database or file. If the bean is destroyed, the persistent data can be restored without any loss.

The `EntityBean` interface, which all entity beans implement, defines the following callback methods for managing the persistent data:

- `ejbStore`--saves the data to persistent storage

  The container always invokes `ejbStore` right before a transaction commits or the bean is removed to save the existing values of the persistent data.

- `ejbLoad`--loads the data saved within persistent storage into the bean

  The container invokes `ejbLoad` right after a bean is instantiated or a transaction begins.

Figure 4-4 shows how the persistent data within an entity bean can be saved to a database using `ejbStore`. In addition, the data is restored from the database through `ejbLoad`.

#### Figure 4-4 Persistent Data Management

The following are the methods from the purchase order example. The `ejbStore` method saves the purchase order items to the database. The `ejbLoad` method restores the purchase order items from the database.

    // The store method replaces all entries in the lineitems table with the
    // new entries from the bean
    public void ejbStore() throws RemoteException {
      // Get the purchase order number
      String ponumber = (String)ctx.getPrimaryKey();

      try {
        // Delete old entries in the database
        #sql { delete from lineitems where ponumber = :ponumber };

        // Insert new entries from the vector in the bean. Crude, but effective.
        Enumeration e = items.elements ();
        while (e.hasMoreElements ()) {
          LineItem item = (LineItem)e.nextElement ();
          #sql { insert into lineitems (ponumber, skunumber, count)
                   values (:ponumber, :(item.sku), :(item.quantity))
          };
        }
      } catch (SQLException e) {
        throw new PurchaseException (this, "store", e);
      }
    }

    // The load method populates the items array with all the saved
    // line items
    public void ejbLoad() throws RemoteException {
      // Get the purchase order number
      String ponumber = (String)ctx.getPrimaryKey();

      // Load all line items into a new vector.
      try {
        items = new Vector ();
        ItemsIter iter = null;
        try {
          #sql iter = {
            select lineitems.skunumber, lineitems.count,
                   skus.description, skus.price
              from lineitems, skus
              where ponumber = :ponumber and lineitems.skunumber = skus.skunumber
          };

          while (iter.next ()) {
            LineItem item =
              new LineItem (iter.skunumber(), iter.count(), iter.description(),
                            iter.price());
            items.addElement (item);
          }
        } finally {
          if (iter != null) iter.close ();
        }
       } catch (SQLException e) {
        throw new PurchaseException (this, "load", e);
      }
    }

##### The EntityBean Methods: Remove

The `ejbRemove` method is invoked when the client invokes the remove method. For a bean-managed persistent bean, you must remove the data from the database that is associated with the bean within this method.

The following example shows how the purchase order line items are removed from the database.

    // The remove method deletes all line items belonging to the purchase order
    public void ejbRemove() throws RemoteException {
      // Get the purchase order number from the session context
      String ponumber = (String)ctx.getPrimaryKey();
      try {
        //delete the line item vector for the purchase order
        #sql { delete from lineitems where ponumber = :ponumber };
        //delete the row associated with the purchase order
        #sql { delete from pos where ponumber = :ponumber };
      } catch (SQLException e) {
        throw new PurchaseException (this, "remove", e);
      }
    }

##### The EntityBean Methods: Setting the Context

If you want to access any information within the context during the lifetime of your application, you must save the context within the `setEntityContext`.

    //Set the provided context to this.ctx
    public void setEntityContext(EntityContext ctx)
    {
     this.ctx = ctx;
    }

    //reinitialize the context to null
    public void unsetEntityContext()
    {
     this.ctx = null;
    }

##### The Entity Bean Methods: Activate and Passivate

Oracle does not currently support activation and passivation. However, you still must provide an empty implementation for these methods. ,

    //There are no requirements for ejbActivate for this bean
    public void ejbActivate()
    {
    }

    //There are no requirements for ejbPassivate for this bean
    public void ejbPassivate()
    {
    }

#### 4. LineItem Class

The purchase order application persistently stores the individual orders within the purchase using a persistent Java object, called `LineItem`. That is, the entity bean delegates management of each item in the purchase order to a non-EJB Java object.

    package purchase;

    public class LineItem implements java.io.Serializable {
      public int sku;
      public int quantity;
      public String description;
      public float price;

      //Persistently manage each line item within the purchase order.
      public LineItem (int sku, int quantity, String description, float price) {
        //Each line item has the following information: SKU number, quantity,
        // description, and price.
        this.sku = sku;
        this.quantity = quantity;
        this.description = description;
        this.price = price;
      }
    }

### Create Database Table and Columns for Entity Data

If your entity bean stores its persistent data within a database, you need to create the appropriate table with the proper columns for the entity bean. This table must be created before the bean is loaded into the database.

In our purchase order example, you must create the following tables:

Table | Columns | Description
--- | --- | ---
SKUS | skunumber: item number description: item description price: price of item | Each item in the warehouse is described in this table.
POS | ponumber:purchase order number status: open, executed, or shipped | The table that manages the state of the purchase order for a customer. Contains the state of the order.
LINEITEMS | ponumber: purchase order number skunumber: item number count: number of items ordered. | The table that contains all of the individual items ordered by a customer.

The following shows the SQL commands that create these fields.

    -- This sql scripts create the SQL tables used by the PurchaseOrder bean

    -- The sku table lists all the items available for purchase
    create table skus (skunumber number constraint pk_skus primary key,
                       description varchar2(2000),
                       price number);

    -- The pos table stores information about purchase orders
    -- The status column is 'OPEN', 'EXECUTED' or 'SHIPPED'
    create table pos (ponumber number constraint pk_pos primary key,
                      status varchar2(30));

    -- The ponumber sequence is used to generate PO ids
    create sequence ponumber;

    -- The lineitems table stores the contents of a po
    -- The skunumber is a reference into the skus table
    -- The ponumber is a reference into the pos table
    create table lineitems (ponumber number constraint fk_pos references pos,
                            skunumber number constraint fk_skus references skus,
                            count number);
    commit;

    exit;

### Deploying the Entity Bean

You deploy the entity bean the same way as the session bean, which is detailed in "Deploying an EJB". In the same manner, you must create the XML deployment descriptors for the bean, create a JAR file containing all of the bean's files, and use `deployejb` tool to load and publish the bean in the database.

The XML deployment descriptors are explained fully in Appendix A, "XML Deployment Descriptors". See the appendix for a full description on defining persistence for entity beans. For completeness, the following is how the purchase order example deployment descriptors are organized. Since the purchase order does not define any logical names that must be mapped and do not use the `<run-as>` option, only the EJB deployment descriptor is required.

#### Example 4-2 Purchase Order EJB Deployment Descriptor

    <?xml version="1.0"?>
    <!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems Inc.//DTD Enterprise JavaBeans 1.1
    //EN" "ejb-jar.dtd">
    <ejb-jar>
       <enterprise-beans>
          <entity>
             <description>no description</description>
             <ejb-name>test/purchase</ejb-name>
             <home>purchase.PurchaseOrderHome</home>
             <remote>purchase.PurchaseOrder</remote>
             <ejb-class>purchaseServer.PurchaseOrderBean</ejb-class>
             <persistence-type>Bean</persistence-type>
             <prim-key-class>java.lang.String</prim-key-class>
             <reentrant>False</reentrant>
          </entity>
       </enterprise-beans>
       <assembly-descriptor>
          <security-role>
             <description>no description</description>
             <role-name>PUBLIC</role-name>
          </security-role>
          <method-permission>
             <description>no description</description>
             <role-name>PUBLIC</role-name>
             <method>
                <ejb-name>test/purchase</ejb-name>
                <method-name>*</method-name>
             </method>
          </method-permission>
          <container-transaction>
             <description>no description</description>
             <method>
                <ejb-name>test/purchase</ejb-name>
                <method-name>*</method-name>
             </method>
             <trans-attribute>Required</trans-attribute>
          </container-transaction>
       </assembly-descriptor>
    </ejb-jar>

### Client Accessing Deployed Entity Bean

To access a deployed entity bean, the client does one of the following:

- Create a New Entity Bean

- Access an Existing Entity Bean

#### Create a New Entity Bean

When you access an entity bean, you must first locate the bean's home interface. You retrieve the home interface from the name space through JNDI. The URL must be of the following syntax:

    <service_name>://<hostname>:<iiop_listener_port>:<SID>/<published_obj_name>

This syntax is described more in "Getting the Home Interface Object".

#### Example 4-3 Retrieving the Home Interface from the JNDI Name Space

The following example retrieves the home interface of the EJB located published in `/test/purchase`. The host, port, and SID are localhost, 2471, and ORCL respectively.

    String serviceURL = "sess_iiop://localhost:2471:ORCL";
    String objectName = "/test/purchase";

    Hashtable env = new Hashtable();
    env.put(Context.URL_PKG_PREFIXES, "oracle.aurora.jndi");
    env.put(Context.SECURITY_PRINCIPAL, user);
    env.put(Context.SECURITY_CREDENTIALS, password);
    env.put(Context.SECURITY_AUTHENTICATION, ServiceCtx.NON_SSL_LOGIN);
    Context ic = new InitialContext (env);

    CustomerHome ch = (CustomerHome)ic.lookup (serviceURL + objectName);
    Customer myCust = (Customer) ch.create();

Note: Notice how the type casting on the lookup does not require the narrow method. The Oracle8i lookup method automatically performs the proper narrowing function for you. Although you still must provide the type that the returned object is cast to.

#### Access an Existing Entity Bean

A client can access an existing entity bean through one of the following methods:

- Receive the reference from another party--for example, as a returned parameter on a method call.

- Given a primary key for the entity bean, invoke the `findByPrimaryKey` method, which returns the entity bean's remote reference.

- Given a `Handle` object for the bean, invoke the `getEJBObject` method on the `Handle` object. This handle was created from the entity bean object using the `getHandle` method. The handle can be passed, as is, to another object or it can be serialized and stored to be used at a later date.

| Copyright © 1996-2000, Oracle Corporation. All Rights Reserved. |
Library | Service | Contents | Index