Chapter 21. Managing Persistence—Entity Beans

Persistence: The Final Tier

As has been covered elsewhere in this book, the typical BEA WebLogic Server configuration consists of three tiers:

  • Tier 1: Web Server Tier

  • Tier 2: Application Server Tier

  • Tier 3: Database Tier

The Database tier is responsible for storage and retrieval of attributes and properties of the enterprise application through persistence. The application achieves the following goals when persistence is used correctly:

  • Durability—The state of the application can be restored after system shutdowns or crashes.

  • Reliability—The state of the application is accurate. The data is left in a consistent and known state.

  • Scalability—The application can grow to support more objects without changing or affecting the architecture.

A durable, reliable, and scalable application is exactly what is provided by entity Enterprise JavaBeans. In Chapter 6, “Transitioning from Software Design to J2EE Technology Components and Services,” the Model-View-Controller design pattern was introduced. This design pattern maps directly to an enterprise application, with JSP and servlets providing the view, session EJBs providing the control, and entity EJBs providing the model. This chapter focuses on the model and how it is implemented using entity EJBs. Specifically, it focuses on the components of an entity EJB and how persistence is implemented.

Note

J2EE also allows for stored data to be represented within an XML document, JMS message, via Cookies (on client), and through JDBC.

What Are Entity Beans?

As the title of this chapter suggests, entity beans are responsible for managing persistence in an enterprise application. They are the data-centric component of the architecture. Persistence is the action of storing the state of the application, allowing it to be fully restored at a later time. The life span of persistent data extends past the system shutdown, allowing it to be restored in a subsequent execution. The state of the application is defined by the value of the objects’ attributes. Some attributes are considered transient and do not take part in persistence of the application. The non-transient attributes contain the state information that must be maintained throughout system shutdowns and crashes. For example, a business application that maintains sales orders and inventory requires that these values are stored persistently.

The Java2 Enterprise Edition addresses the need for persistence through entity EJBs. When such a bean is deployed in BEA WebLogic Server, the persistent data typically resides in a relational database. The entity bean provides the object-relational mapping with the database management system (DBMS). In the multitiered architecture of an enterprise application, the business logic, provided by the session beans, uses the entity beans for persistent storage of application data.

Entity EJBs provide the following behavior for the enterprise application in that they

  • Represent persistent data throughout shutdowns and crashes

  • Are shared by multiple clients

  • Synchronously maintain persistent storage

  • Provide the object-relational mapping between the in-memory attributes of the entity bean and the persistent storage

Using the EJB Container

BEA WebLogic Server provides a subsystem known as the EJB container. All Enterprise JavaBeans exist within this container. Through the EJB container, the application server provides services to allow the EJB to perform database management, transaction management, and life-cycle management. The bean writer has the option to use the EJB container to perform Container-Managed Persistence (CMP). The alternative is Bean-Managed Persistence (BMP), which requires the bean to directly interface with the database to implement persistence. The two types of entity beans, CMP and BMP, will be covered in detail along with the additional services provided by the EJB container. The container also provides Container-Managed Transactions (CMT) and Container-Managed Relationships (CMR). The combination of CMP with CMR greatly reduces the need for the bean provider to write Bean-Managed Persistence. This option frees the entity bean from providing the code to perform these services on its own behalf.

Note

The entity EJB type is defined by the method that is used for persistence: Bean-Managed Persistence or Container-Managed Persistence.

A BMP entity EJB contains the JDBC code in its methods to directly communicate with the database. A CMP entity EJB allows the application server to perform persistence through the EJB container. In either case, the persistent state of the entity bean is stored in a relational database by mapping the attributes of the bean to rows and columns in the tables of a relational database. Both BMP and CMP EJBs provide a deployment descriptor in the form of an XML file to describe the components of the bean and the type of persistence that will be used. In the case of Container-Managed Persistence, the fields whose persistence is managed by the container are also specified in the deployment descriptor.

Container-Managed Persistence Versus Bean-Managed Persistence

Container-Managed Persistence is the preferred implementation for entity beans. The first motivation is that there is far less code for the bean provider to write. This reduces development time and simplifies software maintenance. The portability and reusability of the entity EJB are also increased because the interaction with the database is not hard-coded into the EJB. The EJB class and the deployment descriptor define the specification for the CMP entity bean. An entity EJB using CMP will provide <cmp-field> tags in its deployment descriptor for each field being persisted by the container. The corresponding setters and getters for the container-managed fields in the EJB class are written as abstract methods. The “Container-Managed Persistence Example” section later in this chapter contains sample code using abstract setters and getters. Container-Managed Persistence is generally used in conjunction with Container-Managed Relationships. The need for CMR arises when an entity bean has another entity bean as an attribute. The container must perform the loading and persistence of the encapsulated entity beans as well. The composition relationship between entity beans is specified using CMR. CMP with CMR greatly reduces the need to implement Bean-Managed Persistence.

The primary advantage of implementing BMP is having flexibility and control over persistence. Having this control over the implementation of persistence allows the bean provider to use vendor-specific extensions or stored procedures. The debugging of problems with persistence is simpler with BMP because the interaction with the database is in the entity bean, rather than in the container. In addition, the implementation may contain particularly complex relationships between the objects being persisted. For example, if very complex many-to-many relationships exist between objects, using BMP is often necessary. Another need for BMP occurs when a proprietary scheme is being used for the stored data. This is the case if something other than a relational database supported by the WebLogic Server is being used for data persistence.

Tip

The persistence type is a per-bean property. This means that the same application can have both CMP and BMP entity beans. There are specific advantages to each methodology. The application should exploit the advantages of the persistence mechanisms that best suit the entity bean.

Note

A BMP entity bean cannot be used in a container-managed relationship. Both entities playing a role in the container-managed relationship must be implemented using CMP.

Container-Managed Relationships

The Enterprise JavaBeans 2.0 specification introduced Container-Managed Relationships. This new capability of the container, to manage complex relationships between objects, greatly reduced the need for bean providers to implement Bean-Managed Persistence. Prior to the availability of CMR, the only way to maintain complex relationships in persistent storage was for the bean providers to do it themselves, hence BMP. CMR enhances the services provided by the container, allowing the object relationships to be specified in the ejb-jar.xml deployment descriptor with the <relationships> tag. Now CMP working hand in hand with CMR can fully specify the object-to-relational mapping in the deployment descriptor rather than the bean provider implementing these relationships in the Java code of the entity bean.

The design model that is directly addressed by CMR is a composition or aggregation relationship between entity beans. In UML terminology, this is referred to as the “has” relationship between two or more entity beans. The situation occurs when an entity bean has another entity bean as one of its attributes.

Note

▸ For further information on composition and aggregation class relationships, see “Association Relationships,” p. 108.

Prior to the availability of CMR, the container did not handle the persistence or loading of the contained entity beans. The bean provider had to either write additional code in the ejbCreate(), ejbActivate(), and ejbPassivate() methods when using CMP, or write the entire entity bean using BMP. When the composition between entity beans forms an aggregate relationship where the life cycle of the dependent object couldn’t exist outside of the context of the independent object, the implementation of ejbStore(), ejbLoad(), and ejbRemove() also requires specific code to persist and restore the dependent entity bean. The EJB 2.0 specification alleviates this situation through Container-Managed Relationships.

Refer to the section “Container-Managed Persistence Example” later in this chapter for a complete description of the EntityBean interface for CMP.

The container-managed fields of the entity bean do not appear directly in the entity bean as attributes. Instead, abstract getters and setters are used to identify the fields that are under the control of Container-Managed Persistence. The deployment descriptor contains <ejb-relation> tags to specify the multiplicity of the relationship. A relationship with multiplicity one will return the local interface of the contained entity bean. When the multiplicity of the relationship is many, the return type for the getter is specified as either java.util.Collection or java.util.Set.

Listing 21.1 shows a snippet from the ejb-jar.xml deployment descriptor specifying a relationship between a shopping cart and books. This is a one-to-many relationship: one shopping cart has many books. The relationship being established is a ShoppingCart with a collection of Books as an attribute that is also entity beans. CMP is used for the persistence of both the ShoppingCart and the Books. A container-managed relationship requires the specification of two relationship roles describing the relationship from the perspective of each entity. The ShoppingCart-Has-Books role has a mulitiplicity of many. The Book-Has-ShoppingCart role has a mulitiplicity of one.

Example 21.1. The ejb-jar.xml Deployment Descriptor Specifies Container-Managed Relationships in the <relationships> Tag

  <relationships>
    <ejb-relation>
      <ejb-relation-name>ShoppingCart-Book</ejb-relation-name>
      <ejb-relationship-role>
        <ejb-relationship-role-name>ShoppingCart-Has-Books</ejb-relationship-role-name>
        <multiplicity>many</multiplicity>
                <relationship-role-source>
          <ejb-name>BookStore</ejb-name>
        </relationship-role-source>

        <cmr-field>
          <cmr-field-name>cart</cmr-field-name>
        </cmr-field>

      </ejb-relationship-role>
      <ejb-relationship-role>
        <ejb-relationship-role-name>Book-Has-ShoppingCart</ejb-relationship-role-name>
        <multiplicity>one</multiplicity>
                <relationship-role-source>
          <ejb-name>ShoppingCart</ejb-name>
        </relationship-role-source>

        <cmr-field>
          <cmr-field-name>books</cmr-field-name>
          <cmr-field-type>java.util.Collection</cmr-field-type>
        </cmr-field>

      </ejb-relationship-role>
    </ejb-relation>
  </relationships>

Container-Managed Transactions

Transaction management specifies when a transaction is started, suspended, resumed, committed, and rolled back. This is referred to as the demarcation of transactions. The proper use of transactions is key to maintaining reliable persistent data that can be trusted to be accurate. To ensure that the data is accurate in the database, before a group of updates is performed on the database, a transaction is started. If all updates complete successfully, the transaction is committed; otherwise, a rollback forces all updates to be discarded as a group. This guarantees that no partial modifications are made to the data. Either all updates are performed, or none are performed. Transaction management is performed using the Java Transaction API (JTA).

Note

▸ For further information on transactions, see Chapter 16, “Managing Java Transactions Using JTA,” p. 527.

You don’t need to compare container-managed transactions versus bean-managed transactions with respect to entity beans. The EJB 2.0 specification requires that all entity EJBs use container-managed transactions. Session beans and servlets are allowed to perform bean-managed transactions by directly starting transactions through the javax.transaction.UserTransaction object. All entity beans, whether BMP or CMP, must use container-managed transactions. The following methods must therefore never be used in an entity bean:

  • commit(), setAutoCommit(), and rollback() methods of java.sql.Connection

  • getUserTransaction() method of javax.ejb.EJBContext

  • Any methods of javax.transaction.UserTransaction

The XML deployment descriptor contains the declarative rules for specifying transaction demarcation. Six keywords are used to specify the rules for transaction demarcation. The transaction is applied on a per-method basis of the entity bean. The deployment descriptor allows a wildcard specification of the method name to set the same default transaction management to all methods of the entity bean. Individual methods may still override the default behavior by setting a different transaction attribute for those methods. Table 21.1 lists the transaction attributes.

Table 21.1. Keyword Definitions for Transaction Attributes

<trans-attribute>

 

Keyword

Definition

Never

The method call never participates in transactions. If the method is called within a transaction, the EJB container throws RemoteException.

NotSupported

The method call never participates in transactions. If the method is called within a transaction, the EJB container suspends the transaction during the method call and resumes the transaction when the method completes.

Supports

The method call participates in the transaction if one has been started.

Mandatory

The method call must participate in a transaction. If a transaction has not been started, the EJB container throws TransactionRequiredException.

Required

If a transaction has already been started, the method call will participate in that transaction; otherwise, the EJB container will start a new transaction.

RequiresNew

The EJB container always starts a new transaction for the method call. The new transaction is committed when the method call completes.

Note

The default transaction attribute for the BEA WebLogic Container is Supports.

