Previous     Contents     Index     Next     
iPlanet Application Server Developer's Guide



Chapter 6   Building Entity EJBs


This chapter describes what an entity EJB is and what entity beans must contain. This chapter also provides additional guidelines for creating entity beans and for determining what the entity bean's needs are in an application.

This chapter contains the following sections:

All specifications are accessible from install_dir/ias/docs/index.htm, where install_dir is the location where the iPlanet Application Server is installed.



Introducing Entity EJBs



The heart of a distributed, multi-user application involves interactions with datasources which are often transactional, such as a database or an existing legacy application. In most cases, the external datasource or business object is transparent to the user, or is shielded or buffered from direct user interactions. These protected, transactional, and persistent interactions with databases, documents, and other business objects are candidates for entity EJB encapsulation.

Business EJBs are self-contained, reusable components—with data members, properties, and methods—that represent generic instances, transactionally aware, persistent data objects that are shared among clients. Persistence refers to the creation and bean maintenance throughout the application's lifetime.

There are two persistence management types, and the iPlanet Application Server supports both types as listed below.

  • Container managed persistence - this is when the container is responsible for the bean persistence.

  • Bean managed persistence - this is when the bean is responsible for their own persistence.

A developer codes a bean managed entity bean by providing database access calls—through JDBC and SQL—directly in the bean class methods. Database access calls must be in the ejbCreate(), ejbRemove(), ejbFindXXX(), ejbLoad(), and ejbStore() methods. The bean managed persistence advantage is that these beans can be in any container without requiring the container to generate database calls.

Entity beans rely on the container to manage security, concurrency, transactions, and other container specific services for the entity objects it manages. Multiple clients can access an entity object at the same time and the container transparently handles simultaneous accesses through transactions.

As an application developer, you cannot access the container's entity bean services directly, nor do you ever need to. Instead, the container is there to take care of low level implementation details so you can focus on the larger role the entity bean plays in an application picture.

Clients access an entity bean through the bean's remote interface. The object that implements the remote interface is called the EJB object. Usually, an entity EJB is shared among multiple clients and represents a single entry point to a data resource or business object, such as a database. Regardless of which client accesses an entity object at a given time, each client's object view is both location independent and transparent to other clients.

Finally, any number of entity beans can be installed in a container. The container implements a home interface for each entity bean. The home interface enables a client to create, look up, and remove entity objects. A client looks up an entity bean's home interface through the Java Naming and Directory Interface (JNDI).

An entity bean includes the following attributes:

  • Represents data in a database.

  • Supports transactions.

  • Executes for multiple clients.

  • Persists for as long as needed by all clients.

  • Transparently survives server crashes.

Generally, an entity bean represents shared data in a database and is transaction aware. Its operations always take place in the context of transactions managed by the bean's container.


How an Entity Bean is Accessed

A client, such as a browser or servlet, accesses an entity bean through the bean's remote interface, EJBObject. An EJB object is a remote Java programming language object accessible from the client through standard Java APIs for remote object calls. The EJB lives in the container from its creation to its destruction, and the container manages the EJB's life cycle and support services.

A client never accesses an entity bean instance directly. Instead, a client uses the entity bean's remote interface to access a bean instance. The EJB object class that implements an entity bean's remote interface is provided by the container. At a minimum, an EJB object supports all methods of the java.ejb.EJBObject interface. This includes methods to obtain the entity bean's home interface, to get the object's handle, to retrieve the entity's primary key, to test if the object is identical to another object, and to remove the object. These methods are stipulated by the EJB specification. In addition, the remote interface for most EJB objects also supports specific business logic methods. These are the methods at the heart of a specific application.

All specifications are accessible from install_dir/ias/docs/index.htm, where install_dir is the location where the iPlanet Application Server is installed.



Entity Bean Components



When creating an entity bean, you must provide the following class files:

  • Enterprise bean class

  • Enterprise bean home interface, implementing javax.ejb.EJBHome

  • Enterprise bean remote interface, implementing javax.ejb.EJBObject


Creating the Class Definition

For an entity bean, the bean class must be defined as public and cannot be abstract. The bean class must implement the javax.ejb.EntityBean interface. For example:

import java.rmi.*;
import java.util.*;
import javax.ejb.*;
public class MyEntityBean implements EntityBean {
// Entity Bean implementation. These methods must always included.
public void ejbActivate() throws RemoteException {
}
public void ejbLoad() throws RemoteException {
}
public void ejbPassivate() throws RemoteException {
}
public void ejbRemove() throws RemoteException{
}
public void ejbStore() throws RemoteException{
}
public void setEntityContext(EntityContext ctx) throws RemoteException {
}
public void unsetEntityContext() throws RemoteException {
}
// other code omitted here....
}

In addition to these methods, the entity bean class must also define one or more ejbCreate() methods and the ejbFindByPrimaryKey() finder method. Optionally, it may define one ejbPostCreate() method for each ejbCreate() method. It may provide additional, developer defined finder methods that take the form ejbFindXXX, where XXX represents a unique method name continuation (for example, ejbFindApplesAndOranges) that does not duplicate any other method names.

