Skip Headers
Oracle® Application Server TopLink Application Developer's Guide
10g Release 2 (10.1.2)
Part No. B15901-01
  Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Previous
Previous
Next
Next
 

Mapping EJB Entity Beans

EJB Entity beans represent a business entity. Entity beans can be shared by many users and are long-lived, able to survive a server failure. Essentially, entity beans are persistent data objects (objects with durable state that exist from one moment in time to the next).

This section describes entity bean development, as well as the following mapping topics and techniques:

Terminology and Definitions

Enterprise JavaBeans

An EJB implements a business task or a business entity. EJBs are server-side domain objects that fit into a component-based architecture for building enterprise applications using the Java language. EJBs are Java objects that the developer can install in an EJB server to make them distributed, transactional, and secure. OracleAS TopLink supports three kinds of EJBs under the EJB 2.0 specification: session beans, entity beans, and message-driven beans. Note that EJB 1.1 does not support message-driven beans.

EJB Server and Container

An EJB bean resides in an EJB container that, in turn, resides in an EJB server. Although the EJB 2.0 specification does not define the container-server relationship, the accepted paradigm is that the server provides the bean with access to different services (security, transactions, and so on), and the container provides the execution context for the bean by managing its life cycle.

Deployment Descriptors

Deployment descriptors supply additional information that is required to install an EJB within its server. The deployment descriptors are a set of XML files that provide the security, transaction, relationship, and persistence information for the bean.

Session Beans

Session beans represent a business task, process, or operation. Although the use of a session bean may involve database access, the beans are not in themselves persistent because they do not directly represent a database entry. Session beans do not always retain conversational state. They can be stateful and retain client information between calls; they can be stateless and retain information only within a single method call.

You can use OracleAS TopLink to make the regular Java objects that are accessed by a session bean persistent, or to access OracleAS TopLink persistent entity beans. Session beans may also act as wrappers to other legacy applications.

Entity Beans

Entity beans represent a persistent data object that exists from one access to the next. You accomplish persistence by storing the object in an object database, relational database, or some other storage facility.

Two schemes exist for making entity beans persistent: bean-managed persistence (BMP) and container-managed persistence (CMP). BMP requires the bean developer to hand-code the methods that perform the persistence work. CMP uses information supplied by the developer to handle all aspects of persistence.

Message-Driven Beans

Message-driven beans process asynchronous Java Message Service (JMS) messages. A bean method is transactionally-invoked by a JMS message sent to the objects registered against the given topic. From a client perspective, a message-driven bean is simply a JMS consumer with no conversational state and no Home or Remote interfaces.

Overview of Bean-Managed Persistence

OracleAS TopLink provides a class oracle.toplink.ejb.bmp.BMPEntityBase. This class provides you with a starting point when developing beans. The BMPEntityBase class provides implementation for all EJB specification-required methods except ejbPassivate(), which is excluded because of special requirements. By subclassing the BMPEntityBase, you have an entity bean enabled by OracleAS TopLink.

To use the BMPEntityBase, create the sessions.xml file. For information about the sessions.xml file, see "Session Manager". In addition, add an oracle.toplink.ejb.bmp.BMPWrapperPolicy to each descriptor that represents an Entity Bean. This BMPWrapperPolicy provides OracleAS TopLink with the information to create Remote objects for entity beans and to extract the data out of a Remote object. After this is performed, you must create the Home and Remote interfaces, create deployment descriptors, and deploy the beans.

If a more customized approach is required, OracleAS TopLink provides a hook into its functionality through the oracle.toplink.ejb.bmp.BMPDataStore class. Use this class to translate EJB-required functionality into simple calls.

The BMPDataStore provides implementations of LOAD and STORE, multiple finders, and REMOVE functionality. The BMPDataStore requires a sessions.xml file and the session manager. A single instance of BMPDataStore must exist for each bean type deployed within a session. When creating a BMPDataStore, pass in the session name of the session that the BMPDataStore must use to persist the beans and the class of the Bean type being persisted. Store the BMPDataStore in a global location so that each instance of a Bean type uses the correct Store.

If you use a customized implementation, the full functionality of the server session and the UnitOfWork is available.

BMP Support with EJB 2.0

To use BMP support with EJB 2.0, the Home interface must inherit from the oracle.toplink.ejb.EJB20Home. To make calls to the oracle.toplink.ejb.bmp.BMPEntityBase, the FindAll() method must call the EJB 2.0 version of the methods. These methods are prefixed with ejb20.

For example, in the EJB 2.0 version, the findAll() method appears as ejb20FindAll.