The deployment descriptor, ejb-jar.xml, defines the attributes for container-managed transactions. Listing 21.2 shows a snippet from a deployment descriptor that specifies the Required transaction attribute for all methods in the ShoppingCartEJB entity bean. The <container-transaction> elements are children of the <assembly-descriptor>.

Example 21.2. Sample Snippet of a Container Transaction from ejb-jar.xml

<container-transaction>
  <method>
    <ejb-name>ShoppingCart</ejb-name>
    <method-name>*</method-name>
  </method>
  <trans-attribute>Required</trans-attribute>
</container-transaction>

Individual methods of the ShoppingCart entity bean may override the default transaction attribute by adding additional <container-transaction> specifications as children of the <assembly-descriptor> (see Listing 21.3).

Example 21.3. Container Transaction for a Specific Method

<container-transaction>
  <method>
    <ejb-name>ShoppingCart</ejb-name>
    <method-name>addBook</method-name>
    <method-params>
      <method-param>com.objectmind.BookStore.BookEntityVO</method-param>
    </method-params>
  </method>
  <trans-attribute>Supports</trans-attribute>
</container-transaction>

Container-managed transactions are demarcated on method call boundaries. The transaction attribute specifies the action performed by the EJB container when the method is called. If a new transaction is started, the transaction is committed when the method returns.

Rolling Back Container-Managed Transactions

The transaction demarcation is defined by the <trans-attribute> specification for the methods of the entity bean. Every transaction is either committed or rolled back. The EJB container will roll back container-managed transactions under two conditions. First, if a RuntimeException is thrown after the transaction is started, the transaction is automatically rolled back. Second, invoking the setRollbackOnly() method on the EJBContext enables the bean provider to cause a rollback when an application-specific exception is thrown. For example, the BookStoreEJB will roll back the transaction if the requested book is out of stock in the purchase method. Entity beans are provided with the javax.ejb.EntityContext through their setEntityContext() and unsetEntityContext() methods.

For example, the purchase() method for a BookStoreEJB is written, as shown in Listing 21.4, to throw an application-specific exception and roll back the container-managed transaction if the book is out of stock.

Example 21.4. The setRollbackOnly() Method of EJBContext Is Used to Roll Back a Container-Managed Transaction

    public void addBook(BookEntityVO bookData)
        throws NamingException, CreateException, FinderException
    {
        try
        {
            // use JNDI to lookup the Home interface for Book
            Context jndiContext = new InitialContext();
            BookEntityHome bookHome =
                (BookEntityHome)jndiContext.lookup("cmp.BookEntity");

            // make sure the book is in stock before adding
            // it to the shopping cart
            BookEntity book =
                bookHome.findBookByTitle( bookData.getTitle() );

            // create a new Book entity
            book = bookHome.create(bookData);

            // add the Book reference to our book list
            Collection books = getBooks();
            books.add(book);
        }
        catch( FinderException x )
        {
            ctx.setRollbackOnly();
            throw x;
        }
        catch( RemoteException x )
        {
        }
    }

Tip

After a transaction has been set to the “rolled back” state, all persistent activities thereafter will be rolled back for this transaction. Therefore, as a best practice the bean provider might want to check the state of the transaction using the getRollbackOnly() method on the EJBContext before further processing.

The Lifecycle of Entity Beans

From the perspective of the bean provider, the entity bean exists when the create() method of the home interface is called. The create() method of the remote home interface returns a reference to the remote interface. The create() method of the local home interface returns a reference to the local interface.

The components of the entity bean are discussed in more detail in the next section. This early introduction to the remote and local home interfaces and the remote and local interfaces aids in the discussion of the lifecycle of an entity bean. All four of these components are interfaces, not classes. The implementation classes for the entity bean interfaces are generated by WebLogic Server when the bean is deployed. The lifecycle of the entity bean object is controlled by the WebLogic EJB container. As shown in Figure 21.1, the container maintains a pool of objects that are reused and dynamically associated with an entity bean. This allows the container to provide high-performance bean management by pre-allocating beans at system startup.

The EJB container manages the free pool of entity beans that are dynamically assigned when the client requests the creation of an entity bean instance.

Figure 21.1. The EJB container manages the free pool of entity beans that are dynamically assigned when the client requests the creation of an entity bean instance.

Figure 21.1 shows the sequence of events that occur in the lifecycle of an entity bean object:

  1. The container populates the free pool with a set of Entity Beans.

  2. The client invokes the create() method on the home object.

  3. The home object allocates a bean from the free pool.

  4. The home object invokes the ejbCreate() method on the entity bean, passing the same arguments from create() in step 2.

  5. The entity bean inserts a row into the database table.

  6. The bean returns the primary key of the entity bean object to the home object.

  7. The container creates the EJB object for the entity bean. The primary key is immediately passed to the EJB object.

  8. The home object invokes the ejbPostCreate() method on the entity bean to allow the entity bean to finish initialization with the existence of the EJB object.

  9. The home object returns the remote reference of the EJB object to the client.

  10. The client uses the remote reference to invoke business logic in the bean instance.

When the container creates the entity bean instances to populate the free pool, it provides the entity bean with a reference to the EntityContext through the setEntityContext() method of the EntityBean interface. The EntityContext gives the entity bean access to the container provided at runtime. Through methods of the EntityContext, the entity bean can invoke getEJBLocalObject(), getEJBObject(), and getPrimaryKey(). The entity bean object is said to be in the pooled state because it is not yet associated with the persistence of any particular entity bean.

When the application calls the create() method on the home interface of an entity bean, the container selects an instance of a pooled entity bean and calls the corresponding ejbCreate() and ejbPostCreate() methods on the entity bean. The entity bean is now in the ready state and is used to manage persistence. The entity bean can transition back to the pooled state under three conditions:

  • The container calls ejbPassivate() to disassociate the entity bean from the persistent storage.

  • The client calls the remove() method on the home interface which causes the container to call the ejbRemove() method on the entity bean.

  • The entity bean calls ejbRemove() as a result of rolling back a transaction.

An entity bean that is in the pooled state as a result of passivation can transition back to the ready state through activation. To activate an entity bean that is not associated with a bean instance, the container calls the ejbActivate() method followed by the ejbLoad() method.

The container can remove an entity bean from the bean pool by calling the unsetEntityContext() on the bean. This only occurs on a pooled bean, not a ready bean.

Developing the Components of Entity EJBs

Whether the entity bean type is CMP or BMP, the components are the same; only the implementation and contents of the deployment descriptor differ. The components of an entity EJB are

  • Remote interface

  • Local interface

  • Remote home interface

  • Local home interface

  • Entity bean implementation

  • Primary key

  • Deployment descriptors

The local interfaces for entity EJBs were introduced with EJB 2.0. Previously, only session beans provided local interfaces. The local interface provides a high-performance communication path between the client and EJB when they reside within the same Java Virtual Machine. The local interface avoids the overhead associated with Remote Method Invocation (RMI), allowing the client to directly invoke methods of the EJB. This capability is particularly valuable for an entity bean that contains other entity beans as its attributes. The entity bean will communicate through the local interface, rather than the remote interface, resulting in much higher performance. For further information on RMI, see Chapter 12, “Distributed Processing Using RMI.”

Following the RMI protocol, the components of an EJB include a remote interface, home interface, and remote object. The EJB remote interface is the proxy used by the client to communicate with the EJB remote object. The EJB home interface is the factory to create an instance of an object that implements the remote interface. The EJB class, remote interface, and home interface are the components used to establish communication between the EJB client and the entity EJB. The remaining components for an entity EJB are the primary key classes and deployment descriptors. Unlike a session EJB, a primary key class is a required component for an entity EJB.

An entity EJB establishes and maintains a mapping to persistent storage in a database. When the entity bean is created, through the create() method of the home interface, a corresponding database insert operation is performed. When the entity bean is removed, through the remove() method of the home interface, a database delete operation is performed. A simple entity bean will map to a single row in a database table. The attributes of the entity bean correspond to the columns in the table. More complex implementations can be mapped by the bean provider, using Bean-Managed Persistence, or by the container using Container-Managed Persistence with Container-Managed Relationships.

In order to develop the components for an EJB, the Java2 Enterprise Edition is required. Specifically, j2ee.jar contains the classes in the javax.ejb package. Your EJB components establish inheritance and composition relationships with the classes in the java.rmi and javax.ejb classes. It is valuable to gain an understanding of the J2EE interfaces that are being used by the EJB. Table 21.2 provides a brief description of the API for selected interfaces that are used by entity bean providers from the javax.ejb package.

Note

WebLogic Server 7.0 provides the J2EE classes in eaweblogic700serverlibweblogic.jar. This jar file replaces j2ee.jar when developing on WebLogic Server.

Table 21.2. The API for Selected Interfaces from the javax.ejb Package

Interface

Description

javax.ejb.EJBObject

Public interface EJBObject

 

Extends java.rmi.Remote

 

The remote interface of an EJB must extend the EJBObject interface. Refer to the “EJB Remote Interface” section later in this chapter for more information.

javax.ejb.EJBHome

Public interface EJBHome

 

Extends java.rmi.Remote

 

The remote home interface of an EJB must extend the EJBHome interface. Refer to the “EJB Remote Home Interface” section later in this chapter for more information.

javax.ejb.EntityBean

Public interface EntityBean

 

Extends EnterpriseBean

 

The EntityBean interface is implemented by every entity enterprise bean class. The container uses the EntityBean methods to notify the enterprise bean instances of the instance’s life-cycle events.

 

The entity EJB class must implement the EntityBean interface. Refer to the “Entity EJB Class” section later in this chapter for more information.

javax.ejb.EntityContext

Public interface EntityContext

 

Extends EJBContext

 

The entity EJB accesses the container runtime context through the EntityContext interface. Refer the “Bean-Managed Persistence Example” and the “Container-Managed Persistence Example” sections later in this chapter for more information.

EJB Remote Interface

The remote interface defines the public API for the entity bean. The bean provider writes the remote interface, and the WebLogic EJB container implements the interface when the bean is deployed. The purpose of an entity EJB is to provide persistent storage for the enterprise application. The methods in the remote interface are therefore getter and setter methods for the attributes in the data model of this entity bean. The remote interface must be public and must extend the javax.ejb.EJBObject interface. Following the requirements of RMI, each method in the remote interface must declare that it throws RemoteException. As with any JavaBean interface, the designer chooses which attributes are read/write and which are read-only. The read/write attributes will have get and set methods; the read-only attributes will have only a get method.

The bookstore example was first presented in Chapter 13, “Accessing Data Repositories Using JDBC.” In the new example, shown in Listing 21.5, a book is created with a title, author, and price. After the book is created, the title and author cannot be changed. These will be read-only attributes. The only attribute that can be changed is the price. This API determines that title and author will have only get methods; the price will have get and set methods.

Example 21.5. Remote Interface for the BookEntity EJB

/**
* Remote interface for the BookEntity EJB
*/
package com.objectmind.BookStore;

import java.rmi.RemoteException;
import javax.ejb.EJBObject;

public interface BookEntity extends EJBObject
{
    public String getTitle() throws RemoteException;
    public String getAuthor() throws RemoteException;
    public float getPrice() throws RemoteException;
    public void setPrice( float price ) throws RemoteException;
}

EJB Local Interface

There are only two differences between the remote interface and the local interface. The local interface

  • Extends EJBLocalObject

  • Does not throw RemoteException

Listing 21.6 shows the implementation of the local interface for the Book entity bean. Other than these two differences, the BookEntity and BookEntityLocal interfaces declare the same methods.

Example 21.6. Local Interface for the BookEntity EJB

/**
* Local interface for the BookEntity EJB
*/
package com.objectmind.BookStore;

import javax.ejb.EJBLocalObject;

public interface BookEntityLocal extends EJBLocalObject
{
    public String getTitle();
    public String getAuthor();
    public float getPrice();
    public void setPrice( float price );
}

