Declaring a CMP Entity Bean

   

The basic responsibilities of the bean provider don't change that much based on whether BMP or CMP is being used. In either case, you still have to define the bean class, its home and component interfaces, its primary key class (if it's a multiple-field key), and the initial entries in its deployment descriptor. The good news is that the home and component interfaces and the primary key class are independent of the implementation choice you make. This is key because requiring any differences here would expose your implementation choice to the client. The deployment descriptor identifies persistent fields and relationships for CMP, so it's definitely affected by the persistence choice you make. The contents of the deployment descriptor will be covered in detail a little later, so that leaves the bean implementation class as the place to start.

Compared to BMP (and earlier versions of CMP), the most striking difference in declaring an EJB 2.0 CMP entity bean class is that you have to declare it to be an abstract class. This is where the nature of CMP shows itself. You're letting the container do part of the persistence work for you and the way it does that is by providing the concrete class that actually implements your bean. Your responsibility as a bean provider is to implement your business logic and supply enough information to identify the fields and relationships that need to be persisted for a particular entity, but that's where it stops. It's up to the container to determine how to implement a class that provides the persistence your entity bean needs. You can think of this as an extension to the work an application server does when it prepares an EJB for deployment. Just like the container tools are able to create the stubs and skeletons needed to deploy an EJB, they can also create classes that extend your abstract entity classes to provide implementations for their abstract methods .

Besides being declared as abstract , your CMP bean implementation classes must also

  • Be declared as public .

  • Implement the EntityBean interface.

  • Provide a public , no-argument constructor (which is best done by not explicitly declaring any constructors at all).

  • Not implement finalize() .

Before going on, it's important to talk about why CMP has taken the direction that it has. It all comes down to minimizing the dependencies of the classes you write on other classes and the low-level mechanisms that support them. First of all, EJB as a whole takes advantage of Java interfaces to decouple the public interfaces exposed to a client from the classes that implement them. As with any other use of interfaces, this allows you to change how a set of methods is implemented without impacting the clients that depend on the interface defined by the methods. EJB 2.0 carries this concept further to separate the persistence details of an entity bean from the rest of its implementation. Using CMP, your implementation class defines the business logic that it's responsible for but it has no knowledge of how its persistent fields are maintained . By pulling these details out of the bean class, an entity bean can be ported to another EJB container or data store without changing or recompiling any of its code. The porting process is limited to modifying the deployment information that defines the database mappings and executing the container tools to generate a new concrete bean class.

Defining CMP Fields

The reason you have to declare a CMP bean class as abstract is that you identify its persistent fields and relationships using abstract methods. You're not responsible for declaring any instance variables to represent these fields or hold references to other objects because those are implementation details left to the container. This is a big change from BMP, so it's a good idea to go ahead and look at an example. The following code shows a segment of the local interface declared for the auction example back in Chapter 5, "Entity Beans":

 public interface EnglishAuction extends EJBLocalObject {   ...    public void setName(String newName);    public String getName();    public void setDescription(String newDescription);    public String getDescription();    ...  } 

In Chapter 6, "Bean-Managed Persistence," the implementation presented for EnglishAuctionBean supported these business methods using a set of instance fields declared for the class:

 public class EnglishAuctionBean extends AbstractEntity implements EntityBean {   ...    protected String name;    protected String description;    ...  } 

These field declarations made it possible to provide implementations like the following for the business methods in the component interface:

 public void setName(String newName) {   name = newName;  }  public String getName() {   return name;  } 

The approach for declaring a CMP field is quite different. For example, a CMP implementation of the EnglishAuctionBean class would declare a name field using the following abstract method declarations:

 public abstract class EnglishAuctionCMPBean extends AbstractEntity   implements EntityBean {   ...    public abstract void setName(String newName);    public abstract String getName();    ...  } 

Here, name is a virtual field accessible only through its JavaBean-like get and set methods. It would be illegal for you to declare an instance variable with the identifier name in conjunction with these methods. This is because it's up to the container to provide the implementation details that support virtual fields, which are known as CMP fields . The container uses reflection and entries in the deployment descriptor to identify the CMP fields of a bean class. The get and set methods are all you provide in your bean class to represent a CMP field (declaring an instance variable for one the way you do for BMP would result in an error). By requiring a bean class to access its persistent fields using these methods and not the internal representation used for the fields, the class has no dependencies on how its persistence is handled.

