![]() |
|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
|
![]() |
![]() |
SummaryBy Allen Fogleson
Enterprise JavaBeans (EJB) 2.0's Container-Managed Persistence (CMP) specification allows for fine-grained control over entity bean relationships. The container can also persist these relationships instead of having the bean author control them. In this article, Allen Fogleson explains and demonstrates how to use such relationships in CMP 2 entity beans. (3,000 words; April 19, 2002)
ava developers often create complex relationships when they program
applications to solve real-world business problems. Therefore, it was no
surprise that when programmers began developing enterprise applications using
Enterprise JavaBeans (EJB), they continued to describe their business models
with complex relationships. In the original specification, EJBs, however, made
such complex relationships more difficult because developers had little control
over when an EJB was activated or passivated. In response, developers often
wrote complex logic to ensure object persistence within these relationships.
Luckily, the EJB specification developers, recognizing the problem, added container-managed relationships (CMR) in EJB 2.0.
What are these relationships? How do you describe them? How do you encode them in your Java classes? In this article I answer those questions and others.
Note: I assume you understand Java and EJB. Although I give a CMP 2 (container-manage persistence) overview, you won't find an exhaustive CMP tutorial here. See Resources for more articles on EJB and CMP.
Also note: You can download this article's code examples from Resources.
CMP 2 overview
Before you can
understand EJB relationships, you must first understand how EJBs work. How are
their persistent fields defined? How do you define the relationships? What are
the relationships' return objects?
The EJB 1.0 and 1.1 specifications identified container-managed fields by
placing attributes in the bean, then described the fields in the deployment
descriptor. Although other entity beans could reside within the parent entity
bean, the container did not automatically handle their persistence or loading.
The bean developer, therefore, had to write additional code, normally in the
ejbCreate()
, ejbActivate()
, and
ejbPassivate()
methods to handle these additional entity beans. The
developer, as his only alternative, could define entity beans using bean-managed
persistence.
The EJB 2.0 specification changed the situation dramatically. Among the
specification's many changes, container-managed fields now no longer have
attributes to identify them within the bean; instead they have abstract getters
and setters to identify them. Using the getters and setters, you can now also
include other entity beans. The getters' return types depend upon the
relationships type. If a single child-bean instance returns, the instance is the
child bean's local interface. In contrast, if multiple child-bean instances
return, the instances are a java.util.Collection
or a
java.util.Set
. The bean developer can then describe the
relationship, defined within the deployment descriptor's relationship tag, in
the EJB's deployment descriptor. Here's an example:
...
<relationships>
<ejb-relation>
<ejb-relation-name>User-Demographics</ejb-relation-name>
<ejb-relationship-role>
<ejb-relationship-role-name>User-Demographics</ejb-relationship-role-name>
<multiplicity>One</multiplicity>
<relationship-role-source>
<ejb-name>Users</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>demographics</cmr-field-name>
</cmr-field>
</ejb-relationship-role>
<ejb-relationship-role>
<ejb-relationship-role-name>Demographics-belongs-to-Users</ejb-relationship-role-name>
<multiplicity>One</multiplicity>
<cascade-delete
/>
<relationship-role-source>
<ejb-name>Demographics</ejb-name>
</relationship-role-source>
</ejb-relationship-role>
</ejb-relation>
</relationships>
...
The example above defines a user bean with a one-to-one, unidirectional relationship to a demographics bean. Let's further examine each XML tag in the deployment descriptor.
The relationships
tag tells the container you describe the
relationships of the beans contained in this deployment. Each relationship
starts with the ejb-relation
tag. The
ejb-relation-name
tag is a human-readable description of the
relationship. The ejb-relationship-role
tags, of which there are
two in each ejb-relation
, describe each entity bean's role in this
relationship. Each ejb-relationship-role
tag contains a
human-readable ejb-relationship-role-name
tag and describes that
bean's role in the relationship. The multiplicity
tag describes
whether the bean is on the relationship's one or many side. The
relationship-role
source contains an ejb-name
tag,
which is the entity bean that participates in the relationship. The
ejb-name
tag must match one of the EJBs defined in the EJB jar
file.
The cmr-field
tag is optional. If the described bean lacks a CMR
field, it will not be included as demonstrated in the second
ejb-relationship-role
tag above. However, you must have the
cmr-field
tag for beans that have a CMR field. The text within the
tag contains the cmr-field-name
tag describing the CMR field to use
in the relationship. Finally, the cascade-delete
tag, included in
the bean that is the relationship's child, tells the container that if the
relationship's parent is deleted, the parent's children should also be deleted.
Relationships overview
The new EJB
2.0 persistence scheme can model eight relationships:
From the list above, you see three major relationship types: one-to-one, one-to-many, and many-to-many. We will explore each in more detail. First, however, let's explore how the EJB 2.0 specification handles such relationships.
Local interfaces
As previously
mentioned, the entity bean models each relationship type using abstract
assessors. The return type is either a local interface or a local interface
collection. Although local interfaces are not unique to entity beans, their use
in constructing relationships does restrict their usefulness. Since
relationships use local interfaces that can work only within the same VM, only
relationships within a single VM can occur. Therefore, you cannot create
relationships with other distributed entity beans. Although that restriction
hinders many possibilities, it offers one large advantage: local interfaces are
extremely fast. Because a remote call's overhead no longer exists, local
interface calls act just like other local method calls to a regular Java class.
One-to-one relationships
Now that
you understand EJB relationship basics, let's model your first relationship. For
simplicity's sake, start with an easy one-to-one relationship: the customer and
address relationship. Because in its simplest sense, the customer address
relationship is a one-to-one unidirectional relationship, you model it with one
customer bean and one address bean. I keep the entity beans simple to
concentrate on defining the relationship.
Begin by creating the abstract class defining the customer bean. Although the entity beans presented would work, I've simplified them to demonstrate how the relationships work. In a real system, you would not create entity beans with such primary keys:
package relationships;
import javax.ejb.*;
import
java.math.*;
import relationship.*;
public abstract class CustomerBean
implements EntityBean {
// The primary key
public abstract String getCustomerName();
//
The container-managed relationship field.
public abstract
Address getAddress();
public abstract void setAddress(Address
addr);
.
.
.}
Notice that I've declared the fields holding the relationship values the same
as the entity bean's regular container-managed field. You simply declare an
abstract field with the return type being the relationship bean's local
interface. The Address
bean implementation class proves easier than
the CustomerBean
class, as it contains no relationship-holding
fields:
package relationships;
import javax.ejb.*;
import
java.math.*;
import relationship.*;
public abstract class AddressBean
implements EntityBean {
// Our primary key
public
abstract String getAddress();
.
.
.
}
You don't do much in the entity bean to declare relationships. A relationship's true power, however, lies in that it can be unidirectional, bidirectional, one-to-one, one-to-many, or many-to-many. Although you can determine from this simple example everything needed for a one-to-one relationship, you must still declare the relationship in the deployment descriptor. The deployment descriptor excerpt looks like:
.
.
.
<relationships>
<ejb-relation>
<ejb-relation-name>Customer
contains an
address</ejb-relation-name>
<ejb-relationship-role>
<ejb-relationship-role-name>Customer-Address</ejb-relationship-role-name>
<multiplicity>One</multiplicity>
<relationship-role-source>
<ejb-name>Customer</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>address</cmr-field-name>
</cmr-field>
</ejb-relationship-role>
<ejb-relationship-role>
<ejb-relationship-role-name>Address-belongs-to-Customer</ejb-relationship-role-name>
<multiplicity>One</multiplicity>
<cascade-delete
/>
<relationship-role-source>
<ejb-name>Demographics</ejb-name>
</relationship-role-source>
</ejb-relationship-role>
</ejb-relation>
</relationships>
.
.
.
As you can see, modeling one-to-one unidirectional relationships is easy. You
can now get a customer's information and address from the customer bean. Now
suppose your business user wants to determine demographic information by finding
customers based on location. To do so, you must code logic into the
Address
entity bean. With CMR, you can quickly code the logic by
minimally changing the entity beans. In the Address
bean, add one
container-managed field:
.
.
.
// A container-managed field holding the
customer
public abstract Customer
getCustomer();
public abstract void setCustomer(Customer
customer);
.
.
.
Note that I added an unnecessary setter to the class. Its use depends on the application's business requirements. To wit, if you never create customers from addresses, leave the setter out and use the getter to find a customer from an address.
You also must slightly change the deployment descriptor to change the relationship to bidirectional:
.
.
.
<multiplicity>One</multiplicity>
<relationship-role-source>
<ejb-name>Address</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>customer</cmr-field-name>
</cmr-field>
.
.
.
By changing two code lines -- discounting any additional finder or business methods -- and three-lines in the deployment descriptor, you can now find customers from an address or find addresses belonging to a customer. In fact, if you only used one-to-one relationships, you'd nevertheless solve many of your development problems.
One-to-many and many-to-one relationships
Although one-to-one relationships prove useful, they do not
completely meet your needs. You'll find one-to-many relationships more useful
than one-to-one relationships. Indeed, you could extend the simple customer and
address example to a one-to-many relationship if you wish to track multiple
addresses, such as shipping, billing, home, and work addresses.
As another one-to-many relationship, think of a customer and her telephone information, for which you make a simple telephone bean:
package relationships;
import javax.ejb.*;
import
java.math.*;
import relationship.*;
public abstract class CustomerBean
implements EntityBean {
// The telephone
number
public abstract String getPhoneNumber();
//
The telephone type
public abstract String
getPhoneType();
public abstract void
setPhoneType(String);
.
.
.
The bean lets you set the telephone number and type. With the phone bean
developed, you can now build the relationship in the CustomerBean
.
Because either the Collection
or Set
class returns
many entity bean instances, you must choose one as the abstract field type in
CustomerBean
.
How to choose? The Collection
class contains all the entity
beans in the relationship, regardless of distinctness. In contrast, the
Set
class contains only unique, related entity beans. Because the
beans have the telephone number as their primary key, you have encoded
uniqueness into the bean, so use the Collection
object as the
CustomerBean
return type. Modify the bean so it contains the CMR
phone field:
package relationships;
import java.util.Collection;
import
javax.ejb.*;
import java.math.*;
import relationship.*;
public abstract
class CustomerBean implements EntityBean {
// The primary
key
public abstract String getCustomerName();
// The container-managed relationship field.
public abstract Address getAddress();
public abstract void
setAddress(Address addr);
// The container-managed relationship
field for phones
public abstract Collection
getPhone();
public abstract void setPhone(Collection
phones);
.
.
.
}
With the entity beans complete, you can now describe the relationship in the
deployment descriptor so the container can properly manage the relationship. To
do so, add another ejb-relation
tag to the deployment descriptor:
<relationships>
.
.
.
<ejb-relation>
<ejb-relation-name>Customer
contains many
phones</ejb-relation-name>
<ejb-relationship-role>
<ejb-relationship-role-name>Customer-Phone</ejb-relationship-role-name>
<multiplicity>One</multiplicity>
<relationship-role-source>
<ejb-name>Customer</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>phone</cmr-field-name>
</cmr-field>
</ejb-relationship-role>
<ejb-relationship-role>
<ejb-relationship-role-name>Phones-belongs-to-Customer</ejb-relationship-role-name>
<multiplicity>Many</multiplicity>
<cascade-delete
/>
<relationship-role-source>
<ejb-name>Phone</ejb-name>
</relationship-role-source>
</ejb-relationship-role>
</ejb-relation>
.
.
.
</relationships>
That's it to describe a unidirectional, one-to-many relationship.
Much like the one-to-one relationship, you can easily change a one-to-many
relationship to a bidirectional relationship by adding a CMR field in the
PhoneBean
class. You also must slightly adjust the deployment
descriptor to make the relationship bidirectional. First, modify the
PhoneBean
class:
.
.
.
// A container managed field holding the
customer
public abstract Customer
getCustomer();
public abstract void setCustomer(Customer
customer);
.
.
.
You should recognize the code above -- it's identical to how you modified the
AddressBean
for bidirectional behavior. The deployment descriptor
is the same as the address example, except you modify the phone bean's
relationship descriptor. You can now find a customer from any of his addresses,
or you can find all addresses by knowing the customer.
Many-to-many relationships
Although
you won't see the final relationship type -- many-to-many -- as often as the
other two types, sometimes it's critical to an application. To illustrate
many-to-many relationships, I'll employ an uncommon example. Let's assume you
have developed a Website with its pages stored in a database. Whenever a user
visits a page, you want to know whether he's a return visitor. To accomplish
that goal, you expand the aforementioned CustomerBean
, as it can
also describe a site visitor:
package relationships;
import java.util.Collection;
import
javax.ejb.*;
import java.math.*;
import relationship.*;
public abstract
class CustomerBean implements EntityBean {
// The primary
key
public abstract String getCustomerName();
// The container managed relationship field.
public abstract Address getAddress();
public abstract void
setAddress(Address addr);
// The container managed relationship
field for phones
public abstract Collection
getPhone();
public abstract void setPhone(Collection
phones);
// A container managed field for web page
visits
public abstract Collection getWebPages();
public abstract void setWebPages(Collection pages);
.
.
.
}
Again, you employ a collection because you will return many Webpage instances
for each customer. Next, define a WebpageBean
class to use in the
deployment descriptor:
package relationships;
import java.util.Collection;
import
javax.ejb.*;
import java.math.*;
import relationship.*;
public abstract
class CustomerBean implements EntityBean {
// The primary
key
public abstract String getPageName();
.
.
.
}
Although it having beans with one field is highly contrived, that's all that's necessary to demonstrate the relationship, so let's add the appropriate relationship to the deployment descriptor:
<relationships>
.
.
.
<ejb-relation>
<ejb-relation-name>Many
Customers contain many
webpages</ejb-relation-name>
<ejb-relationship-role>
<ejb-relationship-role-name>Customer-Pages</ejb-relationship-role-name>
<multiplicity>Many</multiplicity>
<relationship-role-source>
<ejb-name>Customer</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>webPages</cmr-field-name>
</cmr-field>
</ejb-relationship-role>
<ejb-relationship-role>
<ejb-relationship-role-name>Address-belongs-to-Customer</ejb-relationship-role-name>
<multiplicity>Many</multiplicity>
<relationship-role-source>
<ejb-name>Webpage</ejb-name>
</relationship-role-source>
</ejb-relationship-role>
</ejb-relation>
.
.
.
</relationships>
Note the difference in the deployment descriptor: you no longer add the
cascade-delete
tag. Why? If you do a cascade delete and delete one
customer, you will delete all the database's Webpages, leaving you with many
customers who apparently never visited a single page.
You now have a CustomerBean
EJB that can find all the Webpages a
customer visited. However, as soon as you finish, your boss asks for some
statistical reporting functionality. He wants to know, for any Webpage, all the
customers who visited that page. Armed with your newfound EJB relationship
skills, you add a CMR field to the WebpageBean
:
.
.
.
// The Customers who have visited
us
public abstract Collection
getCustomers();
public abstract void
setCustomers(Collection customers);
.
.
.
}
You also modify your deployment descriptor so Webpages also include a relationship field:
<relationships>
.
.
.
<ejb-relation>
<ejb-relation-name>Many
Customers contain many
webpages</ejb-relation-name>
<ejb-relationship-role>
<ejb-relationship-role-name>Customer-Pages</ejb-relationship-role-name>
<multiplicity>Many</multiplicity>
<relationship-role-source>
<ejb-name>Customer</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>webPages</cmr-field-name>
</cmr-field>
</ejb-relationship-role>
<ejb-relationship-role>
<ejb-relationship-role-name>Address-belongs-to-Customer</ejb-relationship-role-name>
<multiplicity>Many</multiplicity>
<relationship-role-source>
<ejb-name>Webpage</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>customers</cmr-field-name>
</cmr-field>
</ejb-relationship-role>
</ejb-relation>
.
.
.
</relationships>
After you recompile, your entity bean can get all the customers for any Webpage or it can get all the Webpages for any customer.
Relationship review
Even this
article's simple examples demonstrate that container-managed relationships prove
a powerful feature for developers. Application developers can now design and
encode complex relationships among many entity beans without resorting to custom
logic in their entity-bean implementation class. You can have one-to-one,
one-to-many, or many-to-many relationships without worrying about the entity
bean's state. The container now ensures child beans properly persist, activate,
and load. The enterprise specification's initial aim was to eleminate the
application developer's need to control transactional and persistence logic. EJB
2.0's new relationship capabilities take the extra step necessary to really
remove the developer away from the persistence details in entity beans.
About the author
Allen Fogleson, a senior developer at Cross
Media Marketing, has worked in the IT industry for 16 years, serving in numerous
roles including developer, senior developer, senior technical project manager,
and information systems officer. For the past 5 years, Allen has designed and
developed enterprise applications using Java technologies, concentrating
exclusively on EJB projects over the past 3 years.
![]() |
![]() |
|
![]() |
Copyright © 2002 JavaWorld.com, an IDG Communications company |
![]() |
![]() |
![]() |