Read all about EJB 2.0

Dramatic EJB 2.0 changes enhance flexibility and portability in your application development

1 2 3 4 Page 2
Page 2 of 4

The XML elements used to describe container-managed relationships can become very complex, as they must deal with both the cardinality and direction (unidirectional vs. bidirectional) of the relationships. The above code snippet demonstrates the elements you need to describe a simple relationship between a bean and its dependent object class. Although even simple relationships translate into verbose XML, all those elements are necessary so that the persistence manager can map complex object graphs to the database.

While the XML elements used to define the abstract persistence schema of CMP beans are central to CMP in EJB 2.0, for the sake of brevity, this article will cease to provide XML examples. Instead, it will illustrate the basic concepts behind CMP in EJB 2.0 by relying solely on the abstract idioms that must be used in the bean class. Those code idioms are used in concert with and are defined by the relationship elements in the XML deployment descriptor, so you can't have one without the other, but they are easier to follow than the XML part of the schema.

In addition to the XML elements, the abstract persistence schema also defines a set of idioms that you must use when declaring the bean class and its dependent objects. The way fields are accessed and modified is strictly defined, requiring that set<METHOD> methods be used for modifying persistent fields and get<METHOD> methods be used for accessing them. The names of those methods and their return types are dictated by their corresponding XML relationship elements in the deployment descriptor.

Both the entity bean class and dependent class follow the same abstract persistence schema. Below is an example of how the ContactInfo object might be defined as a dependent object class.

public abstract class ContactInfo {
            // home address info
      public abstract void setStreet(String street);
      public abstract String getStreet();
      public abstract void setState(String state);
      public abstract String getState();
      public abstract void setZip(String zip);
      public abstract String getZip();
      public abstract void setHomePhone(String phone);
      public abstract String getHomePhone();
      // work address info
      public abstract void setWorkPhone(String phone);
      public abstract String getWorkPhone();
      public abstract void setEMail(String email);
      public abstract String getEMail();
      ...
}

Dependents live and die along with the entity bean, which is critical to understanding the relationship between dependents and entity beans. Dependents are contained by a specific entity, so removing the entity results in the removal of the dependents. In relational database terms that is sometimes called a cascading delete.

Dependent objects such as ContactInfo are used in relationship fields. Dependent objects that form relationships with the entity bean are technically called dependent object classes. An EJB client application can never access a dependent object class directly; the class cannot be used as an argument or return value in a bean's remote or home interfaces. Dependent object classes are only visible to the bean class.

The dependent object classes are not suitable as remote argument types because they are tightly coupled with the bean's persistence logic at runtime. The persistence manager extends the abstract dependent object classes to provide implementations that can be used to manage the bean's persistence state at runtime. In addition, the abstract persistence schema models the data -- not the business concepts represented by the enterprise bean -- so it makes sense to hide the abstract persistence schema from the EJB clients as a design strategy.

The ContactInfo relationship field, for example, contains a lot of information beyond the simple address information needed by the bean's clients. While you can use the ContactInfo dependent object class in the abstract persistence schema, which is hidden from the bean's clients, you will use other objects to actually expose the data to the client. Below is an example of how the ContactInfo dependent is hidden from the EJB client. In that case, the address information is exposed using the Address object developed in the EJB 1.1 example.

// remote interface for the Employee bean
public interface Employee extends javax.ejb.EJBObject {
   public Address getHomeAddress();
   public void setHomeAddress(Address address);
   public int getIdentity() throws RemoteException;
   public void setFirstName(String firstName) throws RemoteException;
   public String getFirstName()throws RemoteException;
   public void setLastName(String lastName) throws RemoteException;
   public String getLastName() throws RemoteException;
}
// bean class for the Employee bean
public abstract EmployeeBean implements javax.ejb.EntityBean {
    ...
    public Address getHomeAddress(){
      ContactInfo info = getContactInfo();
      Address addr = new Address();
      addr.street = info.getStreet();
      addr.city = info.getCity();
      addr.state = info.getState();
      addr.zip = info.getZip();
      return addr;
     }
     public void setHomeAddress(Address addr){
      ContactInfo info = getContactInfo();
            info.setStreet(addr.street);
            info.setCity(addr.city); 
            info.setState(addr.state); 
            info.setZip(addr.zip);
     }
  ....
  // container-managed relationship fields
  public abstract void setContactInfo(ContactInfo info);
  public abstract ContactInfo getContactInfo();
...
}

While container-managed relationship fields are not exposed to the client, you can use container-managed persistence fields directly from the remote interface. Notice that the container-managed persistence fields used for accessing the firstName and lastName are used in the remote interface.

A bean may have many different kinds of relationships with dependent object classes (dependents), which are defined by the cardinality and direction of the relationship. Beans can have one-to-many and one-to-one relationships with dependents. For example, the Employee bean may have only one Benefit dependent but may have many ContactInfo dependents.

public abstract EmployeeBean implements javax.ejb.EntityBean {
     ...
      public abstract void setContactInfos(Collection addresses);
      public abstract Collection getContactInfos():
      
      public abstract void setBenefit(Benefit benefit);
      public abstract Benefit getBenefit();
     ...
}

A one-to-many relationship with a dependent is represented as either the java.util.Collection or java.util.Set types (note: the java.util.Map and java.util.List are being considered as additional return types in a subsequent version of the specification), while a one-to-one relationship with a dependent uses the dependent type object's type.