Using the EJB Value Object

In a multitier architecture, each invocation of an EJB method (Session or Entity bean) from the presentation tier, is a network overhead. As the number of remote method invocations increases, the probability for the application to incur a degrade in performance also increases. In such a scenario, a more performance-oriented approach to configure an entity bean is to create a value object class. The value object is written as a standard JavaBean with private attributes and public getters and setters. A common naming convention is to add the letters VO to the end of the name of the entity bean class. BookEntityVO would therefore be the value object for the BookEntity EJB. The attributes of the value object are the same as the attributes of the entity bean. Rather than passing individual parameters to the create() method of the home interface, you pass the single value object, which reduces the number of remote method calls. Following this implementation, the ejbCreate() and ejbPostCreate() methods of the entity bean would also have the value object as their single parameter.

Tip

Rather than passing many parameters to create(), ejbCreate(), and ejbPostCreate(), implement a simple JavaBean to act as a value object.

The value object for the BookEntity EJB contains the title, author, price, and ID for the book (see Listing 21.7).

Example 21.7. Sample Value Object for the BookEntity EJB

/**
* Value object for the BookEntity EJB
*/
package com.objectmind.BookStore;

import java.io.Serializable;

public class BookEntityVO implements Serializable
{
    private String bookID;
    private String title;

    private String author;
    private float  price;

    public String getBookID() { return bookID; }
    public void   setBookID(String value) {
        bookID = value;
    }

    public String getTitle() { return title; }
    public void   setTitle(String value) {
        title = value;
    }

    public String getAuthor() { return author; }
    public void   setAuthor(String value) {
        author = value;
    }

    public float getPrice() { return price; }
    public void   setPrice(float value) {
        price = value;
    }
}

EJB Remote Home Interface

The EJB remote home interface is a factory that is responsible for providing an instance of an object that implements the remote interface. The reference is either created as a new instance using the create() method, or an existing instance can be located using finder methods. Only remote home interfaces provide finder methods to locate and use existing beans. This allows multiple clients to share the same entity bean. As with the remote interface, the bean provider writes the home interface, and the WebLogic EJB container implements the home interface when the bean is deployed.

A common naming convention for the home interface is to simply append Home to the name of the remote interface. The home interface for the BookEntity is therefore BookEntityHome. The home interface must be a public interface that extends javax.ejb.EJBHome.

The create() method in the home interface is effectively a constructor. The parameters to create() are application dependent and used to initialize the EJB. The return type for create() is the remote interface, which extends EJBObject. The calling client then invokes methods on the bean via the remote interface. Refer to Figure 21.1 for a diagram showing the EJBObject and the EJB entity bean. The create() method is declared with a throws clause for javax.ejb.CreateException and java.rmi.RemoteException.

The home interface also provides finder methods to locate instances of existing entity EJBs. There must be a method named findByPrimaryKey() whose parameter is the same type as the primary key. Additional finder methods are implemented to provide application-specific features for the API of the entity EJB. For example, BookEntityHome provides a finder method to return a collection of all books by a given author. The return type for the finder methods is the remote interface for a single instance match, or a java.util.Collection to return a list of matching instances.

Note

The create() method in the home interface may be overloaded for different parameter lists. The EJB class must have ejbCreate() methods along with their corresponding ejbPostCreate() methods overloaded with the same parameter lists as the create() methods.

Listing 21.8 includes examples for both return types from finder methods. The findByPrimaryKey() method returns the Book remote interface of the object that has the unique primary key. The findBooksByAuthor() method returns a java.util.Collection of remote objects matching the requested author.

Note

For BMP, the bean provider must implement the finder methods declared in the home interface through the coding of its corresponding ejbFind... methods. Although, in CMP, the ejbFind... methods cannot be declared in the bean, the methods must be declared in the bean’s home interface. The container will provide the implementation.

Example 21.8. Sample Remote Home Interface for the BookEntity EJB

/**
* BookEntityHome is the home interface for the BookEntity EJBObject.
*/
package com.objectmind.BookStore;

import java.rmi.RemoteException;
import java.util.Collection;
import javax.ejb.EJBHome;
import javax.ejb.CreateException;
import javax.ejb.FinderException;

public interface BookEntityHome extends EJBHome
{
    /**
    * Create method for  BookEntity
    * @param value       value object BookEntityVO
    * @return BookEntity EJBObject
    */
    public BookEntity create(BookEntityVO value)
        throws CreateException, RemoteException;

    /**
    * Finder method by primary key
    * @param  key    book title is the primary key
    * @return Book object matching primary key
    */
    public BookEntity findByPrimaryKey( BookEntityPK key )
        throws FinderException, RemoteException;

    /**
    * Finder method to create a list of books by author
    * @param author    book author to match
    * @return collection of BookEntity objects in a Vector
    */
    public Collection findBooksByAuthor( String author )
      throws FinderException, RemoteException;

    /**
    * Finder method to find a book by title
    * @param title    book title to match
    * @return collection of BookEntity objects in a Vector
    */
    public BookEntity findBookByTitle( String title )
      throws FinderException, RemoteException;
}

EJB Local Home Interface

There are only three differences between the remote home interface and the local home interface. The local home interface

  • Extends EJBLocalHome

  • Creates and finds the local interface

  • Does not throw RemoteException

Listing 21.9 shows the implementation of the local home interface for the Book entity bean. Other than these three differences, the BookEntityHome and BookEntityLocalHome interfaces declare the same methods.

Example 21.9. Local Home Interface for the BookEntity EJB

/**
* BookEntityLocalHome is the home interface for the BookEntity EJBObject.
*/
package com.objectmind.BookStore;

import java.util.Collection;
import javax.ejb.EJBLocalHome;
import javax.ejb.CreateException;
import javax.ejb.FinderException;

public interface BookEntityLocalHome extends EJBLocalHome
{
    /**
    * Create method for  BookEntity
    * @param value       value object BookEntityVO
    * @return BookEntity EJBObject
    */
    public BookEntityLocal create(BookEntityVO value)
        throws CreateException;

    /**
    * Finder method by primary key
    * @param  key    book title is the primary key
    * @return BookEntity object matching primary key
    */
    public BookEntityLocal findByPrimaryKey( BookEntityPK key )
        throws FinderException;

    /**
    * Finder method to create a list of books by author
    * @param author    book author to match
    * @return collection of BookLocal objects
    */
    public Collection findBooksByAuthor( String author )
      throws FinderException;

    /**
    * Finder method to find a book by title
    * @param title    book title to match
    * @return collection of BookEntity objects in a Vector
    */
    public BookEntityLocal findBookByTitle( String title )
      throws FinderException;

}

Writing the Entity EJB Class

The EJB class provides the API for mapping the attributes of the entity bean to the persistent storage. The naming convention for the EJB class used in this chapter is to append BMPBean or CMPBean to the name of the remote interface. The EJB class for Book is therefore BookBMPBean for the BMP example and BookCMPBean for the CMP example. The EJB class must be a public class that implements the javax.ejb.EntityBean interface.

The code written by the bean provider for the implementation of the entity bean is significantly different depending on whether BMP or CMP is being used to manage persistence. The first notable difference is that a CMP entity EJB is written as an abstract class, whereas a BMP entity EJB is fully implemented by the bean provider as a concrete class. This is true because the getters and setters for the container-managed fields are abstract methods when CMP is used.

Refer to the “Bean-Managed Persistence Example” for further information on implementing BMP, and the “Container-Managed Persistence Example” for further information on implementing CMP.

Writing the Primary Key Class

Entity EJBs must include a primary key that is used to uniquely identify an instance of the entity bean. The entity bean returns an instance of the primary key class from the ejbCreate() method. The primary key class may be a standard Java class, such as java.lang.String or java.lang.Integer.

The bean provider may optionally provide a specific primary key class. The naming convention for the primary key class is to add the letters PK to the end of the remote interface name. The primary key for BookEntity is therefore BookEntityPK. The following rules must be followed when implementing a specific primary key class; it must

  • Implement java.io.Serializable.

  • Be constructed with the public default constructor (no parameters).

  • Contain a public attribute that has the same name and type as the primary key attribute in the entity bean.

  • Override the equals() method to compare based on the primary key attribute.

  • Override the hashCode() method to generate the hashcode based on the value of the primary key.

Tip

If the EJB deployment descriptor uses <automatic-key-generation>, the primary key type must be java.lang.Integer. Do not write a specific primary key class for the entity bean if you intend to use automatic key generation. This option is explained in the section, “The weblogic-cmp-rdbms-jar.xml Persistence Descriptor” later in this chapter.

Creating the BookEntityPK Primary Key Class

The primary key for BookEntity is the bookId field, which is a String. The implementation for the specific primary key class for BookEntity contains the bookId as a public attribute and overrides the equals() and hashCode() methods. Listing 21.10 shows the implementation of the primary key class for the BookEntity EJB.

Example 21.10. Sample Primary Key Class for the BookEntity EJB

/**
* Primary Key for the BookEntity EJB
*/
package com.objectmind.BookStore;

import java.io.Serializable;

public class BookEntityPK implements Serializable
{
    // primary key attribute
    public String bookID;

    /**
    * override equals() to compare against bookId
    */
    public boolean equals( Object that )
    {
        if((that == null ) || !(that instanceof BookEntityPK))
            return false;

        return bookID.equals( ((BookEntityPK)that).bookID );
    }

    /**
    * override hashCode()
    */
    public int hashCode()
    {
        return bookID.hashCode();
    }
}

Deployment Descriptors

The deployment descriptor has the name ejb-jar.xml. The XML tags in the deployment descriptor specify the following information for an entity EJB:

  • EJB name

  • EJB home interface

  • EJB remote interface

  • EJB class

  • Persistence type

  • CMP Fields

  • Primary key

  • Query methods

The following sections provide the definition and usage for each of these XML tags.

EJB Name Tag

The EJB name is the unique name that identifies the EJB. This is the name that is used by the client to locate the entity EJB through the Java Naming and Directory Interface (JNDI).

The session bean client must look up the home interface of the entity EJB using JNDI. This name is specified in the deployment descriptor file and is bound to the corresponding home interface by WebLogic Server in the JNDI tree during deployment of the EJB.

The XML tag for the EJB name is

<ejb-name>EntityBeanEJB</ejb-name>

Additional XML Tags in the Deployment Descriptor

In addition to the EJB name, the deployment descriptor specifies the names of the home interface, remote interface, EJB class, and persistence type. In the case of a CMP entity bean, the container-managed fields are also specified. A brief description of the XML tags with a sample of the tag follows. Examples of complete deployment descriptors are provided in “Creating the BMP Style Deployment Descriptor” and “Creating the CMP Style Deployment Descriptor” later in this chapter.

EJB Home Interface Tag

The XML tag for the EJB home interface is

<home>package_name.EntityBeanHome</home>

EJB Remote Interface Tag

The XML tag for the EJB remote interface is

<remote>package_name.EntityBean</remote>

EJB Class Tag

The XML tag for the EJB class is

<ejb-class>package_name.EntityBeanEJB</ejb-class>

Persistence Type Tag

The XML tag for the CMP persistence type is

<persistence-type>Container</persistence-type>

The XML tag for the BMP persistence type is

<persistence-type>Bean</persistence-type>

CMP Field Tags

The XML tags for the CMP fields are

<cmp-field>
  <field-name>field1_name</field-name>
</cmp-field>

EJB Primary Key Field

The XML tag for the EJB primary key field is

<primkey-field>prim_key_field_name</primkey-field>

When using CMP, the primary key must be a cmp-field:

<cmp-field>
  <field-name>myPrimaryKey</field-name>
</cmp-field>
<primkey-field>myPrimaryKey</primkey-field>

Query Tag

The XML tag for the query is

<query>
  <description>brief description</description>
  <query-method>
    <method-name>queryMethodName</method-name>
    <method-params>
      <method-param>first_param_type</method-param>
      <method-param>second_param_type</method-param>
    <method-params>
  </query-method>
 </query>