Using Local Beans

To use local beans, use the oracle.toplink.ejb.EJB20LocalHome setting instead of the default oracle.toplink.ejb.EJB20Home.

Instead of the oracle.toplink.ejb.BMPWrapperPolicy setting, use the oracle.toplink.ejb.bmp.BMPLocalWrapperPolicy setting.

To accommodate both local and remote configurations, ensure the following:

  • For a bean that has a single interface, use the corresponding wrapper policy (local or remote) for the descriptor.

  • Beans can only participate in relationships only as either Local or Remote interfaces—not both.

Overview of Container-Managed Persistence

OracleAS TopLink CMP is an extension of the OracleAS TopLink persistence framework. OracleAS TopLink CMP support provides container-managed persistence for EJBs deployed in a J2EE container.

OracleAS TopLink CMP support enables complex mappings from entity beans to relational database tables and enables you to model bean-to-bean and bean-to-regular Java object relationships. OracleAS TopLink provides a rich set of querying options and allows query definition at the bean-level, rather than the database level. OracleAS TopLink CMP supports the specification as defined by Sun Microsystems.

Understanding CMP

This section introduces the concepts required to use CMP facilities. It highlights the features that are specific to OracleAS TopLink CMP and explains any differences in the use of other core features.

OracleAS TopLink and CMP Entity Beans

The common mechanism for you to make beans persistent is to map beans to a relational database. The EJB specification describes the CMP entity bean as a type of bean for which the designer does not have to include calls to any particular persistence mechanism in the bean itself. The EJB Server and its tools use meta-information in the deployment descriptor to describe how the bean is to be persisted to a database. This function is commonly referred to as automatic persistence.

EJB 2.0 Support

OracleAS TopLink provides support for EJB 2.0 entity beans. Here are some specific features of EJB 2.0 that OracleAS TopLink supports:

  • Local interfaces and local relationships

  • Generation of concrete bean subclasses

  • EJB QL

  • Automatic management of bidirectional relationships

  • Initializing a project from the ejb-jar.xml file

  • Finders

  • Home methods

  • ejbSelect

Java Objects and Entity Beans

Table 3-3 describes the components that Java objects contain:

Table 3-3 Java Object Components

Component Function
Attributes Stores primitive data such as integers, as well as simple Java types such as String and Date.
Relationships Stores references to other OracleAS TopLink-enabled classes. An OracleAS TopLink-enabled class (also known as a persistent class) has a descriptor and can be stored in the database.
Methods Stores paths of execution that can be invoked in a Java environment. Methods are not stored in the database.

Table 3-4 illustrates the components that entity beans contain:

Table 3-4 Entity Bean Components

Component Function
Bean instance An instance of an entity bean class supplied by the bean developer. It is a regular Java object whose class implements the javax.ejb.EntityBean interface. The bean instance has persistent state. The client application must never access the bean instance directly.
EJBObject An instance of a generated class that implements the Remote interface defined by the bean developer. This instance wraps the bean and provides client interaction with the bean. The EJBObject does not have persistent state.
EJBHome An instance of a class that implements the Home interface supplied by the bean developer. This instance, accessible from JNDI, provides all create and finder methods for the EJB. The EJBHome does not have persistent state.
EJBLocalObject (EJB 2.0 only) An instance of a generated class that implements the Local interface defined by the bean developer. The key difference between an EJBLocalObject and an EJBObject is that the EJBLocalObject is accessed only from within the same server on which the beans are deployed. The EJBLocalObject does not have persistent state.
EJBLocalHome (EJB 2.0 only) An instance of a class that implements the localHome interface supplied by the bean developer. This instance, accessible from JNDI, provides all create and finder methods for the EJB. The key difference between an EJBLocalHome and an EJBHome is that access to the EJBLocalHome is available only from within the same server on which the beans are deployed, even when using JNDI. The EJBLocalHome does not have persistent state.
EJB Primary Key An instance of the primary key class provided by the bean developer. The primary key is a serializable object whose fields match the primary key fields in the bean instance. Although the EJB primary key shares some data with the bean instance, it does not have persistent state. If the key consists of a single field, the bean does not have to have a separate primary key class under the EJB 1.1 or later specifications.

For more information about the Enterprise JavaBeans and the EJB specification, see

http://java.sun.com/products/ejb/
http://java.sun.com/products/ejb/docs.html
http://java.sun.com/j2ee/white/index.html

Maintaining Bidirectional Relationships

