Sun Java Solaris Communities My SDN Account Join SDN
 
Article

EJB 2.0 Container-Managed Persistence Example

 
 

Articles Index



The previous article Enterprise JavaBean 2.0 Specification Changes outlined the changes that the new EJB 2.0 specification brings to the EJB 1.0 and 1.1 architectures. Not only has the EJB 2.0 specification introduced the EJB Query Language and message-driven beans, it also has made improvements to session and entity beans, particularly with regards to

  • Container-managed persistence (CMP)
  • Container-managed relationships (CMR)

The EJB 2.0 approach to CMP allows multiple entity beans to have container-managed relationships among themselves. It accomplishes this by introducing a local model which uses local interfaces, in addition to a remote model that uses remote interfaces. The CMP local model can be more tightly coupled than the remote model, and it avoids the performance overhead of the remote model. It is also closer to the Java programming model, particularly in regards to passing parameters between enterprise beans.

This article begins with a review of the EJB 2.0 CMP changes noted in the earlier article. It then shows you how to apply the new local model and to implement session and entity beans that use CMP and CMR. You'll see how to define different kinds of relationships between beans and then use these beans in your code. To illustrate the CMP concepts, you'll look at an example supplied with the latest release of the Java 2, Enterprise Edition (J2EE) Reference Implementation to see how to turn these new enterprise bean concepts into usable code.

