Skip Headers
Oracle TopLink Developer's Guide
10g Release 3 (10.1.3)
B13593-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
 

Understanding the EJB Entity Beans with CMP Architecture

CMP is the part of the J2EE component model that provides an object persistence service that an EJB container uses to persist entity beans. CMP provides distributed, transactional, secure access to persistent data, with a guaranteed portable interface.

This architecture is an extension of the three-tier architecture, in which the implementation of persistence methods is handled by the container at runtime. As a bean provider, you only need to specify in a deployment descriptor (or, in EJB 3.0, an annotation), those persistent fields and relationships for which the container must handle data access and, optionally, an abstract representation of the database schema.

TopLink CMP is an extension of the TopLink persistence framework that provides custom integration to the CMP containers of various application servers (see "Application Server Support"). For more information about choosing an application server, see "Understanding Target Platforms". TopLink integrates with the CMP container in this architecture to augment (or, in the case of OC4J, become) the container's persistence manager. In this release, when using OC4J and Java 1.5, TopLink supports a subset of the CMP features anticipated in the final EJB 3.0 specification. For more information on EJB 3.0 support, see Oracle Containers for J2EE Enterprise JavaBeans Developer's Guide.


Note:

EJB 3.0 feature support is subject to change and dependent upon the contents of the final specification.

TopLink CMP integration is nonintrusive (see Figure 2-6). Through a combination of run-time integration and code generation, the container uses TopLink internally and the bean user interacts with CMP entity beans according to their standard API. This lets you combine the standard interfaces and power of CMP and a container, with TopLink flexibility, performance, and productivity.

Figure 2-6 Three-Tier CMP Architecture

Description of Figure 2-6  follows
Description of "Figure 2-6 Three-Tier CMP Architecture"

For more information, see the following:

Example Implementation

An example of the EJB entity beans with CMP implementation is a Model-View-Controller Model 2 architectural design pattern that runs in a J2EE container, with servlets and JSP that access either session beans or EJB 2.0-compliant CMP entity beans enhanced by TopLink.

Advantages and Disadvantages

A three-tier architecture using EJB entity beans with CMP offers the following advantages:

  • It allows for CMP beans with sophisticated TopLink features such as caching and mapping support, storing bean data across more than one table, composite primary keys, and data conversion.

  • It presents a standard method to access data, which lets developers create standardized, reusable business objects.

  • It is well-suited to create coarse-grained objects, which TopLink relates to dependent, lightweight, regular Java objects (TopLink can also manage CMP relationships to light-weight dependent Java objects).

  • TopLink provides for lazy initialization of referenced objects and beans (see "Indirection").

  • TopLink provides functionality for transactional copies of beans, allowing concurrent access by several clients, rather than relying on individual serialization.

  • TopLink provides advanced query capabilities, as well as dynamic querying, including the ability to define queries at the bean-level rather than the data source level and to use a rich set of querying and finder options.

  • TopLink maintains bean and object identity.

The disadvantage of this architecture is that pure CMP entity bean architectures can impose a high overhead cost. This is especially true when a data model has a large number of fine-grained classes with complex relationships.

Technical Challenges

The key technical challenge in this architecture lies in integrating components into a cohesive system. For example, this architecture requires a specific TopLink integration with the application server or J2EE container.

Other issues include the following:

External JDBC Pools

By default, TopLink manages its own connection pools. You can also configure TopLink to use connection pooling offered by the host application server. This feature is useful for shared connection pools and is required for JTA/JTS integration (see "Configuring External Connection Pooling").

JTA/JTS Integration

JTA and JTS are standard Java components that enable sessions to participate in distributed transactions. You must configure TopLink to use JTA/JTS to use session beans in the architecture (see "Integrating the Unit of Work With an External Transaction Service").

Cache Coordination

If you choose to use multiple servers to scale your application, you may require TopLink cache coordination (see "Understanding Cache Coordination").

Maintaining Bidirectional Relationships

When one-to-one or many-to-many relationship is bidirectional, you must maintain the back pointers as the relationships change.

When the relationship is between two EJB 2.0 entity beans, TopLink automatically maintains the relationship.