Entity beans may also define relationships with other entity beans. Those relationships may be one-to-one, one-to-many, or many-to-many. For example, the Employee bean might have many children but only one spouse. The code fragment below illustrates how those relationships are modeled, using the method idioms of the abstract persistence schema. Children and spouses are both manifested in that application as Person beans.

public abstract EmployeeBean implements javax.ejb.EntityBean {
      ...
      public abstract void setSpouse(Person manager);
      public abstract Person getSpouse();
      
      public abstract void setChildren(Collection family);
      public abstract Collection getChildren();
      ...
}

A one-to-many relationship with another bean is represented as a java.util.Collection or java.util.Set type, while a one-to-one relationship uses the bean's remote interface type.

Dependents themselves can have one-to-one, one-to-many, and many-to-many relationships with other dependents within the same bean. In addition, dependents may have one-to-one or one-to-many relationships with entity beans other than their parent bean. The below example shows how the Benefit dependent object class has a one-to-one relationship with the Salary dependent (a compensation calculator) as well as a one-to-many relationship with the Investment bean.

public abstract class Benefit {
      public abstract void setSalary(Salary salary);
      public abstract Salary getSalary();
      public abstract void setInvestments(Collection investments);
      public abstract Collection getInvestments();
}

At deployment time, the deployer will use the persistence manager tools to generate concrete implementations of the bean class and its dependent classes. Those concrete implementations will maintain relationships and synchronize the bean instance's state with the database at runtime. The container will manage the persistent instance at runtime, providing a robust environment with automatic access control and transaction control.

A bean may also define dependent object values, which are serializable objects such as the Address object in the EJB 1.1 example. Those values are persisted using serialization and do not form relationships with the bean -- they are strictly container-managed persistence fields.

A contract has also been defined between the container and the persistence manager, so that the persistence manager may obtain handles to transactions and access database connection pools managed by the container. That contract is a little looser and will need to be tightened in the future, but it is a basis for allowing portability of persistence managers across EJB containers. The details of the contract between the container and persistence manager are beyond the scope of this article.

In addition to defining persistence through the abstract persistence schema, EJB 2.0 provides a new query language that declares how the persistence manager should implement find methods in CMP.

The EJB Query Language

The EJB Query Language (EJB QL) specifies how the persistence manager should implement find methods defined in the home interface. EJB QL, which is based on SQL-92, can be compiled automatically by the persistence manager, which makes entity beans more portable and easier to deploy.

EJB QL and find methods

EJB QL statements are declared in the entity bean's deployment descriptor. EJB QL is fairly simple to work with. As an example, the home interface of the Employee bean may be declared as follows:

public interface EmployeeHome extends javax.ejb.EJBHome {
      ...
      public Employee findByPrimaryKey(Integer id)
      throws RemoteException, CreateException;
      public Collection findByZipCode(String zipcode)
      throws RemoteException, CreateException;
      
      public Collection findByInvestment(String investmentName)
      throws RemoteException, CreateException;
}

Given the above home interface definition, you can use EJB QL to specify how the find methods should be executed by the persistence manager. All entity beans are required to have a findByPrimaryKey() method. The query required to execute that method is obvious -- use the field(s) of the primary key to lookup the bean in the database, so no EJB QL statement is needed.

The findByZipCode() method is used to obtain all the Employee beans with a certain zip code. That would be expressed by using the following EJB QL in the deployment descriptor.

FROM contactInfo WHERE contactInfo.zip = ?1

This statement basically says "select all the Employee beans that have a zip code equal to the zipcode argument." The SELECT clause, which indicates what to select, is not needed in the EJB QL statements for find methods. That is because the find methods will always select the remote references of its own bean type. In that case, the select statement is supposed to return a collection of remote Employee bean references.

Find methods can even span the abstract persistent schemas of other beans, providing they are deployed together in the same ejb-jar file and have an actual relationship that can be navigated. For example, the findByInvestment() method would require that the find query navigate from the Employee to the Investment bean's abstract persistence schema. The EJB QL statement declared to express that find operation is shown below.

FROM element IN benefit.investments WHERE element.name = ?1

The above statement says "select all the Employee beans whose benefit dependent contains at least one Investment bean reference with the name equal to the investmentName argument of the findByInvestment() method."

EJB QL and select methods

EJB QL is also used in a new query method, called the ejbSelect method, that is similar to the find methods, except it's only for the bean class to use. It's not declared in the home interface and is therefore not exposed to the client. In addition, the ejbSelect methods can return a wider range of values, not just the bean's own remote interface types.

There are two kinds of select methods, ejbSelect<METHOD> and ejbSelect<METHOD>InEntity. The ejbSelect<METHOD> method is executed globally, which means it's not specific to the bean instance on which it is executed. The ejbSelect<METHOD>InEntity is specific to the entity instance in which it is executed. Those select methods are declared as abstract methods in the bean class and are used in the class' business methods. Below are examples of ejbSelect<METHOD> and ejbSelect<METHOD>InEntity methods and a demonstration of how you might use them in business methods.

public abstract class EmployeeBean implements javax.ejb.EntityBean {
      ...
      // ejbSelect<METHOD>InEntity
      public abstract Collection ejbSelectInvestmentsInEntity (String risk);
      
      // ejbSelect<METHOD>
      public abstract Collection ejbSelectInvestments(String risk);
      ...
}

In the above declarations, the two select methods operate under different scopes. The ejbSelectInvestmentsInEntity() is executed only on the current Employee bean instance, so it would return only the employee's risky investments.

1 2 3 4 Page 2
Page 2 of 4