Chapter 10. The Entity-Container Contract

Although CMP and BMP entities are programmed differently, their relationships to the container system at runtime is very similar. This chapter covers the relationship between EJBs and their containers. It includes discussions of primary keys, callback methods, and the entity bean life cycle. When differences between CMP and BMP are important, they will be noted.

The Primary Key

A primary key is an object that uniquely identifies an entity bean. A primary key can be any serializable type, including primitive wrappers (Integer, Double, Long, etc.) or custom classes defined by the bean developer. In the Ship EJB discussed in Chapter 7 and Chapter 9, we used the Integer type as a primary key. Primary keys can be declared by the bean developer, or the primary key type can be deferred until deployment. We will talk about deferred primary keys later.

Because the primary key may be used in remote invocations, it must adhere to the restrictions imposed by Java RMI-IIOP; that is, it must be a valid Java RMI-IIOP value type. These restrictions are discussed in Chapter 5, but for most cases, you just need to make the primary key serializable. In addition, the primary key must implement equals( ) and hashCode( ) appropriately.

EJB allows two types of primary keys: single-field and compound. Single-field primary keys map to a single persistence field defined in the bean class. The Customer and Ship EJBs, for example, use a java.lang.Integer primary key that maps to the container-managed persistence field named id. A compound primary key is a custom-defined class that declares several instance variables that map to more than one persistence field in the bean class.

Single-Field Primary Keys

The String class and the standard wrapper classes for the primitive data types (java.lang.Integer, java.lang.Double, etc.) can be used as primary keys. These are referred to as single-field primary keys because they are atomic; they map to one of the bean’s persistence fields. Compound primary keys map to two or more persistence fields.

In the Ship EJB, we specified an Integer type as the primary key:

public interface ShipHomeRemote extends javax.ejb.EJBHome {

    public Ship findByPrimaryKey(java.lang.Integer primarykey) 
        throws FinderException, RemoteException;
    ... 
}

In this case, there must be a single persistence field in the bean class with the same type as the primary key. For the ShipBean, the id persistent field is of type java.lang.Integer, one of the single-field primary key types. The term “persistent field” means a container-managed persistent field in CMP entities or a instance field in an BMP entity that maps to the beans state in the database. In container-managed persistence, the primary key type must map to one of the bean’s CMP fields. The abstract accessor methods for the id field in the ShipBean class fit this description:

public class ShipBean implements javax.ejb.EntityBean {
    public abstract Integer getId( );
    public abstract void setId(Integer id);
    ...
}

The single-field primary key must also map to a CMP field in bean-managed persistence. For the BMP ShipBean class defined in Chapter 9, the Integer primary key maps to the id instance field:

public class ShipBean implements javax.ejb.EntityBean {
    public Integer id;
    public String name;
    ...
}

In CMP entities, you identify the CMP field that will serve as the single-field primary key using the <primkey-field> element in the deployment descriptor. In addition, the <prim-key-class> element specifies the type of object used for the primary key class. The CMP Ship EJB discussed in Chapter 7 uses both of these elements when defining the id CMP field as the primary key:

<entity>
    <ejb-name>ShipEJB</ejb-name>
    <home>com.titan.ShipHomeRemote</home>
    <remote>com.titan.ShipRemote</remote>
    <ejb-class>com.titan.ShipBean</ejb-class>
    <persistence-type>Container</persistence-type>
    <prim-key-class>java.lang.Integer</prim-key-class>
    <reentrant>False</reentrant>
    <cmp-field><field-name>id</field-name></cmp-field>
    <cmp-field><field-name>name</field-name></cmp-field>
    <cmp-field><field-name>tonnage</field-name></cmp-field>
    <primkey-field>id</primkey-field>
</entity>

In BMP entities you do not specify a <primkey-field>, because primary keys are created by the bean code, not the container. However, you are required to identify the <prim-key-class> with BMP entities as shown in the following listing.

<entity>
    <ejb-name>ShipEJB</ejb-name>
    <home>com.titan.ShipHomeRemote</home>
    <remote>com.titan.ShipRemote</remote>
    <ejb-class>com.titan.ShipBean</ejb-class>
    <persistence-type>Bean</persistence-type>
   <prim-key-class>java.lang.Integer</prim-key-class>

</entity>

Although primary keys can be primitive wrappers (Integer, Double, Long, etc.), they cannot be primitive types (int, double, long, etc.) because the semantics of the EJB programming model require the use of Object type primary keys. For example, the EJBObject.getPrimaryKey( ) method returns an Object type, thus forcing primary keys to be Objects. Primitives also cannot be primary keys because primary keys must be managed by Collection objects, which work only with Object types. Primitives are not Object types and do not have equals( ) or hashcode( ) methods.

Compound Primary Keys

A compound primary key is a class that implements java.io.Serializable and contains one or more public fields whose names and types match a subset of persistence fields in the bean class. They are defined by bean developers for specific entity beans.

For example, if a Ship EJB didn’t have an id field, we might uniquely identify ships by their names and registration numbers. (We are adding the registration persistent field to the Ship EJB for this example.) In this case, the name and registration persistent fields would become our primary key fields, which match corresponding fields (NAME and REGISTRATION) in the SHIP database table. To accommodate multiple fields as a primary key, we need to define a primary key class.