Finally, most useful entity beans also implement one or more business methods. These methods are usually unique to each bean and represent its particular functionality. Business method names can be anything, but must not conflict with the method names used in the EJB architecture. Business methods must be declared as public. Method arguments and return value types must be Java RMI legal. The throws clause may define application specific exceptions and may include java.rmi.RemoteException.

There are two business method types to implement in an entity bean:

  • internal ones, which are used by other business methods in the bean, but are never accessed outside the bean itself

  • external ones, which are referenced by the entity bean's remote interface

The following sections describe the various methods in an entity bean's class definition.

The examples in these sections assume the following member variable definitions:

private transient javax.ejb.EntityContext m_ctx = null;

// These define the state of our bean
private int m_quantity;
private int m_totalSold;


Using ejbActivate and ejbPassivate

When an entity bean instance is needed by a server application, the bean's container invokes ejbActivate() to ready a bean instance for use. Similarly, when an instance is no longer needed, the bean's container invokes ejbPassivate() to disassociate the bean from the application.

If, specific application tasks need to be performed when a bean is first made ready for an application or needs to be performed when a bean is no longer needed, program those operations within these methods.

Activation is not the same as creating a bean. You can only activate a bean that has already been created. Similarly, passivation is not the same as removing a bean. Passivation merely returns a bean instance to the container pool for later use. ejbRemove() is required to actually terminate a bean instance.

The container passivates entity beans after they are inactive for a specified (or default) time. This timeout value is set in the bean's property file. For more information, see "EJB XML DTD."

For more information about ejbCreate() and ejbRemove(), see "Using ejbCreate Methods."

For more information about ejbActivate() and ejbPassivate(), see the EJB specification. All specifications are accessible from install_dir/ias/docs/index.htm, where install_dir is the location where the iPlanet Application Server is installed.


Using ejbLoad and ejbStore

An entity bean using Bean Managed Persistence should permit its container to store the bean state information in a database for synchronization purposes. Use your implementation of ejbStore() to store state information in the database and use your implementation of ejbLoad() to retrieve state information from the database. When the container calls ejbLoad(), it synchronizes the bean state by loading state information from the database.

If the entity bean uses Container Managed Persistence, then you need not code the database access layer.

The following example shows ejbLoad() and ejbStore() method definitions that store and retrieve active data.

public void ejbLoad()
      throws java.rmi.RemoteException
{
   String itemId;
   DatabaseConnection dc = null;
   java.sql.Statement stmt = null;
   java.sql.ResultSet rs = null;

   itemId = (String) m_ctx.getPrimaryKey();

   System.out.println("myBean: Loading state for item " + itemId);

   String query =
      "SELECT s.totalSold, s.quantity " +
      " FROM Item s " +
      " WHERE s.item_id = " + itemId;

   dc = new DatabaseConnection();
   dc.createConnection(DatabaseConnection.GLOBALTX);
   stmt = dc.createStatement();
   rs = stmt.executeQuery(query);

   if (rs != null) {
      rs.next();
      m_totalSold = rs.getInt(1);
      m_quantity = rs.getInt(2);
   }
}

public void ejbStore()
      throws java.rmi.RemoteException
{
   String itemId;
   itemId = (String) m_ctx.getPrimaryKey();
   DatabaseConnection dc = null;
   java.sql.Statement stmt1 = null;
   java.sql.Statement stmt2 = null;

   System.out.println("myBean: Saving state for item = " + itemId);

   String upd1 =
      "UPDATE Item " +
      " SET quantity = " + m_quantity +
      " WHERE item_id = " + itemId;

   String upd2 =
      "UPDATE Item " +
      " SET totalSold = " + m_totalSold +
      " WHERE item_id = " + itemId;

   dc = new DatabaseConnection();
   dc.createConnection(DatabaseConnection.GLOBALTX);
   stmt1 = dc.createStatement();
   stmt1.executeUpdate(upd1);
   stmt1.close();
   stmt2 = dc.createStatement();

   stmt2.executeUpdate(upd2);
   stmt2.close();
}

For more information about bean isolation levels that access transactions concurrently with other beans, see "Handling Concurrent Access."


Using setEntityContext and unsetEntityContext

A container calls setEntityContext() after it creates an entity bean instance to provide the bean's interface to the container. Implement this method, to store the container reference in an instance variable.

public void setEntityContext(javax.ejb.EntityContext ctx)
{
m_ctx = ctx;
}

Similarly, a container calls unsetEntityContext() to remove the container reference from the instance. This is the last bean class method a container calls. After this call, the Java garbage collection mechanism eventually calls finalize() on the instance to clean it up and dispose it.

public void unsetEntityContext()
{
m_ctx = null;
}


Using ejbCreate Methods

The entity bean must also implement one or more ejbCreate(...) methods. There must be one method for each way a client is allowed to invoke the bean. For example:

public integer ejbCreate() {
   string[] userinfo = {"User Name", "Encrypted Password"};
}

