Container Managed Relationships

Container Managed Relationships (CMR) allow a developer to define relationships between entities that are automatically managed by the CMP implementation. This can be used to simply represent the business data model in the Entity layer and allow applications to easily navigate between entities. Further, by defining the relationships in a manner the EJB Container can understand, it can factor them into the requests it makes to the store, potentially reducing the number of operations needed and significantly improving performance.

This article describes the definition and operation of CMR from the EJB perspective. Mapping CMR to SQL Databases describes how relationships map to relational databases and how operations at the EJB level generate SQL statements.

Types of Relationship

Relationships are defined between Entities using their local interfaces, which means that both entities must be located in the same JVM. In theory, you can establish a relationship between a CMP entity and a BMP entity, but in reality this may stretch the capabilities of your implementation.

Relationships are defined in the deployment descriptor and navigated using accessor methods in the EJB implementation. if a relationship is only navigable in one direction, accessor methods only need to be defined in one of the EJBs.

The multiplicity of each end of the relationship can be specified as One or Many, allowing the relationship as a whole to be:

  • One-to-One
  • One-to-Many
  • Many-to-One
  • Many-to-Many

In addition, a relationship end may be tagged as cascade-delete. This provides support for a basic form of aggregation in that removal of the entity on the "One" end will automatically result in the removal of all related entities.

Defining a Relationship in ejb-jar.xml

The XML definition defines an optional name for the relationship and then each end separately.

Each end of the relationship defines:

  • The multiplicity of the EJB instances (whether it is single-valued or multi-valued)
  • Whether a remove of this end cascades to related entities
  • Which EJB to use for that end
  • The (optional) name of an accessor used to navigate from that end

An example of a relationship definition is:


  
    Order-has-LineItems
    Relationship between an Order and its associated LineItems

    
      the definition of the Order end of the relationhip
      Order
      One

      
      

      
        the definition of the EJB at this end
        Order
      

      
        allow navigation from an Order to its LineItems
        lineItems
        java.util.Set
the definition of the LineItem end of the relationship LineItem Many LineItem allow navigation from a LineItem to its Order order

Navigating a Relationship from an EJB

EJBs navigate relationships using accessor methods in a similar way to cmp-fields. The name of the method is derived from the cmr-field-name in the relationship definition in ejb-jar.xml; the type of the relationship is detemined by the multiplicity of the other end.

Single-Valued Relationships

When the other end of the relationship has a multiplicity of "One", then the relationship is single valued and the accessor uses the local component interface of the related EJB. In the example above, each LineItem is related to a single Order, so the accessors for the "order" cmr-field would be:

    /**
* Return the order this lineitem is part of
*/ public abstract OrderLocal getOrder();

/**
* Set which order this lineitem is part of
*/
public abstract void setOrder(OrderLocal order);

When the set accessor is used to change the related entity, any existing relationship is broken and a new relationship is established to the target entity. If the other end of the reationship is multi-valued, then this entity will automatically be removed from the original entity's collection and added to the target entity's collection.

Multi-Valued Relationships

When the other end of the relationship has a multiplicity of "Many", the the relationship is multi-valued and the accessor uses collections of the local component interface. The supported collection types are java.util.Set and java.util.Collection; the EJB specification says that List and Map may be added in the future but these are not currently available. The distinction between Set and Collection is that a Collection may possibly return duplicate values; in most applications this is not likely and Set is typically used. Which collection type to use is specified by the cmr-field-type element, which much contain the fully qualified class name.

In the example above, an Order is related to many LineItems and the collection is defined as being a Set. The accessor definitions for this would be:

    /**
* Return the lineitems for this order
*/ public abstract Set getLineItems();

/**
* Set the lineitems for this order
*/
public abstract void setLineItems(Set lineItems);

The value returned from a get accessor may be empty but will never be null; an attempt to assign a null value using the set accessor will result in an IllegalArgumentException. Using the set accessor to change the related items is equivilent to performing a clear followed by an addAll on the internal collection; is does not change the value returned by the get accessor, just its contents.

The value returned from the get accessor is live for the duration of the current transaction. During that time it may be operated on using methods in the collection API such as add, addAll, iterator and remove. Once the transaction is over then any attempt to use the collection will result in an IllegalStateException. This effectively means that all Entities that use CMR must run inside a transaction.

If the relationship is Many-to-One, then adding an entity to a CMR collection will automatically break any existing relationships that it has. Thus the add method can be used to change which instance another entirty is related to. For example:

    // associated LineItem 1 with order 1
order1.getLineItems().add(lineItem1);
assertTrue(order1.getLineItems().contains(lineItem1));

// reassign LineItem 1 to order 2
order2.getLineItems().add(lineItem1);
assertFalse(order1.getLineItems().contains(lineItem1));
assertTrue(order2.getLineItems().contains(lineItem1));

Care must be taken when using an iterator to remove related entities. Any modification to the content of the underlying collection except through the iterator's remove() method will result in an IllegalStateException. Further, removing an entity from the collection breaks the relationship but does not remove the actual entity; if you wish to remove that entity then you must invoke its remove method. The following snippet shows how to remove a set of related entities:

    // remove all lineItems for this order
Set lineItems = getLineItems();
for (Iterator i = lineItems.iterator(); i.hasNext(); ) {
LineItemLocal lineItem = (LineItemLocal) i.next();

// remove the lineItem from the relationship
i.remove();

// remove the lineItem itself
lineItem.remove();
}

This also applies if the related entities are being assigned to another relationship. By adding them to another CMR collection, they are automatically removed from this one. This implicitly modifies the collection and the next operation on the iterator will result in an IllegalStateException.