Writing Finder Methods

The finder methods allow multiple clients to share an entity EJB. This is a difference between session beans and entity beans. Sessions beans cannot be shared and therefore do not have finder methods. All entity beans must contain a findByPrimaryKey() method in their home interfaces. The findByPrimaryKey() method in the remote home interface will return an instance of the remote interface. The findByPrimaryKey() method in the local home interface will return an instance of the local interface. The parameter to findByPrimaryKey() is used to match against the primary key of an existing instance of the entity bean. The deployment descriptor of the entity bean specifies the <prim-key-class>. The parameter must therefore be of the same class type as the primary key class. In a BMP implementation, the entity bean will have a corresponding method named ejbFindByPrimaryKey() that takes the same parameter as findByPrimaryKey(); however, the ejbFindByPrimaryKey() return type is the primary key where the findByPrimaryKey() return type is the remote interface.

The bean provider can supply additional finder methods in the home interfaces, both local and remote. The return type must be the same as the return type for findByPrimaryKey(). The parameters are application specific to provide convenient access for multiple clients to share entity beans. Each findByXXX() method in the home interface will have a corresponding ejbFindByXXX() method in the entity bean. The return value for ejbFindByXXX() is the primary key of the matching entity bean. In a BMP implementation, the ejbFindByXXX() methods will execute SQL SELECT statements to find the matching entry in the relational database. Refer to the “Bean-Managed Persistence Example” later in this chapter for an example of the findByPrimaryKey() method.

Enterprise JavaBean Query Language

For Container-Managed Persistence, EJB 2.0 introduced EJB-QL, the Enterprise JavaBean Query Language. This standard query language enhances portability over proprietary query languages. The WebLogic Query Language (WLQL) provides extensions to EJB-QL; however, you cannot use WLQL with entity beans that use EJB 2.0 features or EJB 2.0 deployment descriptors. Rather than coding SELECT statements directly in the entity bean, the deployment descriptor contains an <ejb-ql> tag that specifies the query to be performed using EJB-QL syntax. The ShoppingCart in the “Container-Managed Relationship Example” uses EJB-QL to implement the findAll() method that returns a java.util.Collection of all the books in the shopping cart. The following components provide the implementation of a finder method that uses EJB-QL; it must

  • Provide application-specific finder methods in the home interfaces

  • Include the <ejb-ql> tag following the <query-method> in the deployment descriptor

An example of an EJB-QL query method is shown in Listing 21.11. This example was extracted as a snippet from the deployment descriptor for the book store example.

Example 21.11. EJB-QL Allows You to Place the SQL Statement for Finder Methods in the Deployment Descriptor

      <query>
        <query-method>
          <method-name>findAll</method-name>
          <method-params>
          </method-params>

        </query-method>

        <ejb-ql><![CDATA[SELECT OBJECT(s) FROM ShoppingCartSchema
                   AS s]]></ejb-ql>
      </query>

The XML syntax uses the special tag <![CDATA[...]]>. It tells the XML parser to treat the EJB-QL statement literally and to not attempt to parse it as XML. The EJB-QL statement is entered between the square brackets of the CDATA.

Bean-Managed Persistence Example

This BMP example illustrates what occurs when the methods of an entity EJB are invoked. The features that are common for all BMP entity beans are that they

  • Implement the EntityBean interface

  • Maintain an EntityContext attribute

  • Retrieve the primary key from the EntityContext

  • Create a connection with the data source

  • Insert a row into the database for ejbCreate()

  • Refresh attributes from the database for ejbLoad()

  • Update a row in the database for ejbStore()

  • Delete a row in the database for ejbRemove()

  • Select rows for ejbFindXXX()

  • Initialize resources from ejbActivate()

  • Release resources from ejbPassivate()

  • Provide getters and setters for attributes

Creating a Connection with a Data Source

When an entity EJB is created, it establishes a connection with a data source. The connection is used to query, store, and retrieve the persistent data that is being mapped by the entity EJB. As shown in Chapter 13, “Accessing Data Repositories Using JDBC,” the connection may be established using the DriverManager directly. Chapter 16, “Managing Java Transactions Using JTA,” introduced the DataSource and TxDataSource interfaces as features of the JDBC Optional Package. The DataSource or TxDataSource is located by performing a JNDI lookup on the configured name for the object. This isolation from the JDBC driver further increases portability and database independence. The applicaton obtains the database connection from the DataSource instance. In addition to these two methods of establishing a connection with a data source, Chapter 13 showed the configuration for the JDBC Data Source Factory. The DataSource Factory is an instance of a data source resource bound to the JNDI tree. The entity EJB performs a lookup on the JNDI tree to gain access to this resource.

A resource manager is an object that controls access to an enterprise resource. The JDBC DataSource Factory is used to create a connection to a resource manager. The ejb-jar.xml deployment descriptor specifies the resource reference, as shown in Listing 21.12.

Example 21.12. The ejb-jar.xml Deployment Descriptor May Contain a Specification for a Resource Reference

<resource-ref>
  <resource-ref-name>jdbc/ExampleDSRef</resource-ref-name>
  <res-type>javax.sql.Datasource</res-type>
  <res-auth>Container</res-auth>
  <res-sharing-scope>Shareable<res-sharing-scope>
<resource-ref>

The weblogic-ejb-jar.xml deployment descriptor specifies the JNDI name bound to the resource connection factory. Listing 21.13 shows the reference descriptor snippet.

Example 21.13. The weblogic-ejb-jar.xml Deployment Descriptor Specifies the JNDI Name that Is Bound to the Resource Connection Factory

<reference-descriptor>
  <resource-description>
    <res-ref-name>jdbc/ExampleDSRef</res-ref-name>
    <jndi-name>ExampleDS</jndi-name>
  <resource-description>
</reference-descriptor>

With the resource reference specified in the deployment descriptors, the EJB can look up the resource connection factory using JNDI. The code snippet used by the EJB to lookup the resource connection factory to obtain a DataSource is shown in Listing 21.14.

Example 21.14. The EJB Looks Up the Resource Connection Factory Using JNDI

InitialContext ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/ExampleDSRef");
Connection con = ds.getConnection();

Creating the BookEntityBMPBean

The behavior of an entity EJB is defined by the EntityBean interface. The implementation of the entity bean methods defines the object-relational mapping. The BookEntityBean first shown in Chapter 13 focused on the JDBC API. In Listing 21.15, the focus is turned toward the EntityBean interface as implemented using BMP. As mentioned in the section “Container-Managed Persistence Versus Bean-Managed Persistence,” the BMP implementation gives the bean provider full control and flexibility over the connection with the database.

Example 21.15. An Entity EJB Using BMP Takes Full Control over the Interaction with the Database

/**
* BookEntityBMPBean example of a BMP Entity EJB
*/
package com.objectmind.BookStore;

import java.util.Vector;
import java.util.Collection;
import javax.ejb.*;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.PreparedStatement;

/**
 * @ejbHome <{BookEntityHome}>
 * @ejbRemote <{BookEntity}>
 * @ejbPrimaryKey <{BookEntityPK}>
 * @ejbTransactionAttribute Required
 * @persistent*/

// must implement the EntityBean interface
public class BookEntityBMPBean implements EntityBean {

    // the WebLogic Server will provide a context
    private EntityContext context;

    // object attributes
    public  String bookID;    // primary key
    private String title;
    private String author;
    private float  price;

    public void setEntityContext(EntityContext context)
        throws javax.ejb.EJBException {

        // save the EntityContext
        this.context = context;
    }

    public void unsetEntityContext()
        throws javax.ejb.EJBException {

        // clear the EntityContext
        context = null;
    }

    /** ejbActivate is used to initialize any resources
    *   required by the bean, files, sockets, etc...
    */
    public void ejbActivate() throws javax.ejb.EJBException {
    }

    /** ejbPassivate is used to release any resources used
        by the bean
    */
    public void ejbPassivate() throws javax.ejb.EJBException {
    }

    /**
    * ejbCreate() must be overloaded identical with the create()
    * methods of the home interface. The parameter list must match
    * the parameter list of the create() method in the home interface.
    */
    public BookEntityPK ejbCreate(BookEntityVO value)
        throws javax.ejb.CreateException,
               javax.ejb.EJBException {

        bookID = value.getBookID();
        title = value.getTitle();
        author = value.getAuthor();
        price = value.getPrice();

        // insert a row into the database containing
        // this object's attributes
        try {
            Connection con = openConnection();
            PreparedStatement ps = con.prepareStatement(
                "INSERT INTO books "
                + "( book_id, book_name, book_author, book_price ) "
                + "VALUES ( ?, ?, ? )" );
            ps.setString( 1, bookID );
            ps.setString( 2, title );
            ps.setString( 3, author );
            ps.setFloat ( 4, price );
            if( ps.executeUpdate() != 1 ) {
                throw new CreateException(
                    "Failed to create a row in the database" );
            }
            ps.close();
            // return the new primary key on success
            BookEntityPK pk = new BookEntityPK();
            pk.bookID = bookID;
            return pk;
        } catch( Exception x ) {
            throw new CreateException( x.getMessage() );
        }
    }

    /**
    * ejbPostCreate() must be overloaded identical with the
    * ejbCreate() methods. The ejbPostCreate() is automatically
    * called after ejbCreate()
    */
    public void ejbPostCreate(BookEntityVO value)
    {
        // nothing more to do
    }

    /**
    * ejbRemove() deletes row with matching primary key
    */
    public void ejbRemove() throws RemoveException {
        try {
            // always open a new connection
            Connection con = openConnection();
            PreparedStatement ps = con.prepareStatement(
                "DELETE FROM books  " +
                "WHERE book_id = ?" );
            ps.setString( 1, bookID );
            if( ps.executeUpdate() != 1 ) {
                throw new CreateException(
                    "Failed to delete book with id " + bookID );
            }
            ps.close();
        } catch( Exception x ) {
            throw new RemoveException( x.getMessage() );
        }
    }

    public void ejbStore() throws javax.ejb.EJBException {
    }

    public void ejbLoad() throws javax.ejb.EJBException {
    }

    // getters and setters for object attributes
    public String getBookID(){ return bookID; }

    public void setBookID(String param){ this.bookID = param; }

    public BookEntityPK ejbFindByPrimaryKey(BookEntityPK pk)
        throws javax.ejb.FinderException, javax.ejb.EJBException {
            // Write your code here
            // refresh this object's attributes by searching the database
            // for the primary key
            if( pk == null ) {
                throw new FinderException( "primary key cannot be null" );
            }

            try {
                Connection con = openConnection();
                PreparedStatement ps = con.prepareStatement(
                    "SELECT author_id, book_name FROM books " +
                    "WHERE book_id = ?" );
                ps.setString( 1, pk.bookID );
                ps.executeQuery();
                ResultSet rs = ps.getResultSet();
                if( rs.next() ) {
                    bookID = pk.bookID;
                    title = rs.getString( 2 );
                } else {
                    throw new FinderException( "Could not find " + pk.bookID );
                }
                ps.close();
            } catch( Exception x ) {
                throw new FinderException( x.getMessage() );
            }

            return pk;
        }

    public Collection ejbFindBooksByAuthor(String author)
        throws javax.ejb.FinderException,
               javax.ejb.EJBException {
        PreparedStatement ps = null;
        try {
            Connection con = openConnection();
            ps = con.prepareStatement(
                "SELECT book_id FROM books " +
                "WHERE author = ?" );
            ps.setString( 1, author );
            ResultSet rs = ps.executeQuery();
            Vector books = new Vector();
            while( rs.next() ) {
                BookEntityPK key = new BookEntityPK();
                key.bookID = rs.getString( 1 );
            }
            return books;
        } catch( Exception x ) {
            throw new FinderException( x.getMessage() );
        }
        finally {
            closePS( ps );
        }
    }