Each ejbCreate() method must be declared as public, return either the entity's primary key type or a collection, and be named ejbCreate. The return type can be any legal Java RMI type. Any arguments must be legal Java RMI types. The throws clause, may define application specific exceptions, and may include java.rmi.RemoteException and/or javax.ejb.CreateException.

For each ejbCreate() method, the entity bean class may define an ejbPostCreate() method to handle entity services immediately following creation. Each ejbPostCreate() method must be declared as public, must return void, and be named ejbPostCreate. The method arguments, if any, must match in number and argument type of its corresponding ejbCreate method. The throws clause, may define application specific exceptions, and may include java.rmi.RemoteException and/or java.ejb.CreateException.

Finally, an entity bean also implements one or more ejbRemove() methods to free a bean when it is no longer needed.


Using Finder Methods

Because entity beans are persistent, are shared among clients, and may have more than one instance instantiated at the same time, an entity bean must implement at least one method, ejbFindByPrimaryKey(), that enables the client and the bean's container to locate a specific bean instance. All entity beans must provide a unique primary key as an identifying signature. Implement the ejbFindByPrimaryKey() method in the bean's class to enable a bean to return its primary key to the container.

The following example shows a definition for FindByPrimaryKey():

public String ejbFindByPrimaryKey(String key)
      throws java.rmi.RemoteException,
         javax.ejb.FinderException
{
   //System.out.println("@@@ myBean.ejbFindByPrimaryKey key = " + key);
   return key;
}

In some cases, you find a specific entity bean instance based on what the bean does, based on certain values the instance is working with, or based on other criteria. These implementation specific finder method names take the form ejbFindXXX, where XXX represents a unique continuation of a method name (for example, ejbFindApplesAndOranges) that does not duplicate any other method names.

Finder methods must be declared as public, and their arguments and return values must be legal Java RMI types. Each finder method return type must be the entity bean's primary key type or a collection of objects of the same primary key type. If the return type is a collection, the return type must be one of the following:

  • The JDK 1.1 java.util.Enumeration interface

  • The Java 2 java.util.Collection interface

The throws clause of a finder method is an application specific exception, and may include java.rmi.RemoteException and/or javax.ejb.FinderException.


Declaring vs. Implementing the Remote Interface

A bean class definition must include one matching method definition including matching method names, arguments, and return types, for each method defined in the bean's remote interface. The EJB specification permits the bean class to implement the remote interface's methods, but recommends against this practice to avoid inadvertently passing a direct reference (through this) to a client in violation of the client-container-EJB protocol intended by the specification.


Creating the Home Interface

The home interface defines the methods that enables a client accessing an application to create and remove entity objects. A home interface always extends javax.ejb.EJBHome. For example:

import javax.ejb.*;
import java.rmi.*;

public interface MyEntityBeanHome extends EJBHome {
   MyEntityBean create() throws CreateException, RemoteException;
}

This example illustrates, an entity bean's home interface defines one or more create methods. Usually the home interface also defines one or more find methods corresponding to the finder methods in the bean class.


Defining Create Methods

Each method must be named create, and must correspond in number and argument types to an ejbCreate method defined in the entity bean class. The return type for each create method, however, does not match the corresponding ejbCreate method's return type. Instead, it must return the entity bean's remote interface type.

All exceptions defined in the throws clause of an ejbCreate method must be defined in the throws clause of the matching create method in the home interface. In addition, the throws clause in the home interface must always include javax.ejb.CreateException.


Defining Find Methods

A home interface can define one or more find methods. Each method must be named findXXX (for example, findApplesAndOranges), where XXX is a unique method name continuation. Each finder method must correspond to one of the finder methods defined in the entity bean class definition.

The number and argument types must also correspond to the finder method definitions in the bean class. The return type, however, may be different. The finder method's return type in the home interface must be the entity bean's remote interface type or a collection of interfaces. In addition, the trows clause in the home interface must always include javax.ejb.FinderException.

Finally, all home interfaces automatically define two remove methods for destroying an EJB when it is no longer needed.



Note Do not override these methods.




Creating the Remote Interface

An entity bean's remote interface defines a user's access to a bean's methods. All remote interfaces extend javax.ejb.EJBObject. For example:

import javax.ejb.*;
import java.rmi.*;
public interface MyEntityBean extends EJBObject {
// define business method methods here....
}

The remote interface defines the entity bean's business methods that a client calls. The business methods defined in the remote interface are executed by the bean's container at runtime. For each method you define in the remote interface, you must supply a corresponding method in the bean class. The corresponding method in the bean class must have the same signature.

Besides the business methods you define in the remote interface, the EJBObject interface defines several abstract methods that enables you to retrieve the bean's home interface, to retrieve the bean's handle, to retrieve the bean's primary key which uniquely identifies the bean's instance, to compare the bean to another bean to see if it is identical, and to remove the bean when it is no longer needed.

For more information about these built-in methods and how they are used, see the EJB specification. All specifications are accessible from install_dir/ias/docs/index.htm, where install_dir is the location where the iPlanet Application Server is installed.



Additional Entity Bean Guidelines



