|
Articles Index
By Beth Stearns
July 2001
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
- Compile the application's enterprise beans
- 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.
- 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
- Select the application's JAR file containing the enterprise beans.
- 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
- Select bean A and bean B from the list of possible beans.
- Choose the multiplicity level between bean A and bean B.
- 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.
- Select Many to Many from the Multiplicity pull-down menu.
- 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.
- 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
- Select One to Many from the Multiplicity pull-down menu.
- For Enterprise Bean A, select
CustomerBean.
- For its Field Referencing Bean B, choose addresses from the pull-down menu.
- For Enterprise Bean B, select
AddressBean from the list of beans.
- 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,
- Use the
Entity tab for a selected entity bean.
- 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)
- 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
|