    public BookEntityVO getBookData(){
        BookEntityVO value = new BookEntityVO();
        value.setBookID( bookID );
        value.setAuthor( author );
        value.setTitle( title );
        value.setPrice( price );

        return value;
    }

    public void setBookData(BookEntityVO value) {
        bookID = value.getBookID();
        author = value.getAuthor();
        title = value.getTitle();
        price = value.getPrice();
    }


    public String getTitle(){ return title; }

    public String getAuthor(){ return author; }

    public double getPrice(){ return price; }

    public void setPrice( float value ) {
        price = value;

        //update database record
                PreparedStatement ps = null;
        try {
            Connection con = openConnection();
            BookEntityPK key =
                (BookEntityPK)context.getPrimaryKey();
            ps = con.prepareStatement(
                "UPDATE books " +
                "set book_price = ? " +
                "WHERE book_id = ?" );
            ps.setFloat( 1, value );
            ps.setString( 2, key.bookID );
            int rows = ps.executeUpdate();
            if( rows != 1 ) {
                throw new EJBException(
                    "setPrice failed to update database" );
            }
        } catch( Exception x ) {
            throw new EJBException( x.getMessage() );
        }
        finally {
            closePS( ps );
        }
    }

    // get Connection from DataSource
    public Connection openConnection() throws EJBException {
        try {
            InitialContext ic = new InitialContext();
            DataSource ds = (DataSource)ic.lookup("MyDataSource");
            return ds.getConnection();
        }
        catch( Exception x ) {
            throw new EJBException( x.getMessage() );
        }
    }

    private void closePS( PreparedStatement ps )
    {
        try
        {
            if( ps != null )
                ps.close();
        }
        catch( SQLException x ) {
        }
    }

}

Creating the BMP Style Deployment Descriptor

The ejb-jar.xml deployment descriptor for the BookEntityBMPBean is shown in Listing 21.16. The JAR file contains a single deployment descriptor in the META-INF directory that defines the deployment characteristics for all the EJBS in the JAR.

Example 21.16. The Deployment Descriptor for a BMP Entity Bean Specifies the Names of the Interfaces for the EJB Class and Defines the Transaction Attributes

<!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN'
'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>

<!-- Generated XML! --><ejb-jar>

<ejb-jar>
  <enterprise-beans>
    <entity>
      <ejb-name>BookEntityBMPBean</ejb-name>
      <home>BookEntityHome</home>
      <remote>BookEntity</remote>
      <local-home>BookEntityLocalHome</local-home>
      <local>BookEntityLocal</local>
      <ejb-class>BookEntityBMPBean</ejb-class>
      <persistence-type>Bean</persistence-type>
      <prim-key-class>BookEntityPK</prim-key-class>
      <reentrant>False</reentrant>
    </entity>
  </enterprise-beans>

  <assembly-descriptor>
    <container-transaction>
      <method>
        <ejb-name>BookEntityBMPBean</ejb-name>
        <method-name>*</method-name>
      </method>
      <trans-attribute>Required</trans-attribute>
    </container-transaction>
  </assembly-descriptor>
</ejb-jar>

Container-Managed Persistence Example

This CMP example illustrates how much work the EJB container does for the entity bean. A CMP implementation has far less code to be implemented by the bean provider. The features that are common for all CMP entity beans are that they

  • Are implemented as an abstract class

  • Implement the EntityBean interface

  • Maintain an EntityContext attribute

  • Provide abstract getters and setters for attributes

  • Initialize the attributes by calling each of the setters from ejbCreate()

  • Implement stubs for ejbPostCreate(), ejbActivate(), ejbPassivate(), ejbLoad(), ejbStore(), and ejbRemove()

A CMP entity bean provides the abstract setters and getters for the container-managed fields of the entity bean. The EJB container performs the persistence of the fields identified in the deployment descriptor as a <cmp-field>. The container derives the type of the field from the type used with the getter and setter. For example, a name field of type String has the <cmp-field> in the deployment descriptor:

<cmp-field>
  <field-name>name</field-name>
<cmp-field>

This corresponds to the abstract methods:

abstract public String getName();
abstract public void setName( String name ) ;

Creating the BookEntityCMPBean

Listing 21.17 shows the BookEntityCMPBean, which is an example of a CMP implementation of the BookEntity EJB. It is an abstract class with abstract setters and getters for the attributes of the entity EJB. The CMP fields for this entity bean are specified in the ejb-jar.xml deployment descriptor. The connection with the data source and the persistence of the container-managed fields are specified in the weblogic-ejb-jar.xml and weblogic-cmp-rdbms-jar.xml deployment descriptors. The deployment descriptors are described in the section, “Deployment on WebLogic Server,” later in this chapter.

Example 21.17. A CMP Entity Bean Is Implemented As an Abstract Class with Abstract Setters and Getters and Stubs for EJB Methods Provided by the Container

/**
* BookEntityCMPBean example of a CMP Entity EJB
*/
package com.objectmind.BookStore;


import com.objectmind.ShoppingCart.*;
import javax.ejb.*;
import java.sql.SQLException;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.PreparedStatement;

/**
 * @ejbHome <{BookEntityHome}>
 * @ejbRemote <{BookEntity}>
 * @ejbPrimaryKey <{BookEntityPK}>
 * @ejbTransactionAttribute Required
 * @persistent*/

// must implement the EntityBean interface
public abstract class BookEntityCMPBean implements EntityBean {

    // the WebLogic Server will provide a context
    private EntityContext ctx;

    public void setEntityContext(EntityContext context)
        throws EJBException {

        // save the EntityContext
        ctx = context;
    }

    public void unsetEntityContext()
        throws EJBException {

        // clear the EntityContext
        ctx = null;
    }

    /**
    * abstract setters and getters for container-managed fields
    */
    abstract public BookEntityPK getBookID();
    abstract public void   setBookID(BookEntityPK key);

    abstract public String getTitle();
    abstract public void   setTitle(String title);

    abstract public String getAuthor();
    abstract public void   setAuthor(String author);

    abstract public float  getPrice();
    abstract public void   setPrice(float title);

    abstract public ShoppingCart getCart();
    abstract public void setCart( ShoppingCart cart ) ;

    /**
    * stubs for container-managed behavior
    */
    public void ejbPostCreate( BookEntityVO book ) {
    }

    public void ejbActivate() throws javax.ejb.EJBException {
    }

    public void ejbPassivate() throws javax.ejb.EJBException {
    }

    public void ejbRemove() throws RemoveException {
    }

    public void ejbStore() throws javax.ejb.EJBException {
    }

    public void ejbLoad() throws javax.ejb.EJBException {
    }

    /**
    * initialize the bean from the value object
    */
    public BookEntityPK ejbCreate(BookEntityVO book) {
        setBookData( book );
        return null;
    }

    /**
     * get the attributes of this entity bean as a value object
     */
    public BookEntityVO getBookData(){
        BookEntityVO value = new BookEntityVO();
        BookEntityPK key = getBookID();
        value.setBookID( key.bookID );
        value.setAuthor( getAuthor() );
        value.setTitle( getTitle() );
        value.setPrice( getPrice() );

        return value;
    }

    /*
     * set the attributes of this entity bean using the value object
     */
    public void setBookData(BookEntityVO value) {
        BookEntityPK key = new BookEntityPK();
        key.bookID = value.getBookID();
        setBookID( key );
        setAuthor( value.getAuthor() );
        setTitle( value.getTitle() );
        setPrice( value.getPrice() );
    }
}

Container-Managed Relationship Example

The Container-Managed Relationship is used when a composition relationship exists between CMP entity beans. This example is a shopping cart that contains the books that have been selected for purchase. The ShoppingCart requires persistent storage to allow its life cycle to span system shutdowns and crashes. The ShoppingCart will be implemented as a CMP entity EJB. The books in the shopping cart establish a one-to-many relationship between the ShoppingCart and the BookEntityCMPBean. The local interfaces of the BookEntity will be used by the ShoppingCart for improved efficiency and higher performance.

Creating the ShoppingCart Remote Interface

The remote interface shown in Listing 21.18 allows books to be added to the cart and all books to be retrieved from the cart as an array. This example uses the BookEntityVO as a convenient way to pass all attributes in a single value object parameter. The addBook() method is overloaded to pass an instance of a BookEntity as the parameter.

Example 21.18. Remote Interface for the ShoppingCart

/**
* Remote interface for the ShoppingCart
*/
package com.objectmind.ShoppingCart;

import com.objectmind.BookStore.*;
import java.rmi.RemoteException;
import javax.ejb.EJBObject;
import javax.ejb.CreateException;
import javax.ejb.FinderException;
import javax.naming.NamingException;

public interface ShoppingCart extends EJBObject
{
    public void addBook(BookEntityVO book)
        throws NamingException, CreateException,
               RemoteException, FinderException;

    public void addBook(BookEntity book)
        throws NamingException, CreateException,
               RemoteException;

    public BookEntityVO[] getAllBooks()
        throws RemoteException;
}

Creating the ShoppingCart Home Interface

The ShoppingCartHome, shown in Listing 21.19, creates a reference to a ShoppingCart for the given primary key. The finder methods find a ShoppingCart by matching a primary key and also find all instances of ShoppingCart.

Example 21.19. Sample Remote Home Interface for ShoppingCart

/**
* ShoppingCartHome is the home interface for the ShoppingCart.
*/
package com.objectmind.ShoppingCart;

import java.rmi.RemoteException;
import java.util.Collection;
import javax.ejb.EJBHome;
import javax.ejb.CreateException;
import javax.ejb.FinderException;

public interface ShoppingCartHome extends EJBHome
{
    /**
    * Create method for ShoppingCart
    * @return BookEntity EJBObject
    */
    public ShoppingCart create()
        throws CreateException, RemoteException;

    /**
    * findByPrimaryKey is required for all entity beans
    * @param  primaryKey  Integer
    * @return ShoppingCart  object matching primary key
    */
    public ShoppingCart findByPrimaryKey(
        Integer primaryKey )
        throws FinderException, RemoteException;

    /**
    * Finder method to create a list of all shopping carts
    * @return collection of BookEntity objects in a Vector
    */
    public Collection findAll()
      throws FinderException, RemoteException;
}

Creating the ShoppingCart CMP Entity Bean

The attributes of the ShoppingCart are its primary key and a collection of books. The primary key is the cartId of type java.lang.String while books are represented through a java.util.Collection. Listing 21.20 shows the CMP entity bean implementation of the shopping cart.

Example 21.20. The ShoppingCartCMPBean Is Implemented As an Abstract Class That Uses Container-Managed Persistence

/**
* ShoppingCartCMPBean example of a CMP Entity EJB
*/
package com.objectmind.ShoppingCart;

import com.objectmind.BookStore.*;
import java.util.Collection;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;
import javax.ejb.EJBException;
import javax.ejb.CreateException;
import javax.ejb.FinderException;
import javax.ejb.RemoveException;
import java.rmi.RemoteException;
import javax.naming.NamingException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.PreparedStatement;

/**
 * @ejbHome <{ShoppingCartHome}>
 * @ejbRemote <{ShoppingCart}>
 * @ejbPrimaryKey <{ShoppingCartPK}>
 * @ejbTransactionAttribute Required
 * @persistent*/

// must implement the EntityBean interface
public abstract class ShoppingCartCMPBean implements EntityBean {

    // the WebLogic Server will provide a context
    private EntityContext ctx;

    public void setEntityContext(EntityContext context)
        throws EJBException {

        // save the EntityContext
        ctx = context;
    }

    public void unsetEntityContext()
        throws EJBException {

        // clear the EntityContext
        ctx = null;
    }

    /**
    * abstract setters and getters for container-managed fields
    */
    abstract public Integer getCartID();
    abstract public void   setCartID(Integer id);

    abstract public Integer getCustomerID();
    abstract public void    setCustomerID( Integer id );

    abstract public Collection getBooks();
    abstract public void   setBooks(Collection books);