Before you decide what application parts you can represent as entity beans, you should consider a few more guidelines. A couple of these are related to the EJB specification for entity beans, and a couple are specific to the iPlanet Application Server and its support for entity beans.


Accessing iPlanet Application Server Functionality

You can develop entity beans that adhere strictly to the EJB specification, you can develop entity beans that take advantage of both the specification and additional, value-added iPlanet Application Server features, and you can develop entity beans that adhere to the specifications in non-iPlanet Application Server environments but take advantage of the iPlanet Application Server features if they are available. Make the choice that is best for your intended deployment scenario.

The iPlanet Application Server offers several features through the iPlanet Application Server container and the iPlanet Application Server APIs that enables your applications to take programmatic advantage of specific iPlanet Application Server environment features. You can embed API calls in your entity beans if you plan on using those beans only in an iPlanet Application Server environment.


Serializing Handles and References

The EJB specification indicates that to guarantee serializable bean references, you should use handles rather than direct references to EJBs.

The iPlanet Application Server direct references are also serializable. You may wish to take advantage of this extension, but be aware not all vendors support it.


Managing Transactions

Many entity beans interact with databases. You control bean transactions by using settings in the bean's property file. This permits specifying transaction attributes at bean deployment time.

With entity beans, you can only use Container Managed Transaction.

If the Container manages the transaction, there is no need to explicitly start, rollback, or commit transactions in the bean's database access methods.

By moving transaction management to the Container level, you gain the ability to place all the bean's activities—even those not directly tied to the database access—under the same transaction control as your database calls. This guarantees that all application parts controlled by Container run as part of the same transaction, and either everything the Container undertakes is committed, or it is rolled back in a failure case. In effect, a Container managed transactional state permits synchronizing the application without programming any synchronization routines.


Committing a Transaction

When a commit occurs, it signals the container that the entity bean has completed its useful work and should synchronize its state with the underlying datasource. The container permits the transaction to complete and then returns the bean to the pool for later reuse. Result sets associated with a committed transaction are no longer valid. Subsequent requests for the same bean cause the container to issue a load to synchronize state with the underlying datasource.

Note that transactions begun in the container are implicitly committed. Also, any participant can rollback a transaction. For more information on transactions, see Chapter 8 "Handling Transactions with EJBs."


Commit Option C

Commit option C is supported by the iPlanet Application Server. Commit option C gets a bean instance from the free pool at the start of a transaction and transitions the instance back to the free pool at the end of the transaction.

The lifecycle for every business method invocation under commit option C looks like this:

ejbActivate-> ejbLoad -> business method -> ejbStore -> ejbPassivate

If there is more than one transactional client concurrently accessing the same entity EJBObject, the first client gets the ready instance and subsequent concurrent clients get new instances from the pool.


Handling Concurrent Access

As an entity bean developer, you do not have to be concerned about concurrent access to an entity bean from multiple transactions. The bean's container automatically provides synchronization in these cases. In an iPlanet Application Server, the container activates one entity bean instance for each simultaneously occurring transaction that uses the bean. Transaction synchronization is performed automatically by the underlying database during database access calls.

The iPlanet Application Server EJB container implementation does not provide its own synchronization mechanism when multiple transactions try to access an entity bean. It creates a new entity bean instance for every new transaction. The iPlanet Application Server container delegates the responsibility of the application synchronization.

You typically perform this synchronization in conjunction with the underlying database or resource. One approach, when using Bean Managed Persistence, would be to acquire the corresponding database locks in the ejbLoad() method, for example by choosing an appropriate isolation level or by using a select for update clause. The specifics depend on the database being used. For more information, see the EJB specification as it relates to concurrent access.

The following example ejbLoad() snippet illustrates the select for update syntax to obtain database locks. This prevents other instances from being loaded at the same time.

public void ejbLoad() throws java.rmi.RemoteException
{
....
// Get the lock on the corresponding DB table
   try {
      java.sql.Connection dbConn = ds.getConnection();
      String query = "SELECT accountNum, balance FROM accounts "
         + "WHERE customerId = ? FOR UPDATE";
      prepStmt = dbConn.prepareStatement(query);
      prepStmt.setString(1, m_customerId);
      resultSet = prepStmt.executeQuery();
      if ((resultSet != null) && resultSet.next()) {
         acctNum = resultSet.getInt(1);
         acctBalance = resultSet.getInt(2);
      } else {
         throw new RemoteException("Database error. "
               + "Couldn't find accout");
      }
   } catch (java.sql.SQLException e) {
      throw new RemoteException("Database error. "
            + "Couldn't load account");
   } finally {
      try {
         if (resultSet != null)
            resultSet.close();
         if (prepStmt != null)
            prepStmt.close();
         if (dbConn != null)
            dbConn.disconnect();
         } catch (java.sql.SQLException e) {
         System.out.println("Unexpected exception while "
            + "closing resources"); }
   }
}



Container Managed Persistence



An entity bean using container-managed persistence (CMP) defers the management of its state (or persistence) to the iPlanet Application Server. Normally, CMP beans persist to a relational database.

