Skip Headers

Oracle TopLink Developer's Guide 10g Release 3 (10.1.3) B13593-01 |
| Home | Solution Area | Contents | Index

Previous | 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 Three-Tier CMP Architecture"

For more information, see the following:

- "Integrating TopLink With an Application Server ``"

- "Creating TopLink Files for Deployment"

- "Packaging a TopLink Application"

- "Deploying a TopLink Application"

- "Configuring Persistence Type"

## 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

- JTA/JTS Integration

- Cache Coordination

- Maintaining Bidirectional Relationships

- Managing Dependent Objects in EJB 1.1

- Managing Dependent Objects in EJB 2.0

- Managing Collections of EJBObjects in EJB 1.1

- Managing Collections of EJBObjects in EJB 2.0

### 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

- Merging Dependent Objects Without the Session Accessor

#### 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.