Oracle8i Enterprise JavaBeans Developer's Guide and Reference Release 3 (8.1.7) Part Number A83725-01 |
|
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:
javax.ejb.EJBObject
.
javax.ejb.EJBHome
. It defines the create
and finder methods, including findByPrimaryKey
, for your bean.
java.lang.String
, or be defined within its own class.
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
>
.
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 extendsEJBHome
{ // Create a new PO public PurchaseOrdercreate
() throws CreateException, RemoteException; // Find an existing one public PurchaseOrderfindByPrimaryKey
(String POnumber) throws FinderException, RemoteException; }
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 extendsEJBObject
{ // Price the purchase order public floatprice
() throws RemoteException; // getContents returns a Vector of LineItem objects in the purchase public VectorgetContents
() throws RemoteException; //Add items to the purchase public voidaddItem
(int sku, int count) throws RemoteException; }
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:
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.
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.
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>
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>
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:
ejbCreate
method initializes the primary key, ponumber
, to the next available number in the purchase order number sequence.
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 StringejbCreate
() 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 StringejbFindByPrimaryKey
(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 entity bean class implements the following methods:
ejbCreate
method and any finder methods, including ejbFindByPrimaryKey
.
EntityBean
interface.
The following code implements methods of an entity bean called PurchaseOrderBean
.
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 classPurchaseOrderBean
implementsEntityBean
{ EntityContext ctx; Vector items; // The items in the PO (instances of LineItem)
The following is the implementation for the bean methods that were declared in the remote interface: price
, getContents
, and addItem
.
public floatprice
() 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 VectorgetContents
() throws RemoteException { return items; } // The add Item method gets the price and description public voidaddItem
(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); } }
Once you have implemented the business logic methods, you also must provide the following:
ejbCreate
method for each create
method defined in the home interface.
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.
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.
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
() {}
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 StringejbCreate
() 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 voidejbPostCreate
() { items = new Vector (); }
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.
In order to provide other finder methods, you must do the following:
find<
name
>
in the home interface.
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 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
.
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 voidejbStore
() 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 voidejbLoad
() 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 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);
}
}
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 voidsetEntityContext
(EntityContext ctx) { this.ctx = ctx; } //reinitialize the context to null public voidunsetEntityContext
() { this.ctx = null; }
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 voidejbActivate
() { } //There are no requirements for ejbPassivate for this bean public voidejbPassivate
() { }
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; } }
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:
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;
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.
<?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>
To access a deployed entity bean, the client does one of the following:
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".
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();
A client can access an existing entity bean through one of the following methods:
findByPrimaryKey
method, which returns the entity bean's remote reference.
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. |
|