The convention in this book is to define all compound primary keys as serializable classes with names that match the pattern BeanNamePK. In this case we can construct a new class called ShipPK, which serves as the compound primary key for our Ship EJB:

public class ShipPK implements java.io.Serializable {
    public String name;
    public String registration;
    
    public ShipPK( ){
    }   
    public ShipPK(String name, String registration) {
        this.name = name;
        this.registration = registration;
    }
    public String getName( ) {
        return name;
    }
    public String getRegistration( ) {
        return registration;
    }
    public boolean equals(Object obj) {
        if (obj == null || !(obj instanceof ShipPK))
            return false;
        
        ShipPK other = (ShipPK)obj;
        if(this.name.equals(other.name) && 
            this.registration.equals(other.registration))
            return true;
        else
            return false;
        
    }
    public int hashCode( ) {
        return name.hashCode( )^registration.hashCode( );
    }

    public String toString( ) {
        return name+" "+registration;
    }

}

To make the ShipPK class work as a compound primary key, we must make its fields public. This allows the container system to use reflection when synchronizing the values in the primary key class with the persistence fields in the bean class. We must also define equals( ) and hashCode( ) methods to allow the primary key to be easily manipulated within collections by container systems and application developers. We have also overridden the toString( ) method to return a meaningful value. The default implementation defined in Object returns the class name of the object appended to the object identity for that name space.

With CMP entities, it’s important to make sure that the variables declared in the primary key have corresponding CMP fields in the entity bean with matching identifiers (names) and data types. This is required so that the container, using reflection, can match the variables declared in the compound key to the correct CMP fields in the bean class. In this case, the name and registration instance variables declared in the ShipPK class correspond to name and registration CMP fields in the Ship EJB, so it’s a good match.

With BMP entities, the instance fields of the primary key class are not required to map exactly to corresponding persistent fields in the bean class. The bean class is directly responsible for creating and managing the instance fields of the primary key, not the container. In most cases, however, the instance fields in the primary key will map to persistent fields in the bean class.

The ShipPK class defines two constructors: a no-argument constructor and an overloaded constructor that sets the name and registration variables. The overloaded constructor is a convenience method for developers that reduces the number of steps required to create a primary key. The no-argument constructor is required for container-managed persistence. When a new EJB is created in CMP, the container automatically instantiates the primary key using the Class.newInstance( ) method and populates it from the bean’s container-managed fields. A no-argument constructor must exist in order for this process to work.

To accommodate the ShipPK, we change the ejbCreate( )/ejbPostCreate( ) methods on the bean class of both BMP and CMP entities so that they have name and registration arguments to set the primary key fields in the bean.

Here is how the ShipPK primary key class would be used in the CMP ShipBean class we developed for in Chapter 7:

import javax.ejb.EntityContext;
import javax.ejb.CreateException;

public abstract class ShipBean implements javax.ejb.EntityBean {
       
    public ShipPK ejbCreate(String name, String registration) {
        setName(name);
        setRegistration(registration);
        return null;
    }
    public void ejbPostCreate(String name, String registration) {
    }
    ...

The deployment descriptor for CMP entities is required to define <cmp-field> entries that match the instance fields of the compound primary key, but it must not define a <primkey-field> element. The <primkey-field> element is only used with single-field primary keys. The deployment descriptor for CMP entities must define a <prim-key-class> for compound primary keys, however.

<entity>
    <ejb-name>ShipEJB</ejb-name>
    <home>com.titan.ShipHomeRemote</home>
    <remote>com.titan.ShipRemote</remote>
    <ejb-class>com.titan.ShipBean</ejb-class>
    <persistence-type>Container</persistence-type>
         <prim-key-class>com.titan.ShipPK</prim-key-class>
    <reentrant>False</reentrant>
    <cmp-field><field-name>name</field-name></cmp-field>
                  <cmp-field><filed-name>registration</field-name></cmp-field>
    <cmp-field><field-name>tonnage</field-name></cmp-field>
</entity>

Here is how the ShipPK primary key class might be used in the BMP ShipBean class we developed for in Chapter 9:

public class ShipBean implements javax.ejb.EntityBean {
    public String name;
    public String registration;