    /**
    * stubs for container-managed behavior
    */
    public void ejbActivate() throws javax.ejb.EJBException{
    }

    public void ejbPassivate() throws javax.ejb.EJBException {
    }

    public void ejbRemove() throws RemoveException {
    }

    public void ejbStore() throws javax.ejb.EJBException {
    }

    public void ejbLoad() throws javax.ejb.EJBException {
    }

    /**
    * initialize the bean from the value object
    */
    public Integer ejbCreate() {

        return null;
    }

    public void ejbPostCreate() {
    }

    public void addBook(BookEntityVO bookData)
        throws NamingException, CreateException, FinderException
    {
        try
        {
            // use JNDI to lookup the Home interface for Book
            Context jndiContext = new InitialContext();
            BookEntityHome bookHome =
                (BookEntityHome)jndiContext.lookup("cmp.BookEntity");

            // make sure the book is in stock before adding
            // it to the shopping cart
            BookEntity book =
                bookHome.findBookByTitle( bookData.getTitle() );

            // create a new Book entity
            book = bookHome.create(bookData);

            // add the Book reference to our book list
            Collection books = getBooks();
            books.add(book);
        }
        catch( FinderException x )
        {
            ctx.setRollbackOnly();
            throw x;
        }
        catch( RemoteException x )
        {
        }
    }

    public void addBook(BookEntity book)
        throws NamingException, CreateException
    {
        // add the Book reference to our book list
        Collection books = getBooks();
        books.add(book);
    }

    public BookEntityVO[] getAllBooks()
    {
        Collection books = getBooks();
        return (BookEntityVO[])books.toArray();
    }
}

The ShoppingCart Primary Key

The weblogic-cmp-rdbms-jar.xml deployment descriptor for the ShoppingCart specifies automatic key generation for the primary key. The primary key class for the ShoppingCart is therefore, java.lang.Integer. Refer to the ejb-jar.xml shown in Listing 21.22 to see the specification for the <prim-key-class> for the ShoppingCart. Also, refer to Listing 21.24 to see the specification for <automatic-key-generation> in the weblogic-cmp-rdbms-jar.xml descriptor. These topics are explained in the “Deployment on WebLogic Server” section later in this chapter.

Testing the Book Store

The client of an entity EJB is typically either a session EJB, a JSP or a servlet. The Customer class, shown in Listing 21.21, is provided here as an example to test the API for the BookEntity and ShoppingCart entity EJBs. The customer creates an instance of the home interface for the BookEntity. This BookEntityHome object is used to invoke the finder methods for books. A reference to a ShoppingCart is obtained by looking up the ShoppingCartHome and using it to create a ShoppingCart. The addBook() method of ShoppingCart is invoked when a book is purchased.

Example 21.21. The Customer Class Tests the API for the BookEntity and ShoppingCart Entity EJBs

/**
 * BookStore customer will purchase books by
 * adding them to a shopping cart.
 */
package com.objectmind.BookStore;

import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.ejb.CreateException;
import javax.ejb.DuplicateKeyException;
import java.rmi.RemoteException;
import com.objectmind.ShoppingCart.*;

public class Customer
{
    // object attributes
    private Context         context;
    private ShoppingCart    cart;
    private BookEntityHome    bookFinder;
    private BookEntityVO    bookData;

    public Customer( String title, String author, float price, String id )
        throws NamingException, CreateException, RemoteException
    {
        // create the JNDI context
        context = createJNDIContext();

        // create the book entity home to
        // use its finder methods for books
        bookFinder = createBookFinder();

        bookData = new BookEntityVO();
        bookData.setTitle( title );
        bookData.setAuthor( author );
        bookData.setBookID( id );
        bookData.setPrice( price );

        // create a sample book
        try
        {
            bookFinder.create( bookData );
        }
        catch( RemoteException x )
        {
            // the DuplicateKeyException is nested as an RemoteException
            System.err.println( "Book already exists" );
        }
        catch( Exception x )
        {
            System.err.println( x.getMessage() );
        }

        // create a shopping cart
        cart = createShoppingCart();
    }

    private Context createJNDIContext()
        throws NamingException
    {
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
        env.put(Context.PROVIDER_URL,"t3://localhost:7001");

        Context context = new InitialContext( env );
        return context;
    }

    public BookEntityHome createBookFinder()
        throws NamingException, CreateException
    {
        // use JNDI to lookup the Home interface for Book
        BookEntityHome bookHome =
            (BookEntityHome)context.lookup("cmp.BookEntity");

        return bookHome;
    }

    public ShoppingCart createShoppingCart()
        throws NamingException, CreateException, RemoteException
    {
        // use JNDI to lookup the Home interface for Book
        ShoppingCartHome cartHome =
            (ShoppingCartHome)context.lookup("cmp.ShoppingCart");

        ShoppingCart cart = cartHome.create();

        return cart;
    }

    public void purchase()
        throws Exception
    {
        cart.addBook( bookData );

        // if no exceptions were thrown...
        System.out.println( "Thank you for purchasing " + bookData.getTitle() );
    }

    /**
     * Main method to unit test the customer API
     */
    public static void main(String[] args)
    {
        // check command line args
        if( args.length != 4 )
        {
            System.err.println(
                "usage: Customer <title> <author> <price> <bookID>");
            System.exit( -1 );
        }

        try
        {
            String title = args[0];
            String author = args[1];
            float price = Float.valueOf( args[2] ).floatValue();
            String bookID = args[3];
            Customer customer = new Customer(
                title, author, price, bookID );
            customer.purchase();
        }
        catch( Exception x )
        {
            System.err.println( "Failed to purchase "" + args[0] + """ );
        }
    }
}

Creating the Deployment Descriptor

The ShoppingCart uses CMP for the cartID field and CMR for the one-to-many relationship with books. All methods of ShoppingCart are specified to use the Required transaction type for CMT. These specifications are defined in the ejb-jar.xml deployment descriptor shown in Listing 21.22.

Example 21.22. The Deployment Descriptor Provides the Specification for the Persistence and Transactions Provided by the Container

<!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN' 'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>


<!-- Generated XML! -->

<ejb-jar>
  <enterprise-beans>
    <entity>
      <ejb-name>ShoppingCart</ejb-name>
      <home>com.objectmind.ShoppingCart.ShoppingCartHome</home>
      <remote>com.objectmind.ShoppingCart.ShoppingCart</remote>
      <ejb-class>com.objectmind.ShoppingCart.ShoppingCartCMPBean</ejb-class>
      <persistence-type>Container</persistence-type>
      <prim-key-class>java.lang.Integer</prim-key-class>
      <reentrant>False</reentrant>
      <cmp-version>2.x</cmp-version>
      <abstract-schema-name>ShoppingCartSchema</abstract-schema-name>
      <cmp-field>
        <field-name>cartID</field-name>
      </cmp-field>
      <cmp-field>
        <field-name>customerID</field-name>
      </cmp-field>
      <primkey-field>cartID</primkey-field>
      <query>
        <query-method>
          <method-name>findAll</method-name>
          <method-params>
          </method-params>

        </query-method>

        <ejb-ql><![CDATA[SELECT OBJECT(s) FROM ShoppingCartSchema
                   AS s]]></ejb-ql>
      </query>
    </entity>
    <entity>
      <ejb-name>BookStore</ejb-name>
      <home>com.objectmind.BookStore.BookEntityHome</home>
      <remote>com.objectmind.BookStore.BookEntity</remote>
      <local-home>com.objectmind.BookStore.BookEntityLocalHome</local-home>
      <local>com.objectmind.BookStore.BookEntityLocal</local>
      <ejb-class>com.objectmind.BookStore.BookEntityCMPBean</ejb-class>
      <persistence-type>Container</persistence-type>
      <prim-key-class>com.objectmind.BookStore.BookEntityPK</prim-key-class>
      <reentrant>False</reentrant>
      <cmp-version>2.x</cmp-version>
      <abstract-schema-name>BookstoreSchema</abstract-schema-name>
      <cmp-field>
        <field-name>bookID</field-name>
      </cmp-field>
      <cmp-field>
        <field-name>title</field-name>
      </cmp-field>
      <cmp-field>
        <field-name>author</field-name>
      </cmp-field>
      <cmp-field>
        <field-name>price</field-name>
      </cmp-field>
      <primkey-field>bookID</primkey-field>
      <query>
        <query-method>
          <method-name>findBookByTitle</method-name>
          <method-params>
            <method-param>java.lang.String</method-param>
          </method-params>

        </query-method>

        <ejb-ql>
          <![CDATA[SELECT OBJECT(a) FROM BookstoreSchema AS a WHERE a.title = ?1]]>
        </ejb-ql>
      </query>
      <query>
        <query-method>
          <method-name>findBooksByAuthor</method-name>
          <method-params>
            <method-param>java.lang.String</method-param>
          </method-params>

        </query-method>

        <ejb-ql><![CDATA[SELECT OBJECT(a) FROM BookstoreSchema AS a WHERE a.author = ?1]]></ejb-ql>
      </query>
    </entity>
  </enterprise-beans>

  <relationships>
    <ejb-relation>
      <ejb-relation-name>ShoppingCart-Book</ejb-relation-name>
      <ejb-relationship-role>
        <ejb-relationship-role-name>ShoppingCart-Has-Books</ejb-relationship-role-name>
        <multiplicity>many</multiplicity>
                <relationship-role-source>
          <ejb-name>BookStore</ejb-name>
        </relationship-role-source>

        <cmr-field>
          <cmr-field-name>cart</cmr-field-name>
        </cmr-field>

      </ejb-relationship-role>
      <ejb-relationship-role>
        <ejb-relationship-role-name>Book-Has-ShoppingCart</ejb-relationship-role-name>
        <multiplicity>one</multiplicity>
                <relationship-role-source>
          <ejb-name>ShoppingCart</ejb-name>
        </relationship-role-source>

        <cmr-field>
          <cmr-field-name>books</cmr-field-name>
          <cmr-field-type>java.util.Collection</cmr-field-type>
        </cmr-field>

      </ejb-relationship-role>
    </ejb-relation>
  </relationships>

  <assembly-descriptor>
    <container-transaction>
      <method>
        <ejb-name>ShoppingCart</ejb-name>
        <method-name>*</method-name>
      </method>
      <trans-attribute>Required</trans-attribute>
    </container-transaction>
    <container-transaction>
      <method>
        <ejb-name>BookStore</ejb-name>
        <method-name>*</method-name>
      </method>
      <trans-attribute>Required</trans-attribute>
    </container-transaction>
 </assembly-descriptor>

Deployment on WebLogic Server

The entity bean must run within a bean container on an application server. This stage of software development, to load the software on the target environment, is referred to as deployment. At this point, the bean provider has written the following interfaces and classes:

  • Remote interface

  • Remote home interface

  • Local interface

  • Local home interface

  • Value object class

  • Entity bean class

In addition to the classes and interfaces for the entity EJB, the bean provider must also write deployment descriptors to define the deployment specifications for the bean. The deployment of an entity EJB has the following steps performed by the application server:

  • Generate all of the implementation classes

  • Set up the JNDI tree for JNDI lookups

  • Set up the connection pool

  • Set up tables in the database that match the schema

All four of these steps are performed when an entity bean is deployed. The deployment of the bean simply requires the jar file containing the implementation and descriptors to be placed in the application directory on WebLogic Server. The implementation for the interfaces and abstract classes are automatically generated by WebLogic Server when the entity bean is deployed. Optionally, the tables in the database can be created automatically through a specification in the WebLogic deployment descriptor. The EJB deployment descriptor, ejb-jar.xml, was covered previously in this chapter. WebLogic Server supports additional deployment descriptors:

  • weblogic-ejb-jar.xml

  • weblogic-cmp-rdbms-jar.xml

Through the specification provided by the WebLogic specific deployment descriptors, the JNDI tree and relational database are configured on behalf of the deployed entity EJB.

The weblogic-ejb-jar.xml Deployment Descriptor

