Patterns and Idioms

This section presents some patterns and idioms that relate to CMP Entity beans. You'll recognize some of the points made here; they were made earlier in the “Container Managed Relationships” section.

Normalize/Denormalize Data in ejbLoad()/ejbStore()

Under CMP, the ejbLoad() and ejbStore() methods don't have very much (or indeed anything) to do; the interactions with the persistent data store are done by the EJB container.

However, it may be that the physical schema of the persistent data store (especially if that persistent data store is an RDBMS) does not correspond exactly with the logical schema of the Entity bean.

For example, the Applicant table defines two columns—address1 and address2. However, at least conceptually, the Applicant Entity bean has a vector field of address, of type String[]; there could be many lines in the address (and it's just that the physical schema of the persistent data store constrains the size of this vector to 2):

Santa Claus
No. 1 Grotto Square   (line 1)
Christmas Town        (line 2)
North Pole            (line 3)
The World             (line 4)
Earth                 (line 5)
The Solar System      (line 6, and so on ...)

Because the ejbLoad() method is called after the EJB container has loaded the data, it may renormalize the data. In other words, the data in the two cmp-fields of address1 and address2 can be converted into the String[] address field. The bean's clients' view (as presented by the methods of the local interface) is that the address field is a vector.

Conversely, the ejbStore() method, called just before the EJB container updates the data, can denormalize the data. In other words, the data in the address vector field can be “posted” back to the address1 and address2 cmp-fields.

Don't Expose cmp-fields

Although the EJB specification allows cmp-fields to be exposed in the local (or remote) interface of a CMP Entity bean, there are problems with doing so. Because the setter method that corresponds to the field is generated by the EJB container, it is not possible to perform any application-level validation.

Instead, it is better to create a shadow setter method, have it do any validation, and then delegate to the actual cmp-field setter method.

You may also want to create a shadow getter method. This would allow you symmetry in the names of the methods, and you could also perhaps do some caching of values or other application-level logic.

As an example, instead of exposing the getter and setter methods for the description cmp-field of the Job bean, you might have a local interface of

package data;

import javax.ejb.*;
// imports omitted

public interface JobLocal extends EJBLocalObject {
    String getDescriptionField();
    void setDescriptionField(String description);
    // code omitted
}

with a corresponding implementation of

package data;

import javax.ejb.*;
// imports omitted

public abstract class JobBean implements EntityBean {
    public String getDescriptionField() {
        // any application logic here
        return getDescription();
    }
    public void setDescriptionField(String description) {
        // any application logic and validation here
        setDescription(description);
    }
    public abstract String getDescription();
    public abstract void setDescription(String description);
}
						

Don't Expose cmr-fields

Although the EJB specification allows cmr-fields to be exposed in the local interface of a CMP Entity bean, it may be best not to. There are two reasons why exposing the cmr-field causes problems, both related to the returned collection from the getter method of a cmr-field:

  • The first is that this returned collection is mutable. A client can change of the Entity bean's relationships with other beans by manipulating this collection. In other words, the bean's state is changed without it being aware.

  • The second is that the returned collection becomes invalid when the transaction context changes. This is actually good that this is so, but it is a subtle point, and some developers might not appreciate it if less than familiar with EJB transactions (making debugging their application somewhat tricky).

An alternative to exposing the setter method of a cmr-field would be for the bean to offer alternative methods, such as addXxx() and removeXxx(), on the bean itself and have these call the setter method. An alternative to exposing the getter method would be to expose a shadow method called something like getXxxCopy(). This would create a copy of the collection. The method name suggests to the client that they will not be able to change the relationships of the bean. Indeed, the returned collection could even be made immutable.

As an example, instead of exposing the getter and setter methods for the skills cmp-field of the Job bean, you might have a local interface of

package data;

import javax.ejb.*;
import java.util.*;
// imports omitted

public interface JobLocal extends EJBLocalObject {
    Collection getSkillsCopy();
    void addSkill(SkillLocal skill);
    void removeSkill(SkillLocal skill);
    // code omitted
}

with a corresponding implementation of

package data;

import javax.ejb.*;
import java.util.*;
// imports omitted

public abstract class JobBean implements EntityBean {
    public Collection getSkillsCopy() {
        List skills = new ArrayList();
        for(Iterator iter = getSkills().iterator(); iter.hasNext(); ) {
            skills.add(iter.next());
        }
        return Collections.unmodifiableList(skills);
    }
    public void addSkill(SkillLocal skill) {
        getSkills().add(skill);
    }
    public void removeSkill(SkillLocal skill) {
        getSkills().remove(skill);
    }
    public abstract Collection getSkills();
    public abstract void setSkills(Collection skills);
    // code omitted
}
						

Enforce Referential Integrity Through the Bean's Interface

Entity beans represent the domain layer in the n-tier architecture, and Entity beans have relationships among themselves. If a method in a bean's interface has an argument of some bean (usually for a relationship), this bean should be defined via the local reference rather than by its primary key. In other words, referential integrity is effectively enforced; the client guarantees that the bean exists, because it passes through an actual reference to that bean.

This idiom is honored implicitly for CMP Entity beans that expose their cmr-fields. For CMP Entity beans that provide shadow methods (as discussed earlier), these shadow methods should still deal with local references to the related beans, rather than identifying the related bean by its primary key.

Note

This idiom applies equally to BMP Entity beans. Indeed, if BMP Entity beans are written to follow this idiom, it becomes that much easier to convert them to CMP.


Use Select Methods to Implement Home Methods

Select methods can only be called by a bean itself, so they act as helper methods. A common place where they are often used is in implementing home methods.

For example, the Job bean could have provided a home interface to count the number of jobs advertised. This could have been implemented as follows:

package data;
import javax.ejb.*;
// imports omitted

public interface JobLocal extends EJBLocalObject {
    int countJobs();
    // code omitted
}

with a corresponding implementation of

package data;

import javax.ejb.*;
import java.util.*;
// imports omitted

public abstract class JobBean implements EntityBean {
    public int ejbHomeCountJobs() {
        int count = 0;
        for (Iterator iter = ejbSelectAllJobs().iterator(); iter.hasNext(); ) {
            iter.next(); count++;
        }
        return count;
    }
    public abstract Collection ejbSelectAllJobs();
    // code omitted
}

The ejbSelectAllJobs() EJB QL query string would be simply

SELECT OBJECT(j)
FROM Jobs AS j

Note

EJB QL does not (yet) support a SELECT COUNT(*) syntax, so this is the only way of performing counts (other than resorting to direct access to the data store).


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

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