CMP fields are how you manage the persistent attributes of an entity bean. Each CMP field you declare must hold either a Java primitive or a serializable type. For the auction example, you would use CMP fields to represent such values as the auction name, description, and starting bid amount. A CMP field can't be used to hold a reference to a related entity bean. You'll see how relationships between entity beans are handled shortly.

Even though the get and set methods for a bean's CMP fields must be declared as public , you're actually only exposing them to the container because clients don't access bean instances directly. You can allow a client to access a bean's CMP fields by including their get and set methods in the component interface. The one restriction is that you can't expose any set methods that operate on fields that make up a bean's primary key. This is because the container won't allow a CMP entity's primary key to be changed after it's been assigned.

Although you're allowed to expose CMP set methods that aren't associated with the primary key, you won't typically do this. The problem with directly exposing these methods is that you can't enforce any business logic when they're called. When the container implements one of your abstract methods, all it provides is the code required to persist the field. If you need to apply any validation logic or perform any other processing when a field is updated, you must "intercept" the relevant method calls to do so. The EJB specification doesn't address this issue, so you need to choose your own approach for the applications you build. A simple solution is to expose update methods other than the CMP set methods in your component interfaces. These methods can perform any logic that's needed in addition to calling the methods that update the fields.

The get and set methods exposed by the EnglishAuction local interface would correspond to a convenient set of virtual field names for the auction class. As pointed out earlier, getName and setName would support a virtual name field. The problem with this is that using these methods to declare the CMP fields would rule out executing any business logic during a client's update to a field. An easy solution to this is to keep the interface method names but use alternative virtual field names. For example, appending Field to the end of each name would allow updates to be intercepted without affecting the local interface. The following code fragment shows how this can be applied to an auction's starting bid:

 public abstract class EnglishAuctionCMPBean extends AbstractEntity   implements EntityBean {   ...    public abstract void setStartingBidField(Double newStartingBid);    public abstract Double getStartingBidField();    public void setStartingBid(Double newStartingBid)     throws InvalidAuctionStatusException {     if ((getStatusField() == null)        IAuctionStatus.AUCTION_PENDING.equals(getStatusField())) {       setStartingBidField(newStartingBid);      }      else {       throw new InvalidAuctionStatusException(         "Can only set the starting bid for a pending auction");      }    }    public Double getStartingBid() {     return getStartingBidField();    }    ...  } 

As shown in this listing, the auction virtual fields have names such as startingBidField . The get and set methods exposed in the local interface are intercepted to prevent the client from directly interacting with the abstract methods declared to support the CMP fields. You can adopt other method naming conventions to achieve the same result. For example, some developers prefer to use names such as updateStartingBid for methods in a component interface. This would allow you to use startingBid as a virtual field name if you wanted. What matters in the end is that you're able to implement business logic that is executed whenever a client accesses a field.

Defining CMR Fields

Under CMP, the container can maintain relationships between entity beans for you. The major constraints are that you can only define relationships between beans implemented using EJB 2.0 CMP, and the related beans must be declared in the same deployment descriptor. These relationships can be one-to-one, one-to-many, or many-to-many and they can be either bidirectional or unidirectional. In a bidirectional relationship, either entity can navigate to the related entity (or entities). A unidirectional relationship supports navigation from only one side. All relationships are defined in terms of local interfaces, so a bean must expose a local interface if you want to use it as the target of a navigable relationship. A bean without a local interface can be used in a unidirectional relationship only where it's the source of the navigation. From a logical standpoint, a unidirectional relationship makes sense when only one side needs to know about the other. For example, an auction needs to know about the item it's offering for sale but the item doesn't necessarily need to know that it's been assigned to an auction.

The following method declarations illustrate how you declare a relationship to another entity bean:

 public abstract class EnglishAuctionCMPBean extends AbstractEntity   implements EntityBean {   ...    public abstract void setItem(Item newItem);    public abstract Item getItem();    ...  } 