The primary purpose of the weblogic-ejb-jar.xml file is to specify JNDI names for the deployment of the entity bean. The JNDI names are used to set up the JNDI tree database to allow the home interface of the entity bean to be retrieved using a JNDI lookup. Session beans look up the home interface of the entity beans, and entity beans with container-managed relationships look up the home interface of the other entity beans that they contain. The major XML tags that are specified in the weblogic-ejb-jar.xml deployment descriptor are defined in Table 21.3.

Table 21.3. The Major XML Tags for the weblogic-ejb-jar.xml Deployment Descriptor

Tag

Purpose

<entity-descriptor>

This is the parent tag that contains the persistence options: <persistence-type> and <persistence-use>.

<persistence-type>

This tag specifies the services that the container can use for persistence. Multiple <persistence-type> tags can be specified. In addition to <type-identifier> and <type-version>, the <type-storage> is specified, which is the weblogic-cmp-rdbms-jar.xml persistence descriptor.

<persistence-use>

This tag specifies which of the <persistence-type> elements to use.

<jndi-name>

This tag specifies the name for the remote home interface.

<local-jndi-name>

This tag specifies the name for the local home interface.

<concurrency-strategy>

WebLogic Server allows several methods to optimize currency: Exclusive, Database, and ReadOnly.

 

Exclusive places a synchronized lock on the entity bean until the transaction is committed.

 

Database defers locking to the underlying datastore. This is the default for WebLogic.

 

ReadOnly is used by entity beans that do not update the database, they only read from the database.

<read-timeout-seconds>

This tag specifies an interval timer that is used by the WebLogic container to call the ejbLoad() method on the entity bean every <read-timeout-seconds>. It would be used in a stock ticker application, for example.

<invalidate-target>

This tag causes another ReadOnly entity bean to become invalid when this bean gets modified.

 
<invalidate-target>
 <ejb-name>MyReadOnlyClient<ejb-name>
<invalidate-target>

<db-is-shared>

This tag is used to optimize calls to ejbLoad(). By default, WebLogic Server calls ejbLoad() at the beginning of each transaction to ensure that any updates by other entity beans are captured by this bean. If no other beans are updating the data used by this entity bean, set <db-is-shared> to false; it is true by default.

<delay-updates-until-end-of-tx>

This tag is used to optimize calls to ejbStore(). By default, WebLogic Server calls ejbStore() when a transaction is committed. To allow other entity beans to see uncommitted data, set <delay-updates-until-end-of-tx> to false, which will cause ejbLoad() to be called whenever there is an update, not just after commits. This property is true by default.

<create-default-dbms-table>

This tag is primarily used during the development stage to allow WebLogic Server to automatically create database tables. To enable this feature, set <create-default-dbms-table> to true; it is false by default.

The weblogic-ejb-jar.xml deployment descriptor for the ShoppingCart is provided as an example in Listing 21.23.

Example 21.23. The WebLogic Deployment Descriptor Provides the Specification for Deploying the Entity EJB on WebLogic Server

<!DOCTYPE weblogic-ejb-jar PUBLIC '-//BEA Systems, Inc.//DTD WebLogic 7.0.0 EJB//EN' 'http://www.bea.com/servers/wls700/dtd/weblogic-ejb-jar.dtd'>


<!-- Generated XML! -->

<weblogic-ejb-jar>
  <weblogic-enterprise-bean>
    <ejb-name>ShoppingCart</ejb-name>
    <entity-descriptor>
      <pool>
      </pool>

      <entity-cache>
        <cache-between-transactions>False</cache-between-transactions>
      </entity-cache>

      <persistence>
        <persistence-use>
          <type-identifier>WebLogic_CMP_RDBMS</type-identifier>
          <type-version>7.0</type-version>
          <type-storage>META-INF/weblogic-cmp-rdbms-jar.xml</type-storage>
        </persistence-use>

      </persistence>

      <entity-clustering>
      </entity-clustering>

    </entity-descriptor>

    <transaction-descriptor>
    </transaction-descriptor>

    <jndi-name>cmp.ShoppingCart</jndi-name>
    <local-jndi-name>cmp.ShoppingCart-Local</local-jndi-name>
  </weblogic-enterprise-bean>
  <weblogic-enterprise-bean>
    <ejb-name>BookStore</ejb-name>
    <entity-descriptor>
      <pool>
      </pool>

      <entity-cache>
        <cache-between-transactions>False</cache-between-transactions>
      </entity-cache>

      <persistence>
        <persistence-use>
          <type-identifier>WebLogic_CMP_RDBMS</type-identifier>
          <type-version>7.0</type-version>
          <type-storage>META-INF/weblogic-cmp-rdbms-jar.xml</type-storage>
        </persistence-use>

      </persistence>

      <entity-clustering>
      </entity-clustering>

    </entity-descriptor>

    <transaction-descriptor>
    </transaction-descriptor>

    <jndi-name>cmp.BookEntity</jndi-name>
    <local-jndi-name>cmp.BookEntity-Local</local-jndi-name>
  </weblogic-enterprise-bean>
</weblogic-ejb-jar>

The weblogic-cmp-rdbms-jar.xml Persistence Descriptor

The weblogic-cmp-rdbms-jar.xml persistence descriptor is referenced by the weblogic-ebj-jar.xml deployment descriptor as the <type-storage> for persistence. The major XML tags that are specified in the weblogic-cmp-rdbms-jar.xml persistence descriptor are shown in Table 21.4.

Table 21.4. The Major Tags in the weblogic-cmp-rdbmc-jar.xml Persistence Descriptor

Tag

Purpose

<weblogic-rdbms-bean>

This is the parent tag that contains the specification for the persistence of the beans. The children of this tag are: <ejb-name>, <data-source-name>, <table-name>, <field-map>, and <automatic-key-generation>.

<ejb-name>

This tag must correspond to an <ejb-name> of an <entity> element in the ejb-jar.xml deployment descriptor.

<data-source-name>

This tag specifies the name of the database connection pool to use for persistence.

<table-name>

This tag specifies the name of the database table to use for persistence of this entity bean.

<field-map>

This tag maps <cmp-field> elements to <dbms-column> names.

<automatic-key-generation>

This tag is a value-added feature of WebLogic Server to generate primary keys for CMP entities.

<weblogic-rdbms-relation>

This is the parent tag that contains the specification for the persistence of the relationships. The children of this tag are: <relation-name> and <weblogic-relationship-role>.

<relation-name>

This tag maps an <ejb-relation-name> from ejb-jar.xml to this <weblogic-rdbms-relation>.

<weblogic-relationship-role>

This is the parent tag for the <relationship-role-name> and <column-map> elements.

<relationship-role-name>

This tag maps an <ejb-relationship-role-name> from ejb-jar.xml to this <weblogic-relationship-role>.

<column-map>

This tags specifies the <foreign-key-column> and <key-column> for joining the tables in this relationship.

<create-default-dbms-table>

This tag is primarily used during the development stage to allow WebLogic Server to automatically create database tables. To enable this feature, set <create-default-dbms-table> to true; it is false by default.

Tip

The <create-default-dbms-table> is not recommended for production-quality software. The database schema should be very precise and not automatically generated by WebLogic Server.

The weblogic-cmp-rdbms-jar persistence descriptor for the ShoppingCart is shown in Listing 21.24.

Example 21.24. The WebLogic Persistence Descriptor Provides the Specification for Mapping the Entity EJB to the Relational Database Being Used by WebLogic Server

<!DOCTYPE weblogic-rdbms-jar PUBLIC  '-//BEA Systems, Inc.//
DTD WebLogic 7.0.0 EJB RDBMS Persistence//EN'
'http://www.bea.com/servers/wls700/dtd/weblogic-rdbms20-persistence-700.dtd'>


<!-- Generated XML! -->

<weblogic-rdbms-jar>
  <weblogic-rdbms-bean>
    <ejb-name>ShoppingCart</ejb-name>
    <data-source-name>TxDSBookStore</data-source-name>
    <table-map>
      <table-name>ShoppingCart</table-name>
      <field-map>
        <cmp-field>cartID</cmp-field>
        <dbms-column>cartID</dbms-column>
      </field-map>
      <field-map>
        <cmp-field>customerID</cmp-field>
        <dbms-column>customerID</dbms-column>
      </field-map>
    </table-map>
    <automatic-key-generation>
      <generator-type>NAMED_SEQUENCE_TABLE</generator-type>
      <generator-name>SHOPPING_SEQ</generator-name>
      <key-cache-size>10</key-cache-size>
    </automatic-key-generation>

  </weblogic-rdbms-bean>
  <weblogic-rdbms-bean>
    <ejb-name>BookStore</ejb-name>
    <data-source-name>TxDSBookStore</data-source-name>
    <table-map>
      <table-name>bookstore</table-name>
      <field-map>
        <cmp-field>author</cmp-field>
        <dbms-column>bookAuthor</dbms-column>
      </field-map>
      <field-map>
        <cmp-field>price</cmp-field>
        <dbms-column>bookPrice</dbms-column>
      </field-map>
      <field-map>
        <cmp-field>bookID</cmp-field>
        <dbms-column>bookID</dbms-column>
      </field-map>
      <field-map>
        <cmp-field>title</cmp-field>
        <dbms-column>bookTitle</dbms-column>
      </field-map>
    </table-map>
    <weblogic-query>
      <query-method>
        <method-name>findBooksByAuthor</method-name>
        <method-params>
          <method-param>java.lang.String</method-param>
        </method-params>

      </query-method>

      <max-elements>0</max-elements>
      <include-updates>False</include-updates>
    </weblogic-query>
  </weblogic-rdbms-bean>
  <weblogic-rdbms-relation>
    <relation-name>ShoppingCart-Book</relation-name>
    <weblogic-relationship-role>
      <relationship-role-name>ShoppingCart-Has-Books</relationship-role-name>
      <relationship-role-map>
        <column-map>
          <foreign-key-column>cart</foreign-key-column>
          <key-column>cartID</key-column>
        </column-map>
      </relationship-role-map>
     </weblogic-relationship-role>
  </weblogic-rdbms-relation>
  <create-default-dbms-tables>True</create-default-dbms-tables>
</weblogic-rdbms-jar>

Deploying Entity Beans to WebLogic Server

The implementation of an entity EJB is released as a JAR file, which contains the compiled class files for the interfaces and classes that were created to implement the entity EJB. In addition to the compiled class files, the JAR contains the ejb-jar.xml and weblogic-ejb-jar.xml deployment descriptors. If the bean is implemented using CMP, the weblogic-cmp-rdbms-jar.xml persistence descriptor must also be added to the jar file for the entity bean. The entity bean JAR can be added to an enterprise application archive (EAR) file, but this is not required to deploy the bean on WebLogic Server.

Before the entity bean can be deployed to WebLogic Server, the JDBC DataSources or Tx DataSources and associated connection pools that the entity bean has been configured to use must exist and be operational. This is especially important if you have enabled the <create-default-dbms-tables> tag in the weblogic-cmp-rdbms-jar.xml persistence deployment descriptor to create the database tables the CMP entity bean will use. Also, if you are deploying a CMP entity bean, the sequence table the EJB container will use to auto-generate keys will also need to exist in the target database. Only once WebLogic Server and target database have been configured correctly, the JAR or EAR file can be deployed to WebLogic Server using the weblogic.Deployer tool, WebLogic Builder or the Administration Console.

To showcase the deployment of CMP entity beans, the following steps will explain how to successfully deploy the ShoppingCart EJB (BookEntity and ShoppingCart CMP entity beans) developed in this chapter to WebLogic Server.

Step 1: Start and Configure Your Target Database

To enable you to quickly and easily deploy the ShoppingCart EJB example, the Pointbase database that comes bundled with WebLogic Server will be used as the target database. The Pointbase database uses the concept of schemas to allow you to create unique database environments, while running under one database server instance. Schemas are created for each unique database user. Hence each database user can create and manipulate database tables in their own schema. The database user and schema that will be used in the Pointbase database for the ShoppingCart EJB example will be “PBPUBLIC.”

