Oracle8i Enterprise JavaBeans Developer's Guide and Reference
Release 3 (8.1.7)

Part Number A83725-01

Library

Product

Contents

Index

Go to previous page Go to beginning of chapter Go to next page

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:

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:

Entity Bean Class

The entity bean class implements the following methods:

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:

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:

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

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:



Go to previous page
Go to beginning of chapter
Go to next page
Oracle
Copyright © 1996-2000, Oracle Corporation.

All Rights Reserved.

Library

Product

Contents

Index