When the relationship is between two EJB 1.1 entity beans or between an entity bean and a regular Java object, you must maintain the back pointers manually.

To set the back pointer manually, do one of the following:

  • Code the entity bean to maintain the back pointer when the relationship is established or modified (recommended).

  • Code the client to explicitly set the back pointer.

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

In a one-to-many relationship, a source bean might have several dependent target objects. For example, an EmployeeBean might own several dependent PhoneNumber instances. When you add a new dependent object (a PhoneNumber, in this example) to an employee, you must set the PhoneNumber instance's back pointer to its owner (the employee). Maintaining a one-to-many relationship in the entity bean involves getting the local object reference from the context of the EmployeeBean, and then updating the back pointer as Example 2-1 shows.

Example 2-1 Setting the Back-Pointer in the Entity Bean

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

// Maintain the relationship in the Employee's addPhoneNumber method 
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 in 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 implements the java.io.Serializable interface) so that it can be sent over to the client and back to the 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. As a result, when regular Java objects are referenced remotely, they are passed by value (rather than by reference) and serialized (copied) from the remote machine on which they originally resided.

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, consider the following:

Merging Dependent Objects With the Session Accessor

Use the oracle.toplink.ejb.cmp.SessionAccessor to perform the merge for you within your bean class's setter methods that take regular Java objects as their arguments.

The SessionAccessor provides the following static methods you can use to merge dependent objects:

  • registerOrMergeObject–Merge the changes from the remote clone into the server version of the object for a bean's attribute. The object should not be a collection.

  • registerOrMergeAttribute–Merge the changes from the remote clone into the server version of the object for a bean's attribute. The object may be single object or a collection.

Example 2-2shows how to use the registerOrMergeObject method for a noncollection attribute.

Example 2-2 Using registerOrMergeObject for a Noncollection Attribute

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

Example 2-3 shows how to use the registerOrMergeAttribute method for a noncollection attribute

Example 2-3 Using registerOrMergeAttribute for a Non-Collection Attribute

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

Example 2-4 shows how to use the registerOrMergeAttribute method for a collection.

Example 2-4 Using registerOrMergeAttribute for a Collection Attribute

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

Merging code may be required in methods that add elements to a collection. In Example 2-5, because there is a risk that a phone with the same primary key can be added twice, your code must merge. If the elements in the collection cannot be added more than once, then merging code is not required.

Example 2-5 Using registerOrMergeObject for a Method that Adds to a Collection

/* 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 iterate 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());
    // merge new phone
    PhoneNumber serverSidePhone = (PhoneNumber)SessionAccessor.registerOrMergeObject(
        phone,
        this.ctx
    );
    getPhoneNumbers().addElement(serverSidePhone);
}

Merging Dependent Objects Without the Session Accessor

Alternatively, you can merge the objects yourself by adding merge methods on your regular Java objects and within your set methods, as Example 2-6 shows.

Example 2-6 Manually Merging Dependent Objects

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 in EJB 2.0

Unlike EJB, TopLink dependent persistent objects can be sent back and forth between a client and the server. When objects are serialized, the risk exists 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 as Example 2-7 shows. This enables TopLink to handle merging of objects in the cache.

Example 2-7 Managing Dependent Objects in EJB 2.0

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

Managing Collections of EJBObjects in 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 EJB 1.1 entities, the EJBObject method equals does not yield the desired results. If you manage a collection of entities under EJB 1.1, Oracle recommends 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.

Several options are available when dealing with collections of EJBObjects in EJB 1.1. One option is to create a helper class to assist with collection-type operations. Example 2-8 shows the use of a helper called EJBCollectionHelper and Example 2-9 shows a partial implementation of this helper. Alternatively, you can create a special Collection class that uses isIdentical method instead of equals for its comparison operations. To use isIdentical method, properly define the equals method for the primary key class.

Example 2-8 Using the EJBCollectionHelper in EJB 1.1

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

Example 2-9 EJBCollectionHelper Implementation

...
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; 
    } 
} 
...

Managing Collections of EJBObjects in EJB 2.0

Collections generally use the equals method to compare objects. When using EJB 2.0 this is not a problem in the case of an entity that contains a collection of entities, because the EJB 2.0 container collection handles equality appropriately.