Previous Next Contents Index


Building Business Entity EJBs

This chapter describes how to use entity Enterprise JavaBeans (EJBs) to encapsulate your business entities.

This chapter explains what a business entity EJB is and what entity beans must contain. It also provides additional guidelines for creating entity beans, and for determining the entity beans needs of your applications.

This chapter contains the following sections:

Note. Netscape Application Server (NAS) 4.0 offers full support for entities even though such support is optional in the EJB Specification. The reason for this is because using entities lends flexibility to your applications.

All specifications are accessible from installdir/nas/docs/index.htm, where installdir is the location in which you installed NAS.


Introducing Business Entity EJBs
Often, the heart of a distributed, multi-user application involves interactions with data sources, such as a database, or even with an existing legacy application. Such interactions typically span the lifetime of the application and are transactional. In some cases, they outlast the application that spawned them. In most of these cases, the external data source or business object is transparent to the user, or is shielded or buffered from direct user interaction. These protected, transactional, and persistent interactions with databases, document files, and other business objects, are candidates for encapsulation in entity EJBs.

Business EJBs are self-contained, reusable components—with data members, properties, and methods—that represent instances of generic, transactionally aware, persistent data objects that can be shared among clients. Persistence refers to the creation and maintenance of a bean throughout the lifetime of the application. In NAS 4.0, beans are responsible for their own persistence, called bean-managed persistence.

As a developer, you code a bean-managed entity bean by providing database access calls—via JDBC or SQL—directly in the methods of the bean class. Database access calls must be placed in the code for the ejbCreate(...), ejbRemove(), ejbFindXXX(), ejbLoad(), and ejbStore() methods. The advantage of using bean-managed persistence is that such beans can be installed into any container without requiring the container to generate database calls.

Entity beans call 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 access through transactions.

As an NAS application developer, you cannot access the container's entity bean services directly, nor do you ever need to. Instead, know that the container is there to take care of low-level implementation details so that you can focus on the larger role the entity bean plays in your 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 point of entry to a data resource or business object, such as a database. Regardless of which client access an entity object at a given time, each client's view of the object 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 can look up an entity bean's home interface through the Java Naming and Directory Interface (JNDI).

An entity bean:

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 instances of an entity bean 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 of the 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. All specifications are accessible from installdir/nas/docs/index.htm, where installdir is the location in which you installed NAS.

In addition, the remote interface for most EJB objects also support specific business logic methods. These are the methods at the heart of your specific applications.


Entity Bean Components
When you code an entity bean you must provide the following class files:

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 Remote Exception { }
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() throuws 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 also define one ejbPostCreate() method for each ejbCreate() method. It may also optionally provide additional, developer-defined finder methods that take the form ejbFindXXX, where XXX represents a unique continuation of a method name (e.g., ejbFindApplesAndOranges) that does not duplicate any other methods 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 you want, but must not conflict with the names of methods used in the EJB architecture. Business methods must be declared as public. Method arguments and return value types must be legal for Java RMI. The throws clause may define application-specific exceptions, and may include java.rmi.RemoteException.

There are really two types of business methods you can implement in an entity bean: internal ones, that are used by other business methods in the bean, but are never accessed outside the bean itself; and external ones, that are referenced by the entity bean's remote interface.

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

Note. The examples in this section 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 instance of an entity bean is needed by a server application, the bean's container invokes ejbActivate() to ready an instance of the bean for use. Similarly, when an instance is no longer needed by the application, the bean's container invokes ejbPassivate() to disassociate the bean from the application.

If there are specific application tasks that need to be performed when a bean is first made ready for an application, or that need to be performed when a bean is no longer needed by the application, code those operations within these methods.

Note. 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's existence.

The container passivates session 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 Specifying Passivation Timeout.

For more information about ejbActivate() and ejbPassivate(), see the EJB Specification. All specifications are accessible from installdir/nas/docs/index.htm, where installdir is the location in which you installed NAS.

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

Using ejbLoad and ejbStore
An entity bean should permit its container to store the bean's 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.

The following example shows ejbLoad() and ejbStore() definitions that methods 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(); }
Note. For related information about isolation levels in beans that access transactions concurrently with other beans, see Handling Concurrent Access.

Using setEntityContext and unsetEntityContext
A container calls setEntityContext() after it creates an instance of an entity bean in order to provide the bean with an interface to the container itself. When you implement this method, use it to store the reference to the container 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 will eventually call finalize() on the instance to clean it up and dispose of it.

public void unsetEntityContext()
{
m_ctx = null;
}
Using ejbCreate Methods
The entity bean must also implement one or more ejbCreate(...) methods. There should be one such method for each way a client is allowed to invoke the bean. For example:

public int 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 that can be converted to a number for key purposes. Any arguments must be legal types for Java RMI. The throws clause, may define application-specific exceptions, may include java.rmi.RemoteException, or java.ejb.CreateException.

