Container-Managed Relationships

Container-managed relationships (CMR) might possibly sound pretty daunting, and certainly from the EJB container vendor's perspective, there could be some fairly complex activity happening behind the scenes. However, from the bean provider's perspective (that is, you), they are fairly straightforward and easy to use.

CMRs are defined declaratively through the deployment descriptor, underneath the relationships element. Therefore, container-managed relationships can only be defined between Entity beans that reside within the same local relationship scope (EJB specification, section 10.3.2). What this means in practice is that beans that have relationships must be deployed in the same ejb-jar file. You will be learning more about actually declaring CMRs later today, in “Configuring a CMP Entity Bean.”

Note

The restriction that CMR can only be defined between EJBs deployed in the same ejb-jar file could possibly create problems. Some organizations maintain static reference data globally and replicate that data locally. This works because such data is often updated relatively infrequently.

By its nature, reference data is referenced (!), so one would expect relationships from domain-specific Entity beans up to cross-organizational reference beans. However, if the reference Entity beans are deployed separately from the domain-specific Entity beans (as would be likely), no such relationships can be assigned.


Relationship Types

CMR allows three different types of relationships to be defined between Entity beans:

  • One-to-one

  • One-to-many

  • Many-to-many

The first two relationship types are to be expected, but the last is perhaps more unexpected if you are used to using RDBMS. In relational theory, it is not possible to create a many-to-many relationship directly; instead, a link (or association) table is required. Indeed, such a table can be seen directly in the BMP version of the case study database; the many-to-many link between jobs and skills is captured in the JobSkill table. However, the EJB specification allows many-to-many links to be defined directly for Entity beans, an immediate simplification over the RDBMS approach.

Note

Of course, most EJB containers—the J2EE RI included—will persist to RDBMS, so will require a link table in the physical schema. Indeed, the J2EE RI uses a link table even for one-to-many associations. You can see this if you look back to Figure 7.2. For example, the one-to-many link from Job to Location is captured through the snappily named JobBean_location_LocationBean_Table table.


These relationship types actually refer to the maximum cardinality (also sometimes called multiplicity) of the related beans in the relationship. That is, saying that there is “a one-to-many relationship between Location and Job” is shorthand for “the maximum number of Locations that a Job can be related to is one, and the maximum number of Jobs that a Location can be related to is many.” There is also the question of minimum cardinality. In other words, is it necessary for a Job to be related to any Location (or can it be related to none)? Equally, must a Location have any Job related to it?

The EJB specification answers this question implicitly by always allowing a minimum cardinality of zero. Hence, “one-to-many” also allows for none-to-many, one-to-none, one-to-many, and (trivially) none-to-none.

There are sometimes situations when a minimum cardinality of none is not acceptable. For example, it might be the case that a Job must always relate to a Location. (Actually, for the case study, this is not enforced except in principle). In these cases, it is up to the bean to do the appropriate validation. In other words, the Job bean would only define a create() method that accepted a Location bean reference, and if it provided a setLocation() accessor method in its interface, it would ensure that the supplied Location reference was not null.

A related question is, “What happens if a bean is removed?” Suppose that the Job bean relates to a Location, and the Location bean is deleted. The Job bean will be left with a null reference. In relational terms, this is sometimes called a cascade null.

Suppose (again) that every Job must always relate to a non-null Location. There are a number of options:

  • The first, somewhat radical, option is to remove the related Job beans—in other words, perform a cascade delete. CMR supports this directly (it is specified through the deployment descriptor) and will remove each Job bean in turn.

  • Second, the application can prevent the removal of the foreign key Location bean from occurring. This would be done by implementing an appropriate check in the ejbRemove() lifecycle method.

  • Another alternative would be to reassign every impacted Job bean to some new Location. Again, the ejbRemove() method would need to do this work.

The second option is probably the most likely, so you should take care to do this type of minimum cardinality analysis to make sure that you do not unwittingly end up with beans that have null relationships when the semantics of the problem domain prohibit this from occurring.

Navigability

In addition to specifying multiplicity of the relationships, CMR also allows the navigability of the relationship to be defined. The navigability is either unidirectional or bidirectional.

Navigability is defined by indicating the name of the field that references the related bean. For example, in a many-to-one relationship between the Job and Location beans, indicating a field of location for the Job bean means that there is navigability from Job to Location. There may not necessarily be navigability in the opposite direction; that would depend on whether the Location bean defines a field called jobs.

Caution

While the case study does not require bi-directional navigability either from Location to Job or from Skill to Job, it does define navigability nevertheless. Otherwise, the code generated by the J2EE RI 1.3 deployment tools (somewhat unfortunately) does not compile—not something to inspire confidence!


The term “field” (or more properly, cmr-field) used here indicates the accessor methods for the virtual fields defined in the CMP superclass and implemented by the EJB container. The next section looks at these methods in more detail.