Developers use CMP to simplify the work of creating an entity bean. Rather than write all the JDBC code that is necessary to implement a BMP entity bean, a developer using CMP simply uses tools to create the bean's deployment descriptors. The deployment descriptors contain information that the container uses to map fields to the bean to columns in a relational database.

For more information on CMP, see Chapter 9.4 of the EJB 1.1 specification.

The iPlanet Application Server provides the following support for CMP entity beans:

  • Full support for the J2EE v 1.2 specification's CMP model (i.e., EJB 1.1).

  • Support for third party O/R mapping tools.

  • An "out-of-the-box" lightweight implementation of CMP. Lightweight CMP provides:

    • A basic object-to-relational (O/R) mapping tool within the iPlanet Application Server Deployment Tool that creates XML Deployment Descriptors for each CMP bean.

    • Support for compound (multi-column) primary keys.

    • Support for sophisticated custom finder methods.

    • Standards-based query language (SQL92).


Full J2EE Support

The iPlanet Application Server fully supports the Entity Bean Component Contract defined in the EJB 1.1 specification. Here are a few items of interest:

  • The iPlanet Application Server implements commit option C as defined in the EJB 1.1 specification.

  • The primary key class must be a subclass of java.lang.Object. This is in accordance with the specification, and ensures portability, but is noted because a few vendors allow primitive types (such as int) to be listed as the primary key class.


Third Party O/R Mapping Tools

iPlanet Application Server certifies third party enterprise tool vendors for use. In general, third-party CMP solutions that fully support the EJB1.1 specification work with iPlanet Web Server.

For example, Thought, Inc., provides CocoBase Enterprise as a sophisticated O/R mapping solution for mapping EJBs to relational databases. To use Cocobase, you build your EJBs using Cocobase's O/R Mapping Tool, then deploy the beans using the iPlanet Application Server's Deployment Tool or the iPlanet Application Server's Command Line Interface (CLI).

Several other vendors are currently completing certification. Check the website (developer.iplanet.com) for current information on certified third party O/R mapping tools.


Full Example of a CMP Entity Bean

For a full example of a CMP entity bean, look at the Product sample application from the J2EE Developer's Guide, which is available here:

install_dir/ias/ias-samples/j2eeguide/product


Using the Lightweight CMP Implementation

The iPlanet Application Server provides an "out-of-the-box" lightweight CMP implementation. The implementation includes a mapping tool, found in the iPlanet Application Server Deployment Tool, and a CMP runtime environment. The CMP runtime environment creates persistence managers for each CMP bean. The persistence managers then use information specified in XML deployment descriptors. The three deployment descriptors used in a CMP bean are:

  • ejb-jar.xml - There is one ejb-jar.xml file in each EJB module. This deployment descriptor is covered in detail in the EJB 1.1 specification.

  • ias-ejb-jar.xml - Like the ejb-jar.xml file, there is just one ias-ejb-jar.xml file per EJB module. To use lightweight CMP, you must set some properties in this file. For an overview of the DTD, see Chapter 11 "Packaging for Deployment."

  • property-file-name.xml - Additionally, each CMP Bean has its own deployment descriptor. The name of the file is specified in the ias-ejb-jar.xml file (by the properties-file-location element; see Chapter 11 "Packaging for Deployment," for details). The contents of this file determine how the reference implementation's persistence managers load and store each bean's state in a relational database.

There are two ways to generate these files. The following sections cover each method in detail:


Creating the Deployment Descriptors by Hand

It's easiest to understand what's happening in the iPlanet Application Server's Deployment Tool if you know what's happening behind the scenes, so the manual steps are explained first.


ejb-jar Deployment Descriptor

The ejb-jar.xml file is described in detail in the EJB 1.1 specification. The ejb-jar deployment descriptor is where important information like the transactional attributes of the beans and the fields of a bean that are going to be container-managed are specified. Any J2EE-compliant ejb-jar file is deployable on the iPlanet Application Server if you provide a corresponding ias-ejb-jar.xml file.


ias-ejb-jar Deployment Descriptor

The J2EE vendor-specific information for Enterprise JavaBeans is stored in another deployment descriptor, ias-ejb-jar.xml. The Document Type Definition (DTD) for this XML-based deployment descriptor is covered in detail in Chapter 11 "Packaging for Deployment."

There is some information that is unique to CMP beans that goes in this file, however, within the <persistence-manager> element:

  • The fully qualified class name for the factory class that creates persistence managers is specified in the <factory-class-name> element. The factory class name for the reference implementation is com.netscape.server.ejb.SQLPersistenceManagerFactory.

  • The relative path of the CMP bean's specific property file within the ejb-jar.xml file is specified in the <properties-file-location> element.

Here's a code snippet of what the relevant elements of the deployment descriptor look like:

...
   <persistence-manager>
      <factory-class-name>
         com.netscape.server.ejb.SQLPersistenceManagerFactory
      </factory-class-name>
      <properties-file-location>
         META-INF/MyProduct-ias-cmp.xml
      </properties-file-location>
   </persistence-manager>