This declaration defines a container-managed relationship (CMR) field that associates an item with an auction. A CMR field is declared in much the same way as a CMP field using abstract get and set methods. A related entity bean is referenced using its local interface type in a one-to-one relationship. A one-to-many or many-to-many relationship is defined using a Collection or Set where the members have to be of the related bean's local interface type (it's expected that List and Map eventually will be allowed as well). The container is free to select any implementation of these collection interfaces to support a managed relationship. You never reference a specific implementation in your method declarations. For example, a one-to-many relationship between a customer and that customer's orders can be declared using the following methods:

 public abstract void setOrders(Collection newOrders);  public abstract Collection getOrders(); 

Note

The deployment descriptor is used to identify the CMP and CMR fields associated with a bean and not simply the presence of get and set methods. This means that you can define other business methods that start with get and set without them being confused with a bean's CMP and CMR fields.


As with CMP fields, you can expose the get and set methods associated with a CMR field in the local interface of a bean. If you need to execute your own business logic when a relationship is modified, you should expose an assignment method in the local interface other than the set method for the CMR field. You can't expose the accessor methods for a CMR field in a remote interface because they're always defined using the related bean's local interface. Remember that it's always illegal to reference a local interface or local home type in a remote interface. It's just as illegal to declare a method that returns or accepts a collection of local interface references in a remote interface.

In addition to a field's set method, you can use the methods of the Collection interface, such as add and remove , to define the one-to-many or many-to-many relationship represented by a CMR field. Unlike what you're accustomed to with a collection declared as an instance variable in a regular class, you're not responsible for initializing a collection associated with a CMR field. If there are no related objects assigned to a particular CMR field, the container is required to return an empty collection (as opposed to null ) as the result for the associated get method. This means that in the preceding example, you can call getOrders and then execute the add method on the collection that's returned without ever instantiating a new collection and assigning it to the field yourself. Just like the container isn't allowed to return a null from the get method, you can't assign a null to a one-to-many or many-to-many CMR field or the container will throw an IllegalArgumentException . This same exception is thrown if a collection is passed to a set method that holds an object that isn't of the local interface type for the related entity.

You can access an entity object related to a bean simply by calling the get method that defines the relationship. The work required to locate that entity object is performed behind the scenes by the container. It's the equivalent of looking up the local home for the related entity and executing a finder method to obtain a local interface reference to the desired entity (or collection of them). Because the work of the container is hidden from the abstract bean class, it's possible for the container to use lazy loading and only obtain a reference to a related entity object if it's accessed by the bean instance or one of its clients.

Besides maintaining the references that define a relationship between entities, the container also can cascade the deletion of an entity down to any related entities that you specify. For example, you can specify that when an auction is deleted, all of its bids should be deleted as well. You do this with a simple entry in the deployment descriptor. You'll see how to do this later in "Deploying an Entity Bean using CMP."

Cascade deletion is supported for both one-to-one and one-to-many relationships. It doesn't apply to many-to-many because a single entity doesn't "own" another in this situation.

Dependent Value Classes

Typically, you'll use CMP fields to maintain values corresponding to the Java primitive types and standard classes such as String and Integer . You also can associate your own classes with CMP fields as long as they're serializable and meet a few other restrictions. These classes are referred to as dependent value classes .

For the container to manage the data held in a dependent value class, it must be a concrete, serializable class. As with other CMP fields, you can expose dependent value classes in the local or remote interface of an entity bean if you choose. As indicated by their name, instances of dependent value classes are fully dependent on the associated entity when it comes to life-cycle management. If an entity object is removed, any dependent value instances it owns are destroyed as well.

Unlike an entity referenced through a CMR field, you don't provide a declarative mapping of the individual elements of a dependent value class to persist it. Instead, the container treats it as a single chunk of data that can be managed using serialization for persistence purposes. When you call the get method associated with a dependent value class, the container returns a copy of the object held by the field. Similarly, a call to the set method causes the container to copy the values you supply to the field.

It's not likely that you'll need dependent value classes in a typical application, but the support is there if you do. If you're developing a new persistent class that is related to an entity in your system, you can implement the new class as an entity bean and use a CMR field to manage the relationship. Using a dependent value class with a CMP field is more useful if you have to work with an existing regular Java class that needs to be persisted.



Special Edition Using Enterprise JavaBeans 2.0
Special Edition Using Enterprise JavaBeans 2.0
ISBN: 0789725673
EAN: 2147483647
Year: 2000
Pages: 223

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net