cmr-fields

By way of example, the location cmr-field for the Job bean has accessor methods of getLocation() and setLocation(), and the skills cmr-field has the accessor methods getSkills() and setSkills(). The return type of these methods depend on the multiplicity of the relationship.

The relationship from Job to Location is many-to-one, so the methods that correspond to the location cmr-field are as follows:

public abstract LocationLocal getLocation();
public abstract void setLocation(LocationLocal location);

Tip

The same restriction on naming that applies to cmp-fields also applies to cmr-fields: the name must not start with a capital letter.


For single-valued cmr-fields, the return type for the getter and the parameter to the setter is the local interface of the related bean (LocationLocal in this case). Yesterday, it was noted that local interfaces are the cornerstone of container-managed relationships; this shows why. Remote interfaces cannot be used in CMR.

Note

This isn't recommended, but there is nothing in principle to prevent an Entity bean with only a remote interface from having relationships with other Entity beans. However, the target Entity beans must themselves have a local interface, and the relationship will be unidirectional. The absence of a local interface in the source Entity bean prevents the related Entity beans navigating back to the Entity bean.


The Job bean also has a relationship with the Skill bean, this time many-to-many. Thus, the skills cmr-field corresponds to the following methods:

public abstract java.util.Collection getSkills();
public abstract void setSkills(java.util.Collection skills);

This is a multi-valued cmr-field because a collection of values is returned, not just a single value. The collection returned here is a java.util.Collection of references to the local interface of the related bean (SkillLocal in this case). The EJB specification also allows for java.util.Sets to be returned. The EJB specification does not currently allow Lists or Maps to be returned from multi-valued cmr-fields, but does hint that they may be added in the future.

Note that the fields of a bean are either regular cmp-fields or they are cmr-fields (or they are just regular instance variables, not managed by the container at all). Put another way, cmp-fields cannot be defined that have references to other beans as their argument or return type; such fields must be defined as cmr-fields.

Composite Primary Keys and Relationships

The Job Entity bean has a relationship with both the Location bean and the Customer bean. The getCustomer() method returns the name of a customer as a String, whereas the getLocation() method returns a reference to a LocationLocal. In other words, the former returns the name (the primary key) to a bean, and the latter returns the bean itself. So why the difference?

The reason is that the customer field is part of the primary key for the Job bean, and appears in JobPK. Every public field in JobPK must have a corresponding field in the bean itself.

If the JobPK class defined its customer field to be a reference to a CustomerLocal, the JobPK class could not be guaranteed to be serializable.

This shows up a very subtle area, not highlighted at all in the EJB specification. In the case study, there is a relationship between Customer and Job, in that Jobs are identified by Customer. In other words, the primary key of Job contains the primary key of the Customer that “owns” that Job.

The case study does not define the one-to-many relationship between Customer and Job. If this had been done, a virtual field and corresponding accessor methods ({get/set}CustomerObj() methods) would need to have been defined. The problem that would then have arisen, however, is that potentially the name of the customer returned by getCustomer() may not correspond to the actual customer returned by getCustomerObj().

In commercial EJB containers, this problem can be resolved by mapping both the getCustomer() and getCustomerObj() methods to the same physical data in the persistent data store (the customer column in the Job table). This prevents them from getting out of step (although even here, the EJB container would need to make the customerObj field read-only because allowing it to be changed would implicitly change the primary key of the Job bean).

However, the J2EE RI container does not make the mapping of the Entity beans data to the physical schema explicit. While it might be possible to modify the implied mapping, it would be unclear (from an education standpoint) what was being done. For this reason, the case study does not define the Customer/Job relationship. This is why, for example, the customerObj field is derived from the customer field, and is looked up in the JobBean's ejbLoad() method.


Not only do the methods corresponding to the cmr-field return and accept only local and not remote interfaces to beans, they also cannot appear in the remote interface of the bean. This is not really surprising. After all, the return types and arguments to the methods corresponding to the cmr-field take only local interfaces of remote beans, so the client of the bean invoking the cmr-field methods must be local. You saw yesterday that there are very good reasons why remote interfaces are bad news for Entity beans; this is another reason not to provide a remote interface.

Note

The other reason that cmr-field methods cannot appear in the remote interface is to prevent untoward network traffic. The performance cost of transporting large collections of references across the wire (even assuming those references were serializable) would be overwhelming.


Table 7.1 compares the use of cmp-fields and cmr-fields in interfaces.

Table 7.1. cmp-fields and cmr-fields and Interfaces
Feature cmp-field cmr-field
Can appear in local interface Yes Yes
Can appear in remote interface Yes Not recommended though; Entity beans should be accessed via local clients. No
Can accept as parameters and return local references to beans No cmp-fields deal only with primitives (or serializable objects). Yes
Can accept as parameters and return remote references to beans No cmp-fields deal only with primitives (or serializable objects). No Container managed relationships are defined only through local interfaces of beans.