    public ShipPK ejbCreate(String name, String registration){
        this.name = name;
        this.registration = registration;
        ...
        // database insert logic goes here
        ...
        return new ShipPK(name, registration);
    }

The deployment descriptor for BMP entities is always required to define the <prim-key-class> for both single-field and compound primary keys as shown in the following listing:

<entity>
    <ejb-name>ShipEJB</ejb-name>
    <home>com.titan.ShipHomeRemote</home>
    <remote>com.titan.ShipRemote</remote>
    <ejb-class>com.titan.ShipBean</ejb-class>
    <persistence-type>Bean</persistence-type>
    <prim-key-class>com.titan.ShipPK</prim-key-class>

</entity>

The ejbCreate( ) method of both the BMP and CMP entities now declares the ShipPK as the primary key type. The return type of the ejbCreate( ) method must match the primary key type if the primary key is defined or the java.lang.Object type if it is undefined (CMP only).

In container-managed persistence, if the primary key fields are defined—i.e., if they are accessible through abstract accessor methods—they must be set in the ejbCreate( ) method. While the return type of the ejbCreate( ) method is always the primary key type, the value returned in CMP must always be null. The EJB container itself takes care of extracting the proper primary key directly. In bean-managed persistence, the bean class is responsible for constructing the primary key and returning it to the container.

The ShipHomeRemote interface for both CMP and BMP entities is modified so that it uses the name and registration arguments in the create( ) method and the ShipPK in the findByPrimaryKey( ) method (EJB requires that we use the primary key type in that method):

import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.FinderException;

public interface ShipHomeRemote extends javax.ejb.EJBHome {
    
    public ShipRemote create(String name, String registration) 
        throws CreateException, RemoteException;
    
    public ShipRemote findByPrimaryKey(ShipPK primaryKey) 
        throws FinderException, RemoteException;
    
}

setName( ) and setRegistration( ), which modify the name and registration fields of the Ship EJB, should not be declared in the bean’s remote or local interfaces. As explained in the next paragraph, the primary key of an entity bean must not be changed once the bean is created. However, methods that simply read the primary key fields (e.g., getName( ) and getRegistration( )) may be exposed because they don’t change the key’s values.

CMP requires that the primary key may be set only once, either in the ejbCreate( ) method or, if it’s undefined, automatically by the container when the bean is created. Once the bean is created, the primary key fields must never be modified by the bean or any of its clients. This is a reasonable requirement that should also be applied to bean-managed persistence beans, because the primary key is the unique identifier of the bean. Changing it could violate referential integrity in the database, possibly resulting in two beans being mapped to the same identifier or breaking any relationships with other beans that are based on the value of the primary key.

Undefined Primary Keys in CMP

Undefined primary keys for container-managed persistence were introduced in EJB 1.1. Basically, undefined primary keys allow the bean developer to defer declaring the primary key to the deployer, which makes it possible to create more portable entity beans.

One problem with container-managed persistence in EJB 1.0 was that the entity bean developer had to define the primary key before the entity bean was deployed. This requirement forced the developer to make assumptions about the environment in which the entity bean would be used, which limited the entity bean’s portability across databases. For example, a relational database uses a set of columns in a table as the primary key, to which an entity bean’s fields map nicely. An object database, however, uses a completely different mechanism for indexing objects, to which a primary key may not map well. The same is true for legacy systems and Enterprise Resource Planning (ERP) systems.

An undefined primary key allows the deployer to choose a system-specific key at deployment time. An object database may generate an object ID, while an ERP system may generate some other primary key. These keys may be automatically generated by the database or backend system. The CMP bean may need to be altered or extended by the deployment tool to support the key, but this is immaterial to the bean developer; she concentrates on the business logic of the bean and leaves the indexing to the container.

To facilitate the use of undefined primary keys, the CMP bean class and its interfaces use the Object type to identify the primary key. The Ship EJB developed in Chapter 7 could use an undefined primary key. As the following code shows, the Ship EJB’s ejbCreate( ) method returns an Object type:

public abstract class ShipBean extends javax.ejb.EntityBean {
    
    public Object ejbCreate(String name, int capacity, double tonnage) {
        ...
        return null;
}

The findByPrimaryKey( ) method defined in the local and remote home interfaces must also use an Object type:

public interface ShipHomeRemote extends javax.ejb.EJBHome {

    public ShipRemote findByPrimaryKey(Object primaryKey)
        throws javax.ejb.FinderException;

}

The Ship EJB’s deployment descriptor defines its primary key type as java.lang.Object and does not define any <prim-key-field> elements:

<ejb-jar>
    <enterprise-beans>
        <entity>
            <ejb-name>ShipEJB</ejb-name>
            ...
            <ejb-class>com.titan.ship.ShipBean</ejb-class>
            <persistence-type>Container</persistence-type>
            <prim-key-class>java.lang.Object</prim-key-class>
            <reentrant>False</reentrant>
            <cmp-field><field-name>name</field-name></cmp-field>
            <cmp-field><field-name>capacity</field-name></cmp-field>
            <cmp-field><field-name>tonnage</field-name></cmp-field>
        </entity>

One drawback of using an undefined primary key is that it requires the bean developer and application developer (client code) to work with a java.lang.Object type and not a specific primary key type, which can be limiting. For example, it’s not possible to construct an undefined primary key to use in a find method if you don’t know its type. This limitation can be quite daunting if you need to locate an entity bean by its primary key. However, entity beans with undefined primary keys can be located easily using other query methods that do not depend on the primary key value, so this limitation is not a serious handicap.

In bean-managed persistence, you can declare an undefined primary key simply by making the primary key type java.lang.Object. However, this is pure semantics; the primary key value will not be auto-generated by the container because the bean developer has total control over persistence. In this case the bean developer would still need to use a valid primary key, but its type would be hidden from the bean clients. This method can be useful if the primary key type is expected to change over time.

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

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