When one-to-one or many-to-many mappings are bidirectional, you must maintain the back-pointers as the relationships change. When the relationship is between two entity beans (in EJB 2.0), OracleAS TopLink automatically maintains the relationship. However, when the relationship is between an entity bean and a Java object, or when the application is built to the EJB 1.1 specification, the relationship must be maintained manually. To set the back-pointer under the EJB 1.1 specification, do one of the following:

  • Establish or modify the relationship the entity bean can then maintain the back-pointer.

  • The client must explicitly set the back-pointer.

If you set back-pointers within the entity bean, the client is freed of this responsibility. This has the advantage of encapsulating the mapping maintenance implementation in the bean.


Note:

Under the EJB 1.1 specification, you must manually update all back-pointers.

One-to-Many Relationship

In a one-to-many mapping, a source bean may have several dependent target objects. For example, an EmployeeBean may have several dependent phoneNumbers. When a new dependent object (a phoneNumber, in this example) is added to an employee record, the phoneNumber's back-pointer to its owner (the employee) must also be set.

Example 3-26 Setting the Back-Pointer in the Entity Bean

To maintain a one-to-many relationship in the entity bean you must get the local object reference from the context of the EmployeeBean and then update the back-pointer. The following code illustrates this technique:

// obtain owner and phoneNumber
Employee owner = empHome.findByPrimaryKey(ownerId); 
PhoneNumber phoneNumber = new PhoneNumber("cell", "613", "5551212");
// add phoneNumber to the phoneNumbers of the owner
owner.addPhoneNumber(phoneNumber); 

The Employee's addPhoneNumber() method maintains the relationship, as follows:

public void addPhoneNumber(PhoneNumber newPhoneNumber) {
    //get, then set the back pointer to the owner
    Employee owner = (Employee)this.getEntityContext().getEJBLocalObject();
    newPhoneNumber.setOwner(owner);	
    //add new phone
    getPhoneNumbers().add(newPhoneNumber);
}

Managing Dependent Objects Under EJB 1.1

The EJB 1.1 specification recommends that you model entity beans so that all dependent objects are regular Java objects and not other entity beans. If you expose a dependent or privately owned object to the client application, it must be serializable (that is, it must implement the java.io.Serializable interface) so that it can be sent to the client and back to the server.

Serializing Java Objects Between Client and Server

Because entity beans are remote objects, they are referenced remotely in a pass-by-reference fashion. When an entity bean is returned to the client, a remote reference to the bean is returned.

Unlike entity beans, regular Java objects are not remote objects. Because of this, when regular Java objects are referenced remotely, they are passed by value (rather than by reference) and serialized (copied) from the remote system on which they originally resided.

Merging Changes to Regular Java Objects

One of the effects of serializing regular Java objects between servers and clients is a loss of object identity, due to the copying semantics inherent in serialization. When you serialize a dependent object from the server to the client and then back, two objects with the same primary key but different object identities exist in the server cache. These objects must be merged to avoid exceptions.

If relationships exist between entity beans and Java objects, and these objects are serialized back and forth between the client and server, either:

  • Use the OracleAS TopLink SessionAccessor utility class to perform the merge for you.

  • Merge the objects yourself by adding merge methods on your regular Java objects and within your set methods.

Using Session Accessor to Merge Dependent Objects

Use the class oracle.toplink.ejb.WebLogic.SessionAccessor to perform merges for you within the set methods (on your bean class) that take regular Java objects as their arguments.

Two static methods are defined on the SessionAccessor that allow you to perform the register and merge operation:

registerOrMergeObject()

This method requires two arguments: the object to merge and the EntityContext for the bean.

Example 3-27 Using the registerOrMergeObject() Method

public void setAddress(Address address) {
    this.address = (Address)SessionAccessor
      .registerOrMergeObject(address,this.ctx);
}

The registerOrMergeObject() method is not as simple to use for setters of collection mappings. It requires that you iterate through the collection and invoke the registerOrMergeObject() for each element in the collection. You must also create a new collection, set in the entity bean, to hold the return values of the call.

Merging code may be required in methods that add elements to a collection.

For example:

/* 
The old version of this phone number is removed from the collection. It is assumed that equals() returns true for phones with the same primary key value. If this is not true, you must iterated through the phones to see if a phone with the same primary key already exists in the collection. 
*/
public void addPhoneNumber(PhoneNumber phone) {
    phone.setOwner((Employee)this.ctx.getEJBObject());
    //add to collection
    //merge new phone
    PhoneNumber serverSidePhone =         (PhoneNumber)SessionAccessor.registerOrMergeObject(phone,this.ctx);
    //set back pointer
    getPhoneNumbers().addElement(serverSidePhone);
}