The EJB specification also requires that the Collection returned by a cmr-field method is only used in the same transaction context. You will be learning more about transactions tomorrow, but for now, just consider the following scenario. A Session bean could invoke a multi-valued cmr-field's getter method and receive back a Collection. It could then hold onto this Collection for a few seconds, minutes, days, or months. It might also remove and add elements to this Collection. If the client then calls the setter method for the bean, there are no guarantees that either the Collection hasn't been changed by some other client of the entity bean (the so-called lost update problem) or that the elements in the originally returned collection are still valid (the repeatable read problem). The EJB specification's insistence that collections are only manipulated within a transaction solves these problems, primarily because transactions both prevent the items in the Collection from being removed, and ensure that existing items will remain valid.

Manipulating Relationships

In the context of an RDBMS, relationships between tables can be manipulated in several ways. Consider, for example, the case study (with the schema used in Day 6, “Entity EJBs”). The Job and Skill tables are in a many-to-many relationship, resolved through the JobSkill link table, and the Job and Location table have a many-to-one relationship, implemented through the location column acting as a foreign key in the Job table.

To change a many-to-many relationship means adding or removing entries from the appropriate link table. In the example, this means adding or removing (job, skill) tuples from the JobSkill table.

To change a many-to-one relationship means changing the value of the foreign key column. In the example, this means changing the value of the location column in the Job table.

Manipulating relationships between Entity beans is somewhat different. As you have seen, the setter method for a multi-valued cmr-field (such as setSkills()) takes an entire Collection of items. And for many-to-one relationships (with the appropriate navigability), the relationship can be modified from the “parent” end, just as much as from the “child” end.

Moreover, for multi-valued cmr-fields, the collection of beans referenced by the relationship can be modified just by using the usual add() or remove() methods of the java.util.Collection interface.

The EJB specification lays out in some detail the semantics of various actions that impact relationships between Entity beans. Many of these are straightforward and act as expected, but a few deserve special comment.

Figure 7.4 shows an example configuration of Location and Job beans, and indicates the relationships before and after executing:

loc1.getJobs().add(job21)

Figure 7.4. Before and after object diagram for loc1.getJobs().add (job21).