...


CMP Bean Deployment Descriptor

The file name of the CMP bean-specific deployment descriptor is specified in the ias-ejb-jar.xml file. In the previous example, the properties file would be named MyProduct-ias-cmp.xml. The file's root element is the <ias-persistence-manager> node, but the rest is a simple bean property file. The file uses a simple XML format to describe various properties. The DTD file for this deployment descriptor is located here:

install_dir/ias/dtd/IASPersistence_manager_1_0.dtd

The tags in the xml file follow this basic format:

<bean-property>
   <property>
      <name></name>
      <type></type>
      <value></value>
      <delimiter></delimiter>
   </property>
</bean-property>

Here are descriptions of the subelements of <property>:

name

is one of these valid names: dataSource, allFields, findByPrimaryKeySQL, findByPrimaryKeyParms, insertSQL, insertParms, deleteSQL, deleteParms, loadSQL, loadParms, loadResults, storeSQL, storeParms, or the name of a custom finder.

Each of these properties is described later in this section.

type

is either java.lang.String or java.util.Vector. If Vector is used as the type, the value is treated as a comma-delimited list.

value

is any string.

delimiter

is always , (a comma).

The following properties are defined in the lightweight CMP bean's deployment descriptor:

  • Data Source (dataSource)

  • CMP field to RDB column mapping (allFields)

  • Persistence operations:

    • findByPrimaryKey (findByPrimaryKeySQL and findByPrimaryKeyParms)

    • insert (insertSQL and insertParms)

    • delete (deleteSQL and deleteParms)

    • load (loadSQL, loadParms, loadResults)

    • store (storeSQL and storeParms)

    • custom finders (optional)


Data Source
The first property used in the XML file is the dataSource property. The value of the dataSource property is the JNDI name of the JDBC data source used as a persistent store. For example:

...
   <bean-property>
      <property>
         <name>dataSource</name>
         <type>java.lang.String</type>
         <value>j2eeguide/ProductDB</value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
...


CMP Field to RDB Column Mapping
The allFields property is where the O/R mapping is specified. In the value element, a bracket-enclosed String maps the CMP fields to database columns. CMP fields go to the left side of the =, while database columns go to the right of the expression. A semicolon, ;, must separate the expressions. For example:

...
   <bean-property>
      <property>
         <name>allFields</name>
         <type>java.lang.String</type>
         <value>
             {description=DESCRIPTION;price=PRICE;productId=PRODUCTID;}
         </value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
...


Persistence Operations
Persistence operations consist of three types of properties. These properties follow the following naming patterns:

  • xxxxSQL is an SQL statement for a particular persistence operation (such as insert). The SQL statement in the xxxxSQL property is used to create a java.sql.PreparedStatement; therefore, the SQL statement should conform to the rules specified for parameterized queries (for example, use ? to signify a parameter). To understand how to map your CMP fields to SQL datatypes, see "Mapping Rules."

  • xxxxParms is a list of parameters that are sent to the persistence operation. The first field maps to the first parameter in the SQL statement (denoted by ?), the second maps to the second, and so on.

  • xxxxResults is a list of the fields in the ResultSet that are returned from the execution of the PreparedStatement.

The xxxx part of the name can be one of the following:

  • findByPrimaryKey (findByPrimaryKeySQL and findByPrimaryKeyResults)

  • insert (insertSQL and insertParms)

  • delete (deleteSQL and deleteParms)

  • load (loadSQL, loadParms, loadResults)

  • store (storeSQL and storeParms)

  • The name of a custom finder

Persistence operation properties vary based on whether their CMP bean has a single-field primary key or a multi-field primary key. Where there is a difference, it is noted in the following examples.


findByPrimaryKey
The findByPrimaryKey properties are findByPrimaryKeySQL and findByPrimaryKeyParms. It is not necessary to provide the findByPrimaryKeyResults property for the findByPrimaryKey property because it is already defined in the primary key class. This operation corresponds to the findByPrimaryKey() method in the EJB's home interface.

Here is a single-field primary key example:

...
   <bean-property>
      <property>
         <name>findByPrimaryKeySQL</name>
         <type>java.lang.String</type>
         <value>
            SELECT PRODUCTID FROM PRODUCT WHERE PRODUCTID = ?
         </value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
   <bean-property>
      <property>
         <name>findByPrimaryKeyParms</name>
         <type>java.util.Vector</type>
         <value>productId</value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
...

Here is a multi-field primary key example:

...
   <bean-property>
      <property>
         <name>findByPrimaryKeySQL</name>
         <type>java.lang.String</type>
         <value>
            SELECT PRODUCTID, DESCRIPTION FROM PRODUCT WHERE PRODUCTID = ? AND DESCRIPTION = ?
         </value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
   <bean-property>
      <property>
         <name>findByPrimaryKeyParms</name>
         <type>java.util.Vector</type>
         <value>productId,description</value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
...


insert
The insert properties are insertSQL and insertParms. Inserts are exactly the same for single- and multi-field primary keys. This property corresponds to the bean's create() method in its home interface.