The approach taken in this example is to start the Pointbase database with the WebLogic Server instance you are using. Using this approach you can leverage the startup parameters for the Pointbase database already provided in your StartExampleServer script file located in the %BEA_HOME%weblogic700samplesserverconfigexamples directory.

Note

The %BEA_HOME% directory is the root directory of the BEA software installation, for example c:ea.

An example of these startup parameters is provided in Listing 21.25.

Example 21.25. The Startup Parameters for the Pointbase Database to be Copied into Your WebLogic Server Startup Script

@rem *****************************************************************
@rem PointBase and examples domain specific configuration
set SAMPLES_HOME=C:eaweblogic700samples
set POINTBASE_HOME=%SAMPLES_HOME%serverevalpointbase
set COMMON_CLASSES=%SAMPLES_HOME%serverstagecommon

@rem Add PointBase classes to the classpath, so we can start the
@rem database.
set CLASSPATH="C:eaweblogic700serverlibwebservices.jar;
C:eajrockit70sp2_131lib	ools.jar;%POINTBASE_HOME%libpbserver42ECF183.jar;
%POINTBASE_HOME%libpbclient42ECF183.jar;%CLIENT_CLASSES%;%SERVER_CLASSES%
;%COMMON_CLASSES%;%CLIENT_CLASSES%utils_common.jar"

@rem Start PointBase 4.2.
start "PointBase" cmd /c ""C:eajrockit70sp2_131injava" -classpath %CLASSPATH%
com.pointbase.net.netServer /port:9092 /d:3
/pointbase.ini="%SAMPLES_HOME%serverconfigexamplespointbase.ini"" >
"%SAMPLES_HOME%serverconfigexamplespointbase.log" 2>&1
@rem ************************************************************************

Listing 21.25, as it pertains to your WebLogic Server environment, should be copied into your WebLogic Server startup script to ensure the Pointbase database starts with your WebLogic Server instance. Once you restart your WebLogic Server, you will see a command shell appear, as illustrated in Figure 21.2, indicating the Pointbase database server has started.

A successful starting of the Pointbase database.

Figure 21.2. A successful starting of the Pointbase database.

The final aspect of configuring your Pointbase database for the ShoppingCart EJB example is creating the sequence table that will be used to store key values for the auto-generation of primary keys. This sequence table must contain a single row with a single column named “SEQUENCE” of datatype INTEGER (SEQUENCE INT). The SEQUENCE column holds the current sequence value.

The name of the sequence table to create for the ShoppingCart EJB example is specified by the <automatic-key-generation> tag in the weblogic-cmp-rdbms-jar persistence descriptor, as shown in Listing 21.26.

Example 21.26. Specifying Automatic Key Generation for a Named Sequence Table

<automatic-key-generation>
    <generator-type>NAMED_SEQUENCE_TABLE</generator-type>
    <generator_name>SHOPPING_SEQ</generator-name>
    <key-cache-size>100</key-cache-size>
</automatic-key-generation>

In Listing 21.26,

  • <generator-type> specifies the name of the sequence table to create in your database.

  • <key-cache-size> specifies how many keys the EJB container will fetch in a single DBMS call.

Note

For improved performance, BEA recommends that you set this value to a number greater than one. This setting reduces the number of calls to the database to fetch the next key value.

To easily create a table named “SHOPPING_SEQ” in your Pointbase database, follow these steps:

  1. From a command prompt, set your Java environment, for example by executing the setEnv script provided in your WebLogic domain folder.

  2. Change the directory to the following:

    %BEA_HOME%weblogic700samplesserverevalpointbase	ools
    
  3. Execute the following command to launch the Pointbase Console:

    StartPointBaseConsole
    
  4. In the displayed login screen, as shown in Figure 21.3, enter “PBPUBLIC” for the User and Password entry field. Click OK.

    The Pointbase Login Screen.

    Figure 21.3. The Pointbase Login Screen.

  5. Execute the following SQL statement to create the SHOPPING_SEQ sequence table:

    CREATE SHOPPING_SEQ (SEQUENCE int);
    
  6. Enter a row into the newly created SHOPPING_SEQ table using the following SQL statement:

    INSERT INTO SHOPPING_SEQ VALUES (0);
    

Your Pointbase database is now configured to be used with the ShoppingCart EJB example.

Step 2: Configure the JDBC Connection Pools Tx DataSource in Your WebLogic Server

The ShoppingCart EJB example will use a JDBC connection pool named CMPBeanPool to serve as a pool of database connections to your target Pointbase database using a Tx DataSource named TxDSBookStore.

To configure your CMPBeanPool connection pool in your WebLogic Server:

  1. Start the Administration Console.

  2. From the Administration Console, expand the JDBC node in the left pane.

  3. Click the Connection Pools node, which will display all the connection pools defined in your domain in the right pane.

  4. Click the Configure a New JDBC Connection Pool text link. A new page will display in the right pane (Configuration, General tab) allowing you to configure a new connection pool.

  5. Enter the following values to configure your connection pool

    • Name = CMPBeanPool

    • URL = jdbc:pointbase:server://localhost/demo

    • Driver Classname = com.pointbase.jdbc.jdbcUniversalDriver

    • Properties = user=PBPUBLIC, password=PBPUBLIC

  6. Click Create to create the CMPBean connection pool. This new instance is added under the Connection Pools node in the left pane.

  7. Click the Targets tab and assign the CMPBean connection pool to your WebLogic Servers or clusters.

  8. Click Apply.

Note

▸ For an illustrated and detailed guide to creating a JDBC connection pool, see “Configuring and Using Connection Pools,” p. 427.

To configure your “TxDSBookStore” Tx DataSource in your WebLogic Server:

  1. Start the Administration Console

  2. In the left pane of the Administration Console, click to expand the JDBC node.

  3. Click the Tx DataSources node. The Tx DataSources table displays in the right pane showing all the data sources defined in your domain.

  4. Click the Configure a New JDBC Tx DataSource text link. A new page displays in the right pane showing the Configuration tab for creating a new Tx DataSource.

  5. Enter the following values in the Configuration tab:

    • Name = TxDSBookStore

    • JNDI Name = TxDSBookStore

    • Pool Name = CMPBeanPool

  6. Click Create to create the TxDSBookStore Tx DataSource.

  7. Click the Targets tab and assign the TxDSBookStore Tx DataSource to your WebLogic Servers or clusters.

  8. Click Apply.

Your CMPBean connection pool and TxDSBookStore Tx DataSource is now configured for the ShoppingCart EJB example against the Pointbase database.

Step 3: Deploying the ShoppingCart EJB Example to Your WebLogic Server

To deploy the ShoppingCart EJB example to your WebLogic Server, the ShoppingCart and BookEntity CMP entity beans, the Customer class, and the associated deployment descriptors must be compiled and packaged into a JAR file named ShoppingCart.jar using the following steps:

Note

The Customer class is used to unit test the API for the ShoppingCart and BookEntity entity beans later in this chapter.

  1. Set your Java environment by executing a set environment script, for example the setEnv WebLogic Server script.

  2. Execute the following command from your working directory to compile your Java source code:

    javac -d . *.java
    
  3. Package your compiled class files and deployment descriptors into a deployable JAR file using the following command:

    jar cvf ShoppingCart.jar com META-INF
    

Once you have generated the ShoppingCart.jar module, you can deploy this JAR file to WebLogic Server via the Administration Console using the following steps:

  1. Start the Administration Console.

  2. In the left pane of the Administration Console, click to expand the Deployments node.

  3. Click the EJB node. The EJB Deployments table displays in the right pane showing all the EJBs deployed in your domain.

  4. Click the Configure a New EJB text link. A new page displays in the right pane showing the steps to deploy a J2EE application or module (ShoppingCart) to your WebLogic Server.

    Step 1 involves ensuring the ShoppingCart.jar file is on the same file system as the Administration Server of your WebLogic domain.

    Step 2 involves using the Administration Console to locate the ShoppingCart.jar file you want to deploy. Once you have located the ShoppingCart.jar file, use the [select] link next to that file, as illustrated in Figure 21.4.

    Selecting the ShoppingCart.jar module for deployment.

    Figure 21.4. Selecting the ShoppingCart.jar module for deployment.

    Step 3 involves selecting the WebLogic Servers or clusters you want to deploy the ShoppingCart module to and assigning a WebLogic name to the module within the context of the WebLogic environment, as illustrated in Figure 21.5.

    Selecting the WebLogic Name and target WebLogic Servers for the ShoppingCart module.

    Figure 21.5. Selecting the WebLogic Name and target WebLogic Servers for the ShoppingCart module.

  5. Click Configure and Deploy to deploy the ShoppingCart module to your WebLogic Server(s).

Once the ShoppingCart EJB example has been deployed, you will see a Deployment Status By Target screen in the Administration Console indicating the success or failure of the deployment. Figure 21.6 shows a successful deployment of the ShoppingCart module to its target WebLogic Server.

A successful deployment of the ShoppingCart module to the target WebLogic Server.

Figure 21.6. A successful deployment of the ShoppingCart module to the target WebLogic Server.

Note

▸ For further information on deployment in the WebLogic Server, see Chapter 27, “Packaging, Assembling and Deploying J2EE Applications,” p. 953.

Once the ShoppingCart module is deployed, the associated tables it uses (BOOKSTORE and SHOPPINGCART) will automatically be created by the EJB container. You can validate the creation of these table using the Catalog option in the Pointbase Console, as illustrated in Figure 21.7.

Using the Pointbase Console to validate the creation of the ShoppingCart tables.

Figure 21.7. Using the Pointbase Console to validate the creation of the ShoppingCart tables.

Testing the Deployment of the ShoppingCart Module

The ShoppingCart.jar includes the Customer class which can be used to unit test the API for the ShoppingCart and BookEntity EJBs. The Customer class is run as a standard Java application and uses localhost as the hostname for JNDI lookups. For this reason it must be run on the same system as the WebLogic Server you deployed the ShoppingCart to.

Note

The source code for the Customer class was shown in Listing 21.21.

This test program takes four command-line arguments: title, author, price, and bookID. The constructor creates a BookEntity EJB using the command-line arguments as BookEntityVO data. The main() method then invokes the purchase() method which adds the same BookEntityVO() to a ShoppingCart. If any exceptions are thrown, the error message is displayed; otherwise a thank you message is displayed for purchasing the book.

To run this test application against the ShoppingCart example:

  1. Open a Command Prompt window and set your Java environment.

  2. Set the CLASSPATH to include the location of the ShoppingCart.jar, for example using the following statement:

    Set CLASSPATH=ShoppingCart.jar;%CLASSPATH%
    
  3. Run the application using the following command, as shown in Figure 21.8.

    The Customer class is used to unit the API for the ShoppingCart EJB.

    Figure 21.8. The Customer class is used to unit the API for the ShoppingCart EJB.

    java com.objectmind.BookStore.Customer "WebLogic Platform 7.0" "Jatinder
    Prem" 50.0 1
    

Summary

This chapter described the implementation and deployment of entity EJBs in WebLogic Server. The entity bean provider must be aware of the many services provided by the EJB container. The services include persistence management, transaction management, and life cycle management. Using these services effectively will result in less development time for the bean provider and produce a more reliable, durable, portable, and higher performance in the implementation. There are application-specific issues that require a decision to use bean-managed persistence or container-managed-persistence. With the components of the entity bean are the ejb-jar.xml and weblogic-ejb-jar.xml deployment descriptors. If the entity bean is implemented using CMP, a weblogic-cmp-rdbms.jar.xml persistence descriptor must also be written. The components of the entity EJB are compiled and stored in a jar file along with the deployment descriptors. The entity EJB jar file may be stored in an Entity Application Archive (EAR) file. The JAR or EAR file is then deployed on WebLogic Server to be the persistent storage for an enterprise application.

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

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