registerOrMergeAttribute()

This method requires three arguments: the Java object to be merged, the name of the attribute, and the EntityContext for the bean.

Example 3-28 Using the registerOrMergerAttribute() Method

public void setAddress(Address address) {
    this.address = (Address) SessionAccessor.registerOrMergeAttribute
      (address, ÒaddressÓ, this.ctx);
}

To use the registerOrMergeAttribute() call for collection mappings, pass the entire collection as the attribute object.

For example:

public void setPhones(Vector phones) {
    this.phones = (Vector)SessionAccessor.registerOrMergeAttribute(phones,
      "phones", this.ctx);
    //... additional logic to set back-pointers on the phones
}


Note:

This example requires merging code only if there is a risk that a Phone with the same primary key can be added twice. If the elements in a collection cannot be added more than once, then merging code is not required.

Merging Dependent Objects without Session Accessor

There are several ways to merge objects manually. For example, you can use a set() method, as follows:

public void setAddress(Address address) {
    if(this.address == null){
        this.address = address;
    } else{
        this.address.merge(address);
    }
}

You must merge objects when they are added to a collection on the entity bean unless the objects cannot be added more than once to a collection, in which case merging is not necessary.

Merging a collection requires more work. Determine if a copy of each object already exists in the collection, and if so, merge the two copies. If not, you need only add the new object to the collection.

Managing Dependent Objects Under EJB 2.0

Unlike EJBs, OracleAS TopLink dependent persistent objects can be sent back and forth between a client and the server. When objects are serialized, the risk exists that the objects can cause the cache to lose the identity of the objects or attempt to cache duplicate identical objects. To avoid potential problems, use the bean set methods when adding dependent objects to relationship collections. This enables OracleAS TopLink to handle merging of objects in the cache.

Example 3-29 Adding a Dependent Object

addPhoneNumber(PhoneNumber phone) { 
    Collection phones = this.getPhoneNumbers(); 
    Vector newCollection = new Vector();
    newCollection.addAll(phones); 
    newCollection.add(phone); 
    this.setPhones(newCollection); 
}

Managing Collections of EJBObjects Under EJB 1.1

Collections generally use the equals() method to compare objects. However, in the case of a Java object that contains a collection of entities, the EJBObjects do not respond as expected to the equals() method. If you manage a collection of entities under EJB 1.1, we recommend the use of the isIdentical() method to avoid problems.

In addition, the standard collection methods, such as remove() or contains(), frequently return unexpected results and so must be avoided.


Note:

The issue of collection of EJBObjects does not arise in the case of an entity that contains a collection of entities, because the EJB 2.0 container collection used handles equality appropriately.

Several options are available when dealing with collections of EJBObjects. One option is to create a helper class to assist with collection-type operations. Example 3-30 shows the use of a helper in the EJBCollectionHelper distribution.

Example 3-30 Using a Helper Class to Manage a Collection of EJBObjects

public void removeOwner(Employee previousOwner){ 
    EJBCollectionHelper.remove(previousOwner, getOwners());
} 

Example 3-31 illustrates the implementation of remove() and indexOf() in EJBCollectionHelper.

Example 3-31 Using remove() and indexOf() in the EJBCollectionHelper

public static boolean remove(javax.ejb.EJBObject ejbObject, Vector vector) { 
    int index = -1; 
    index = indexOf(ejbObject, vector); 
    // indexOf returns -1 if the element is not found. 
    if(index == -1){ 
        return false; 
    } 
    try{ 
        vector.removeElementAt(index); 
    } catch(ArrayIndexOutOfBoundsException badIndex){ 
        return false; 
    } 
    return true; 
} 
public static int indexOf(javax.ejb.EJBObject ejbObject, Vector vector) { 
    Enumeration elements = vector.elements(); 
    boolean found = false; 
    int index = 0; 
    javax.ejb.EJBObject current = null; 
    while(elements.hasMoreElements()){ 
        try{ 
            current = (javax.ejb.EJBObject) 
            elements.nextElement(); 
            if(ejbObject.isIdentical(current)){ 
            found = true; 
            break; 
            } 
        }catch(ClassCastException wrongTypeOfElement){ 
            . . . 
        }catch (java.rmi.RemoteException otherError){ 
            . . . 
        } 
        index++; //increment index counter 
    } 
    if(found){ 
        return index; 
    } else{ 
        return -1; 
    } 
} 

You can create a special Collection class that uses isIdentical() instead of equals() for its comparison operations. To use isIdentical(), properly define the equals() method for the primary key class.