...
   <bean-property>
      <property>
         <name>insertSQL</name>
         <type>java.lang.String</type>
         <value>
            INSERT INTO PRODUCT ( DESCRIPTION,PRICE,PRODUCTID ) VALUES(?,?,?)
         </value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
   <bean-property>
      <property>
         <name>insertParms</name>
         <type>java.util.Vector</type>
         <value>description,price,productId</value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
...


delete
The delete properties are deleteSQL and deleteParms. The delete operation provides the functionality for the remove() function in the bean's home interface.

Here is a single-field primary key example:

...
   <bean-property>
      <property>
         <name>deleteSQL</name>
         <type>java.lang.String</type>
         <value>DELETE FROM PRODUCT WHERE PRODUCTID = ?</value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
   <bean-property>
      <property>
         <name>deleteParms</name>
         <type>java.util.Vector</type>
         <value>productId</value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
...

Here is a multi-field primary key example:

...
   <bean-property>
      <property>
         <name>deleteSQL</name>
         <type>java.lang.String</type>
         <value>
            DELETE FROM PRODUCT WHERE PRODUCTID = ? AND DESCRIPTION = ?
         </value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
   <bean-property>
      <property>
         <name>deleteParms</name>
         <type>java.util.Vector</type>
         <value>productId,description</value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
...


load
The load properties are loadSQL, loadParms, and loadResults. Load operations are almost identical for single- and multi-field primary keys. There is a minor difference in the loadSQL property and, consequently, the loadParms property. Load operations correspond to the EJB's ejbLoad() method.

Here is a single-field primary key example:

...
   <bean-property>
      <property>
         <name>loadSQL</name>
         <type>java.lang.String</type>
         <value>
            SELECT DESCRIPTION,PRICE,PRODUCTID FROM PRODUCT WHERE PRODUCTID = ?
         </value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
   <bean-property>
      <property>
         <name>loadParms</name>
         <type>java.lang.String</type>
         <value>productId</value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
   <bean-property>
      <property>
         <name>loadResults</name>
         <type>java.util.Vector</type>
         <value>description,price,productId</value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
...

Here is a multi-field primary key example:

...
   <bean-property>
      <property>
         <name>loadSQL</name>
         <type>java.lang.String</type>
         <value>
            SELECT DESCRIPTION,PRICE,PRODUCTID FROM PRODUCT WHERE PRODUCTID = ? AND DESCRIPTION = ?
         </value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
   <bean-property>
      <property>
         <name>loadParms</name>
         <type>java.util.Vector</type>
         <value>productId,description</value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
   <bean-property>
      <property>
         <name>loadResults</name>
         <type>java.util.Vector</type>
         <value>description,price,productId</value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
...


store
The store properties are storeSQL and storeParms. As with load properties, there are small differences in the storeSQL and storeParms properties. Make sure you get the order right in the multi-field primary key. The store operation is performed when the EJB container calls the ejbStore() method on the bean implementation.

Here is a single-field primary key example:

...
   <bean-property>
      <property>
         <name>storeSQL</name>
         <type>java.lang.String</type>
         <value>
            UPDATE PRODUCT SET DESCRIPTION=?,PRICE=? WHERE PRODUCTID = ?
         </value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
   <bean-property>
      <property>
         <name>storeParms</name>
         <type>java.util.Vector</type>
         <value>description,price,productId</value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
...

Here is a multi-field primary key example:

...
   <bean-property>
      <property>
         <name>storeSQL</name>
         <type>java.lang.String</type>
         <value>
            UPDATE PRODUCT SET PRICE=? WHERE PRODUCTID = ? AND DESCRIPTION = ?
         </value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
   <bean-property>
      <property>
         <name>storeParms</name>
         <type>java.util.Vector</type>
         <value>price,productId,description</value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
...


Custom Finders
Optionally, you can add custom finders to the deployment descriptor. Custom finder operations follow slightly different rules than other operations:

  • For the xxxxSQL property of custom finders, the first argument to the finder method defined in the home interface maps to the first parameter in the SQL statement, the second to the second, and so on.

  • The xxxxResults property for custom finders maps the columns of the ResultSet of the SQL statement to the primary key's fields (for multi-field primary keys) or to the primary key itself (for single-field primary keys).

For example, suppose the following method is defined in an entity bean's home interface:

public Collection findInRange(double low, double high)
   throws FinderException, RemoteException;

The name of the property is the name found in the bean's home interface. In this example, this operation would have up to three properties in the deployment descriptor: findInRangeSQL, findInRangeParms, and findInRangeResults (needed only for a multi-field primary key).

Here are the properties that implement this operation for a single-field primary key:

...
   <bean-property>
      <property>
         <name>findInRangeSQL</name>
         <type>java.lang.String</type>
         <value>
            SELECT PRODUCTID FROM PRODUCT WHERE PRICE BETWEEN ? AND ?
         </value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
   <bean-property>
      <property>
         <name>findInRangeParms</name>
         <type>java.lang.Vector</type>
         <value>low,high</value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