The relationships indicated {new} are created as a result of this action, while the relationships indicated {destroyed} disappear. (This rather elegant notation is an enhancement to UML, described by deSouza and Wills' Catalysis Method.)

Initially, loc1 is associated with job11 through job1n, and similarly, loc2 is associated with job21 through job2n. You can see that as a result of the action, the job21 bean is added to the collection of jobs associated with the loc1 location. However, perhaps less obviously, the job21 bean is also removed from the collection of jobs associated with the loc2 location. This is because there is a one-to-many relationship between Location and Job, so the job21 bean cannot have a relationship with both loc1 and loc2 at the same time.

Note that the collection of jobs associated with loc1 changes, even though the setJobs() method of Location is not called! At least, these are the semantics laid out by the EJB specification, but it would be wise to check that your own EJB container correctly implements this. (The J2EE RI server does implement this correctly.)

The java.util.Collection interface defines the addAll() method as well as the add() method, and this is also supported for CMR collections. This takes a collection of elements rather than a single element. For one-to-many associations, the addAll() has the same semantics as add() in that any child elements (the Job bean in the example) that are added to some collection will also be removed from the collection where they resided.

The diagram in Figure 7.4 holds good for either unidirectional relationships with navigability from Location to Job, and for bi-directional relationships.

Figure 7.5 shows the “opposite” case of removing a bean from a collection, having executed

loc1.getJobs().remove(job1n)

Figure 7.5. Before and after object diagram for loc1.getJobs(). remove(job1n).


Initially, loc1 is associated with job11 through job1n, and similarly, loc2 is associated with job21 through job2n. As you can see, after executing this action, the job1n bean is no longer associated with any Location bean. Earlier today, it was noted that the EJB specification always allows a minimum multiplicity of zero, and this is what has occurred here. Suppose though that every Job should always be related to a Location—that is, a minimum multiplicity of one. Given that the job's location is being set to null indirectly (by calling remove() on the returned Collection from the getJobs() method), there is no easy way to enforce this business rule.

The only available solution is to not make the getJobs() method available in the interface of the bean. You could provide another method, perhaps named getJobsCopy(), that returns a copy of the Collection and make it clear that adding or removing beans to this Collection will not influence the relationship itself. Even better, the Collection could be made immutable. The following is a possible implementation for this method (assuming java.util.* is imported):

public Collection getJobsCopy() {
    List jobs = new ArrayList();
    for(Iterator iter = getJobs().iterator(); iter.hasNext(); ) {
        jobs.add(iter.next());
    }
    return Collections.unmodifiableList(jobs);
}

Incidentally, another way of removing elements from the collection returned by a getter method is to do so using an Iterator. Indeed, changing the contents of a Collection, either directly by using remove() or indirectly, such as by the use of add() as described in figure 7.4, will invalidate any Iterators instantiated from that Collection. In any case, the following would disassociate the loc1 location from all of its jobs:

for(Iterator iter = loc1.getJobs().iterator(); iter.hasNext(); ) {
    iter.next();
    iter.remove();
}

The diagram in Figure 7.5 again holds good for either unidirectional relationships with navigability from Location to Job and for bi-directional relationships.

The case study does not define any one-to-one relationships between beans, so imagine that there is an Employee bean that has a one-to-one relationship with Job. This could perhaps represent the Employee of the Advertiser who originally placed the Job advert.

In any case, Figure 7.6 shows the object diagram having executed

job1.setEmployee(job2.getEmployee())

Figure 7.6. Before and after object diagram for job1.setEmployee( job2.getEmployee() ).


Initially, job1 has a relationship with emp1, and job2 has a relationship with emp2. After the action, emp1 has no relationship with any Job, job2 has no relationship with any Employee, and job1 has a relationship with emp2.

This seems quite straightforward, but again, a minimum multiplicity of zero is needed. If either every Employee must always relate to a Job, or if every Job must always relate to an Employee, there is no method where this application validation can be performed. The Job bean's setEmployee() method might look like a good candidate, until you realize that this method is abstract and is generated entirely by the beans' subclass.

The solution, again, must be to not expose the setter method in the interface of the bean. Instead, a method, such as setEmployeeField(), could be provided. Its implementation, for the case where every Job must relate to an Employee (though not every Employee need have a relationship with a Job), might be as follows:

public void setEmployeeField(EmployeeLocal newEmployee) {
    if (newEmployee.getJob() != null) {
        throw new IllegalArgumentException( "New employee must not already be related to a
 job");
    }
    setEmployee( newEmployee );
}

As you can see, this implementation requires that the supplied employee is not related to a job. (If it were, that job would, in turn, need to be assigned a new employee not related to any job, and so on.)

Note

Clearly, this technique is good wherever some application-level validation is required. For example, if a bean had a cmp-field called phoneNumber, the format of the supplied number could be checked.


The diagram in Figure 7.6 holds good for unidirectional relationships with navigability from Job to Employee and for bi-directional relationships.

Figure 7.7 returns to the relationship between the Location and Job beans, this time showing the object diagrams having executed

loc1.setJobs(loc2.getJobs())

Figure 7.7. Before and after object diagram for loc1.setJobs(loc2. getJobs()).


Again, initially loc1 is associated with job11 through job1n, and similarly, loc2 is associated with job21 through job2n. After the action has completed, the Collection of jobs previously associated to loc2 are associated to loc1. The loc2 bean itself, and the Collection of jobs previously associated with loc1, no longer have any associations.

The discussion for the diagram in Figure 7.7 follows similar lines to that for the previous diagram in Figure 7.6. Specifically, handling minimum cardinality of one for either Job or Location will require the setJobs() method to be removed from the interface of the bean. Instead, a method, such as setJobsField(), should be exposed that will do the validation and then delegate to setJobs().

It's also worth remarking that behind the scenes, the EJB container is having to perform some significant updates to the persistent data store. Assuming an RDBMS physical schema of Location and Job tables with a foreign key in the Job table, the foreign key for job11 through job1n would need to be set to null, and the foreign key for job21 through job2n updated to be loc1.

Note

You saw earlier today that the actual physical schema used by J2EE RI actually has a separate link table called JobBean_location_LocationBean_Table that holds the foreign key. For the J2EE RI to implement this action, it would need to delete all the rows in this table for loc1 and loc2, and then re-insert new rows corresponding to the (loc1, job21)(loc1, job2n) tuples.


If you were to design a bean with multi-valued relationships without using CMR, you might well have provided methods such as addSkill() and removeSkill(). As you now appreciate, CMR itself does not require or provide such methods, relying instead on a single setter method for the cmr-field.

Of course, there is nothing to prevent providing methods, such as addSkill() and removeSkill(), in the bean itself. Indeed, as you have seen, there are good reasons why the setSkills() method might not appear in the local interface to the bean; providing just addSkill() and removeSkill() in the interface would provide an (arguably) more natural interface to the bean while also allowing the bean to perform any application-specific validation.

Finally in this section, note that it is illegal to call the setter method of a multi-valued cmr-field with null; a java.lang.IllegalArgumentException will be thrown by the EJB container if this is attempted. In the example, to indicate that a job has no skills associated with it, an empty collection (for example, java.util.Collection.EMPTY_LIST) must be passed as the argument to setSkills().

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.111.179