For each ejbCreate() method, the entity bean class may optinally 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 type the arguments of its corresponding ejbCreate method. The throws clause, may define application-specific exceptions, may include java.rmi.RemoteException, or may include 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 once instance instantiated at the same time, an entity bean must implement at least one method, FindByPrimaryKey(), 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. You can implement the FindByPrimaryKey() 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 may want to find a specific instance of an entity bean based on what the bean does, based on certain values the instance is working with, or based on still other criteria. These names of these implementation-specific finder methods take the form ejbFindXXX, where XXX represents a unique continuation of a method name (e.g., ejbFindApplesAndOranges) that does not duplicate any other methods names.

Finder methods must be declared as public, and their arguments and return values must be legal types for Java RMI. The return type of each finder method 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 java.util.Enumeration.

The throws clause of a finder method can be an application specific exception, may include java.rmi.RemoteException and/or java.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 also permits the bean class to implement the remote interface's methods, but recommends against this practice to avoid inadvertently passing a direct reference (via 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 enable a client accessing your 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; }
As 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 such method must be named create, and must correspond in number and type of arguments to an ejbCreate method defined in the entity bean class. The return type for each create method, however, does not match the return type of its corresponding ejbCreate method. 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 such method must be named findXXX (e.g., findApplesAndOranges), where XXX is a unique continuation of the method name. Each finder method must correspond to one of the finder methods defined in the entity bean class definition. The number and type of arguments must also correspond to the finder method definitions in the bean class. The return type, however, may be different. The return type for a finder method in the home interface must be the entity bean's remote interface type or a collection of interfaces.

Finally, all home interfaces automatically define two remove methods for destroying an EJB when it is no longer needed. 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 implemented 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 itself. The corresponding method in the bean class must have:

Besides the business methods you define in the remote interface, the EJBObject interface defines several abstract methods that enable you to retrieve the home interface for the bean, 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 to be used, see the EJB Specification. All specifications are accessible from installdir/nas/docs/index.htm, where installdir is the location in which you installed NAS.


Additional Entity Bean Guidelines
Before you decide what parts of your application you can represent as entity beans, you should know a few more things about entity beans. A couple of these things are related to the EJB specification for entity beans, and a couple are specific to NAS and its support for entity beans.

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

NAS offers several features through the NAS container and NAS APIs that enable your applications to take programmatic advantage of specific features of the NAS environment. You can embed API calls in your entity beans if you plan on using those beans only in the NAS 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.

NAS direct references are also serializable. You may wish to take advantage of this extension, but you should be aware that not all vendors support it.

Managing Transactions Most entity beans interact with databases. You can control transactions in beans using settings in the bean's property file. This permits you to specify transaction management at bean design time, deployment time, and run time. By having a bean handle transaction management you are freed from having explicitly to start, roll back, or commit transactions in the bean's database access methods.

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

Committing a Transaction When an entity bean signals that it is time to commit a transaction, the actual commit process is handled by the bean's container. Besides affecting the data your application processes, commit time also affects the state of an entity bean. The NAS container implements commit option C as described in the EJB specification.

When a commit occurs, it signals to the container that the entity bean has completed its useful work, and should synchronize its state with the underlying data store. 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 data store.

Note that transactions begun in the container are implicitly committed. Also, any participant can roll back a transaction. For more information about transactions, see Handling Transactions with EJBs.

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 NAS, the NAS container activates one instance of an entity bean for each simultaneously-occurring transaction that uses the bean. Transaction synchronization is performed automatically by the underlying database during database access calls.

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

You typically perform this synchronization in conjunction with the underlying database or resource. One possible approach 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 backend that is being used. For more information, see the EJB specification as it relates to concurrent access.

The following example ejbLoad() snippet illustrates the use of "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"); } 
    } 
} 
Accessing Databases Most entity beans work with databases. Always use the transaction and security management methods in javax.ejb.deployment.ControlDescriptor to manage transaction isolation level and transaction requirements. For more information about creating and managing transactions with beans, see Chapter 8, "Handling Transactions with EJBs".

To work with data in the context of a bean-managed transaction, use JDBC. For more information about using JDBC to work with data, see Chapter 9, "Using JDBC for Database Access".


Determining the Business Objects in Your Applications
When you adopt the NAS application programming model defined in this book, you split your application into general parts. For example, you separated your presentation logic from your business logic and transient business rules that you placed in session beans. You also determined which part(s) of your application were best handled as servlets, the central dispatcher for your application. What's left are the data and business objects around which your entire application revolves.

In general, business entities correspond to underlying database and document structures. So, for example, all corporations maintain extensive employee databases. An employee database is a perfect candidate for wrapping with a business entity to manage data access.

Choosing a Coarse Bean Granularity By their very nature as business objects, entity beans tend to be larger and more monolithic than session beans. For developing database intensive applications, this is just as well, because fewer, larger objects improve the speed and performance of your distributed application.

As a bean developer, you can make choices that affect the application's speed and performance at bean design time. For example, to increase performance, we recommend that you create fewer, larger beans. You may also choose to gang database access and updates to minimize data traffic.

You should also work with your system administrator to anticipate how your application will be deployed and how many users will access the system at the same time. Armed with that information, you can make wiser choices about what your entity objects look like and how they will be shared within and across applications.

 

© Copyright 1999 Netscape Communications Corp.