...

Here are the properties that implement this operation for a multi-field primary key:

...
   <bean-property>
      <property>
         <name>findInRangeSQL</name>
         <type>java.lang.String</type>
         <value>
            SELECT PRODUCTID, DESCRIPTION FROM PRODUCT WHERE PRICE BETWEEN ? AND ?
         </value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
   <bean-property>
      <property>
         <name>findInRangeParms</name>
         <type>java.lang.Vector</type>
         <value>low,high</value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
   <bean-property>
      <property>
         <name>findInRangeResults</name>
         <type>java.util.Collection</type>
         <value>productid,description</value>
         <delimiter>,</delimiter>
      </property>
   </bean-property>
...


Mapping Rules
Lightweight CMP uses JDBC (specifically, the setter methods of the PreparedStatement interface) to map CMP fields to columns in a relational database table. Therefore, standard JDBC mapping rules apply to CMP fields.

For example, to map a java.lang.String to an SQL column, Lightweight CMP uses the setString method in the PreparedStatement interface. The documentation for the PreparedStatement interface specifies that setString maps to a VARCHAR.

Lightweight CMP supports all native Java field types, all the classes that represent native types (such as Integer), java.lang.String, java.sql.Date, java.sql.Time, java.sql.Timestamp, and arbitrary serializable objects. Table 6-1 describes the mappings between bean attributes and table columns.


Table 6-1    EJB/JDBC mapping

Java Type

JDBC Type

JDBC Driver Access Methods

boolean  

BIT  

getBoolean(), setBoolean()  

byte  

TINYINT  

getByte(), setByte()  

short  

SMALLINT  

getShort(), setShort()  

int  

INTEGER  

getInt(), setInt()  

long  

BIGINT  

getLong(), setLong()  

float  

FLOAT  

getFloat(), setFloat()  

double  

DOUBLE  

getDouble(), setDouble()  

byte[]  

VARBINARY or LONGVARBINARY(1)  

getBytes(), setBytes()  

java.lang.String  

VARCHAR or LONGVARCHAR(1)  

getString(), setString()  

java.lang.Boolean  

BIT  

getObject(), setObject()  

java.lang.Integer  

INTEGER  

getObject(), setObject()  

java.lang.Long  

BIGINT  

getObject(), setObject()  

java.lang.Float  

REAL  

getObject(), setObject()  

java.lang.Double  

DOUBLE  

getObject(), setObject()  

java.math.BigDecimal  

NUMERIC  

getObject(), setObject()  

java.sql.Date  

DATE  

getDate(), setDate()  

java.sql.Time  

TIME  

getTime(), setTime()  

java.sql.Timestamp  

TIMESTAMP  

getTimestamp(), setTimestamp()  

any serializable class  

VARBINARY or LONGVARBINARY(1)  

getBytes(), setBytes()  


Using the Deployment Tool

A simpler way to create the standard ejb-jar deployment descriptors for a CMP bean is by using the iPlanet Application Server Deployment Tool. This tool's extensive built-in help goes into great detail about how to create this deployment descriptor.

Start by either opening an existing EJB Module or creating a new one. For more information on how to use this tool to create a CMP bean, refer to the help within the tool. Once the EJB's class files have been added the EJB Module, you can right-click on the bean to edit its descriptor, as in Figure 6-1.

Figure 6-1    Selecting a bean in the iPlanet Application Server Deployment Tool



Once you open the deployment descriptor, the changes you make in the user interface are reflected in the CMP EJB's section of the ejb-jar.xml deployment descriptor, its section of the ias-ejb-jar.xml deployment descriptor, and the CMP bean-specific deployment descriptor. The CMP bean's mapping information is saved in a file named ejbname-ias-cmp.xml. For an in-depth description of the elements of the user interface, refer to the tool's help.

Figure 6-2 shows the Lightweight CMP tab.

Figure 6-2    The Lightweight CMP tab



After you have studied the previous section on creating a deployment descriptor by hand, everything should be familiar to you on the Lightweight CMP tab, with these exceptions:

  • The TABLE text box is an input field for the relational database table that you'll be accessing via the specified data source.

  • You can toggle the Key field for each EJB Attribute. To create a multi-field primary key, merely set more than one attribute to true; the change is reflected in the bean's corresponding deployment descriptor. (multi-field primary keys require some other modifications, namely the inclusion of a primary key class as defined in the EJB 1.1 specification).

  • Use the Name, Type, and Value fields for the custom finders. Use these in the same manner as described in the previous section.



    Note You can start creating your EJB's deployment descriptors in the iPlanet Application Server's Deployment Tool, save the application in the tool, edit the files by hand, and then go back into the tool. However, if you do this, make sure you re-open the EJB Module or J2EE Application in the tool before you edit the deployment descriptors, then re-save the application in the tool after you make the changes. If you fail to do this, your changes in the user interface are not reflected in the deployment descriptors.




Previous     Contents     Index     Next     
Copyright © 2002 Sun Microsystems, Inc. All rights reserved.

Last Updated March 06, 2002