It is expected that you are familiar with the earlier versions of the EJB architecture and understand how to write session and entity beans that conform to the earlier specifications. (If you are new to the EJB world, refer to the J2EE tutorial and the Java Developer Connection's Tech Center for getting started with J2EE and enterprise beans.)

Introducing New Features

Prior to the EJB 2.0 specification, a client stored and accessed persistent data via an entity bean's instance variables. With the introduction of the 2.0 specification, you can designate instance variables to be container-managed persistence fields (cmp fields) or container-managed relationship fields (cmr fields). You define these cmp and cmr fields in the deployment descriptor.

You retrieve and set the values of these cmp and cmr fields using public get and set methods defined in an entity bean. Similar to the JavaBeans model, you do not access the instance variables directly, but instead use the entity bean's get and set methods to retrieve and set these instance variables. (An enterprise bean does not declare these instance variables.)

Furthermore, you use the deployment descriptor to specify the relationships between entity beans. These relationship specifications serve as the schema definition, so that when the bean is deployed, the bean relationships may be captured in a relational database. For example, a relationship between two beans specified in the deployment descriptor may appear as a foreign key relationship in a relational database.

Container-Managed Relationships

Container-managed relationships, represented by the cmr fields in the deployment descriptor, support these relationships:

  • One-to-one
  • One-to-many
  • Many-to-many

The entity bean uses a Java Collection to represent the "many" side of a relationship. The single side is represented by the local bean object.

The EJB container maintains the referential integrity of these entity bean relationships. For example, in a one-to-one relationship, if you change one side of the relationship, the container automatically drops the old relationship and replaces it with the newly formed one-to-one relationship. You can also specify a "cascade delete" option, whereby when you delete one object, the container also deletes all of that object's dependent objects.

Local and Remote Interfaces

Session beans and entity beans in 2.0 can have two types of interfaces:

  • Remote interface
  • Local interface

Although a bean can have both a local and a remote client view, typically a single enterprise bean provides only one of these two views.

The local interfaces for session and entity beans enable the beans to be tightly coupled with their clients and to use pass-by-reference (rather than pass-by-value) semantics. The local interface is a standard Java interface that does not inherit from RMI. A bean uses the local interface to expose its methods to other beans that reside within the same container. It is thus possible to directly access a bean through its local interface without the overhead of a remote method call.

Local interfaces provide the foundation for container-managed relationships among entity beans and session beans. The bean uses the local interface to maintain its references to other beans. For example, an entity bean uses its local interfaces to maintain relationships to other entity beans. Using local interfaces, beans can also expose their state and use pass-by-reference to pass their state between related bean instances.

A bean that needs to have remote capabilities--the bean inherits from RMI and interacts with distributed clients--uses a remote interface. (All pre-2.0 enterprise beans had to use the remote interface model.) A bean uses its remote interface to expose its methods across the network tier.

CMP Example Overview

You can see the 2.0 implementation of CMP with an example that uses three entity beans (Customer, Address, and Subscription) to model an application that maintains customers, their addresses, and their subscriptions to different publications. Customers may have multiple addresses and may subscribe to multiple publications. Furthermore, publications are classified according to type, such as magazines, newspapers, journals, and so forth.

In this example application, the relationship between customers and addresses is a one-to-many, uni-directional aggregation.

  • Each customer is represented by an instance of the Customer bean.
    A customer may have one or more addresses.
  • Each address is represented by an instance of an Address bean and an Address instance must be associated to one particular customer.
    You may add and delete individual addresses for a customer, but you cannot add an address that is not connected to a customer.

Because this application requires that every address instance be associated to a customer, the customer-address relationship is set up such that when you delete a customer, you also automatically delete all addresses associated to that customer. This illustrates the EJB architecture's cascade delete functionality because it automatically ensures that all address instances associated with a customer instance are deleted when the customer instance is removed.

There is also a many-to-many, bi-directional association between the Customer bean and the Subscription bean. A customer may subscribe to zero or more publications, where each Subscription instance represents a different publication. Similarly, the same publication may be subscribed to by many customers, so a single Subscription instance may be associated to many customers.

Lastly, each subscription instance has an attribute that indicates the type of the publication, such as a magazine or journal. This application requires that when you create a new subscription instance, you designate its type. The subscription type is a separate, dependent value class (SubscriptionType.java) that is not an enterprise bean. The Subscription bean references this separate class as a container-managed persistence field. See Figure 1.

Figure 1 Example Architecture
 

Example Code

Now examine the code for the three example entity beans, which focuses on the new features of the EJB 2.0 architecture.

Using Enterprise Bean Local Interfaces

All three entity beans in this application use the new local client view interfaces available with the CMP 2.0 enhancements. To see how this local model works, look at the customer bean implementation class and its local interfaces.

Implementation Class

The EJB 2.0 CustomerBean implementation class is quite similar to the EJB 1.0 and 1.1 versions, but there are a few important differences. To begin with, the CustomerBean implementation class is an abstract class:

public abstract class
 CustomerBean 
 implements EntityBean {

Keep in mind that to have container-managed relationships between entity (and session) beans, the beans must reside in the same EJB container and use the local interface capability rather than the remote interface capability. However, notice that the implementation class does not directly use the local interface. As in the earlier EJB versions, a session bean implementation class implements SessionBean, while an entity bean implementation class implements EntityBean.

CustomerBean uses three cmp fields: id, firstName, and lastName. In the 2.0 EJB implementation, the cmp fields are no longer declared as public variables and accessed directly. In fact, the implementation class includes no declarations of these fields. Instead, the bean implementation class defines get and set methods for each public variable.

For example, the CustomerBean defines get and set methods for its three cmp fields:

        ...
        //access methods for cmp fields
        public abstract String getCustomerID(
        );      //primary key
        public abstract void setCustomerID(
        String id);
        public abstract String getFirstName();
        public abstract void setFirstName(
        String firstName);
        public abstract String getLastName();
        public abstract void setLastName(
        String lastName);
  ...

Notice that these get and set methods are declared public and abstract in the bean's implementation class. Also, the bean provides no implementation for these methods. Instead, the EJB container provides the method implementations.

CustomerBean includes two container-managed relationship fields, or cmr fields. Once again, rather than declaring these fields as variables, CustomerBean defines get and set methods for its two cmr fields, addresses and subscriptions. These cmr fields represent the relationships that the Customer bean has to the two other entity beans, Address and Subscription. Notice that the two get methods, getAddresses and getSubscriptions, both return Collection (java.util.Collection) types, while the two set methods take Collection types as parameters. Remember that the many side of a relationship is represented by a Collection.

        ...
        //access methods for cmr fields
        public abstract Collection getAddresses(
        );
        public abstract void setAddresses (
        Collection addresses);
        public abstract Collection getSubscriptions(
        );
        public abstract void setSubscriptions (
        Collection
                subscriptions);
  ...

CustomerBean next defines its business logic. You define business methods just as you did for beans developed under the earlier specifications. Keep in mind, however, that the methods now can access the local interface of another entity or session bean. To illustrate, here are three of CustomerBean's business methods:

...
public ArrayList getAddressList() {
        ArrayList list = new ArrayList();
        Iterator c = getAddresses().iterator();
        while (c.hasNext()) {
                list.add(
                (LocalAddress)c.next());
        }
        return list;
}
public void addAddress (LocalAddress address) {
        getAddresses().add(address);
}
public void addSubscription (
LocalSubscription subscription) {
        getSubscriptions().add(subscription);
}
...

Notice that the addSubscription method expects as a parameter a single subscription object whose type is LocalSubscription. LocalSubscription is the local interface for SubscriptionBean, just as LocalCustomer is the local interface for CustomerBean.

CustomerBean also includes the other entity bean methods, such as ejbActivate, ejbPassivate, ejbLoad, ejbStore, and so forth, just as it did in earlier versions.

Local Home Interface

To implement the local model rather than the remote model, the Customer entity bean home interface extends the EJBLocalHome interface instead of the EJBHome interface. The create and find methods return either a reference to a single LocalCustomer object or to a Collection of LocalCustomer objects. (In the remote model, these create and find methods would return references to Customer objects.)

As with the earlier EJB versions, the home interface includes the finder methods particular for this bean. The home interface declares the finder methods and the deployment descriptor contains the actual definition or implementation (the SQL statements) for these finder methods. Notice, too, that the CustomerBean implementation does not declare ejb <find> methods to correspond to the find methods declared in the local home interface.

Here's the LocalCustomerHome home interface for the CustomerBean:

import java.util.Collection;
import javax.ejb.CreateException;
import javax.ejb.EJBLocalHome;
import javax.ejb.FinderException;
public interface LocalCustomerHome 
extends EJBLocalHome {
        public LocalCustomer create (
        String customerID,
                        String firstName, 
                        String lastName)
                        throws CreateException;
        public Collection findByLastName (
        String lastName)
                        throws FinderException;
        public Collection findByFirstName (
        String firstName)
                        throws FinderException;
        public LocalCustomer findByPrimaryKey (
        String customerID)
                        throws FinderException;
}

Local Interface

The Customer bean also defines a local interface. Clients residing on the same container can access the bean's methods through this local interface. Notice that, because the local model is implemented in the bean's LocalCustomer interface, it extends the EJBLocalObject interface rather than the EJBObject interface.

import java.util.ArrayList;
import javax.ejb.EJBLocalObject;

public interface LocalCustomer
 extends EJBLocalObject {
        public String getCustomerID();
        public String getFirstName();
        public String getLastName();
        public ArrayList getAddressList();
        public ArrayList getSubscriptionList();
        public void addAddress (
        LocalAddress address);
        public void addSubscription (
        LocalSubscription subscription);
        public void addSubscription (
        String subscriptionKey);
        public void removeSubscription(
        String subscriptionKey);
}

Keep in mind that the local interface exposes those enterprise bean methods that you want to make public to your application's clients. This means you can provide local interface get methods to a subset of a bean's variables so that a client's access is limited to only certain variables. Or, you can restrict the variables that can be changed by limiting the set methods in the local interface.

For example, the LocalCustomer interface exposes only the get methods for its three cmp fields: getCustomerID to retrieve id, getFirstName to get firstName, and getLastName for lastName. Even though the CustomerBean implementation defines set methods for these three fields, a client of LocalCustomer cannot access those set methods.

Defining the Bean Relationships

The deployment descriptor contains the enterprise bean relationship definitions. It is recommended that you use the deployment tool bundled with your particular application server to establish these relationship definitions. This example uses the J2EE Reference Implementation's deploytool to establish these relationships and to generate the XML for the deployment descriptor. Though it is not advised, you could also enter the XML tags directly into the deployment descriptor.

When you write your own application that uses enterprise beans, you

  1. Compile the application's enterprise beans
  2. Using the deploytool, designate both the cmp and cmr fields, the bean-to-bean relationships, and the EJB QL code for the finder methods.
    It is important that you follow what has been done in the enterprise bean code when you make these designations. That is, the mappings for the deployment descriptor must mirror the code. Otherwise, your application may not run properly.
  3. Eventually, the deployment descriptor is packaged into a JAR file along with the enterprise beans, and ultimately into an application .ear file.

Later, when you use a tool such as deploytool to open the packaged application, the tool reads the deployment descriptor and shows you all these defined relationship mappings, container-managed persistence fields, and so forth. You can make further changes to these associations.

Defining the Relationships in the Code

In the code, a local bean object represents the single side of a one-to-many relationship. A Java Collection of local objects represents the "many" side. For example, the CustomerBean defines the method getAddresses to retrieve all the addresses for one customer and the method setAddresses to set all addresses:

public abstract Collection getAddresses();
public abstract void setAddresses (
Collection addresses);

Because the Customer to Address relationship is only in one direction, the Address bean does not define a method to get an address instance's associated customer.

To illustrate how methods represent the single side of a one-to-many relationship, assume for a moment that the Address bean does have a bi-directional relationship to the Customer bean. If so, the Address bean might include methods to retrieve and set the customer for an address. Such methods would return or pass as a parameter a single Customer object rather than a collection of Customer objects, because an Address object may only have one customer. The methods might look as follows:

public abstract Customer getCustomer();
public abstract void setCustomer (
Customer customer);

The Customer bean has a many-to-many, bi-directional relationship to the Subscription bean. So, the Customer bean includes two methods: one to return all subscriptions associated to a single customer and the other to update a customer's subscriptions:

public abstract Collection getSubscriptions();
public abstract void setSubscriptions (
Collection subscriptions);

The Subscription bean includes equivalent methods to retrieve and set all customers associated to a particular subscription instance:

public abstract Collection getCustomers();
public abstract void setCustomers(
Collection customers);

As noted earlier, the SubscriptionBean also contains a reference to the dependent value class SubscriptionType. SubscriptionType is a Java class that extends the Serializable interface, but it is not an enterprise bean. (See SubscriptionType.java) SubscriptionBean references this object as a container-managed persistence field, as follows:

public abstract class SubscriptionBean
implements EntityBean {
        //access methods for cmp fields
        ...
        public abstract SubscriptionType
        getType();
        public abstract void setType(
        SubscriptionType type);
        ...

Defining the Relationships at Deployment

These bean-to-bean relationships must also be defined in the deployment descriptor. To define them using the J2EE Reference Implementation deploytool, you

  1. Select the application's JAR file containing the enterprise beans.
  2. Select the Relationship tab.

The Relationship tab screen lets you map one bean to another bean and designate the fields within each bean that reference the other bean.

The deploytool displays the container-managed relationships currently defined for the beans within this JAR file. You can edit these relationships or add new relationships. To add a new relationship, you

  1. Select bean A and bean B from the list of possible beans.
  2. Choose the multiplicity level between bean A and bean B.
  3. You can choose one-to-one, one-to-many, many-to-one, or many-to-many relationships.
    Designate the field within bean A that holds the reference to bean B and, likewise, the field within bean B that holds the reference to bean A.

You can edit an existing relationship by highlighting the relationship, then select edit. Editing a relationship is much the same as adding a new relationship.

Defining a Many-to-Many Relationship

The Customer bean to Subscription bean is a good example of defining a many-to-many relationship.

  1. Select Many to Many from the Multiplicity pull-down menu.
  2. Select CustomerBean for enterprise bean A.
    Because CustomerBean uses the subscriptions field to hold its references to the Subscription bean (see CustomerBean.java), you choose subscriptions from the list of fields for its Field Referencing Bean.
  3. Select SubscriptionBean for enterprise bean B.

According to SubscriptionBean's code, it uses the field customers to hold its references to the Customer bean, so choose customers for its Field Referencing Bean A. (See SubscriptionBean.java.)

Defining One-to-Many Relationships

To set up the one-to-many relationship between the Customer bean and the Address bean

  1. Select One to Many from the Multiplicity pull-down menu.
  2. For Enterprise Bean A, select CustomerBean.
  3. For its Field Referencing Bean B, choose addresses from the pull-down menu.
  4. For Enterprise Bean B, select AddressBean from the list of beans.
  5. For its Field Referencing Bean A, choose none (because AddressBean does not contain any references to the Customer bean).

Setting the Cascade Delete Functionality

You can set cascade delete functionality from the same Relationship screen. Beneath bean B, notice that you can check a box for the option to delete bean B when bean A is deleted. When you check this box, you are instructing the container to delete all instances of bean B when an application deletes bean A.

Because an Address object should only exist when the customer that references it exists, you want to ensure that, when you delete a customer, you also delete all addresses associated to that customer. Check the option to delete bean B for AddressBean in the Customer to Address bean one-to-many relationship. Now, when the application deletes an instance of a Customer bean, the container ensures that all instances of the Address bean that are associated to the deleted Customer object are also deleted.

Setting CMP Fields

You must also be sure that the deployment descriptor correctly indicates the cmp fields for the entity beans using container-managed persistence. In the J2EE Reference Implementation deploytool,

  1. Use the Entity tab for a selected entity bean.
  2. From the Entity tab screen, check the type of persistence management for the bean:
    • Bean-managed persistence
    • Container-managed persistence (1.0)
    • Container-managed persistence (2.0)
  3. Check the bean fields that you want persisted.

For example, the SubscriptionBean uses container-managed persistence (2.0) and relies on the container to persist two of its three fields: type and title. Check these two fields so that they correspond to the cmp fields for which the SubscriptionBean provides get and set methods.

The CustomerBean uses container-managed persistence (2.0). It also uses container-manager persistence for three of its five fields: customerID, firstName, and lastName. These three fields should also be checked to correspond to the cmp fields for which the CustomerBean provides get and set methods.

Conclusion

This article:

  • Showed you how to translate the container-managed persistence changes introduced in the EJB 2.0 specification into usable enterprise bean code.
  • Focused on using the new local client view model to set up multiple entity beans that have container-managed relationships among themselves.
  • Demonstrated how to write enterprise beans that use the local interfaces that make up the local model.
  • Showed how to correctly code the bean methods to reference other beans and to use the container-managed persistence features.
  • Explained how, along with writing the code, the deployment descriptor must mirror what has been done in the bean code.
  • Illustrated, using the J2EE Reference Implementation, how these container-managed relationships and persistence fields are reflected in the deployment descriptor.

For More Information

For further information, refer to the EJB 2.0 specification, the J2EE tutorial, and the documentation that comes with the J2EE Reference Implementation 1.3 beta release. Related downloads are available:

Reference Implementation 1.3 release

The EJB 2.0 specification and the J2EE tutorial

Follow these links to view the code for the implementation classes and local interfaces of the three enterprise beans and the dependent value class used in this example:

CustomerBean.java

LocalCustomer.java

LocalCustomerHome.java

AddressBean.java

LocalAddress.java

LocalAddressHome.java

SubscriptionBean.java

LocalSubscription.java

LocalSubscriptionHome.java

SubscriptionType.java

About the Author

Beth Stearns is the principal partner of ComputerEase Publishing, a computer consulting firm she founded in 1982. Her client list includes Sun Microsystems, Inc., Silicon Graphics, Inc., Oracle Corporation, and Xerox Corporation. Among her publications are the "Java Native Interface" chapter in "The Java Tutorial Continued" book in the Addison Wesley Java series, "The EJB Programming Guide" for Inprise Corporation, and "Understanding EDT", a guide to Digital Equipment Corporation's text editor. Most recently, she co-authored with Vlada Matena of the Addison Wesley Java series book, "Applying Enterprise JavaBeans: Component-Based Development for the J2EE Platform."

Have a question about programming? Use Java Online Support.

Back to top