Programmer's Guide



Chapter 21   Writing Entity Beans

This chapter describes the type of Enterprise Java Bean called entity bean. It specifically includes these sections:

About entity beans   Top of page

An entity bean is a server-side class that represents an object in an underlying data store. You can think of an entity bean class as a representation of a database table and each instance of the entity bean class as a single row of that table. Entity beans can also represent complex relationships among one or more related tables or components of a non-relational data store like SAP, PeopleSoft, or Lotus Notes.

SilverStream's Version 3.0 container managed persistence requires that the underlying data store be a relational database, and not some other type of data store, so this chapter focuses on EJBs used in this way.

Entity bean access

Entity beans deployed on the SilverStream Server can be accessed by:

Entity beans deployed on the SilverStream Server can access:

Entity bean components

Entity beans include the following components:

Component

Description

Entity bean class

The Java class that implements the javax.ejb.EntityBean interface. This class has member variables representing the fields of the underlying data store. These fields are known as persistent fields. The bean can also include any business logic that you want to perform on those fields. For example, data validation or aggregation.

    For more information about the javax.ejb.EntityBean interface, see the online API Reference.

Primary key class

A Java class that implements the Serializable interface and the equals() and hashCode() methods.

Like any primary key, the primary key class uniquely identifies the entity bean. The primary key class usually includes member variables representing the fields that make up the underlying data stores primary key.

Home interface

A Java interface that extends the javax.ejb.EJBHome interface.

The home interface includes the bean's lifecycle methods like create() and remove(). It can also include finder methods.

Finder methods are used to obtain a specific bean, Enumerations or Collections of beans. Finders are similar to SQL Select statements. Some simple examples of finder methods include: findByName(), findByID(), and findLargeAccounts(). If you use bean managed persistence, you will implement your own finders; if you use container managed persistence, you can use the container to generate the finder methods, or you can write your own.

At deployment time, the container generates the class that implements the home interface.

    For more information about the javax.ejb.EJBHome interface, see the online API Reference.

Remote interface

A Java interface that extends the javax.ejb.EJBObject interface.

The remote interface exposes the bean's public methods to the entity bean's clients. At deployment time, the container generates the class that implements this interface.

    For more information, see the online API Reference.

About the javax.ejb.EntityBean interface

The javax.ejb.EntityBean interface includes the methods listed below. They are used by the container to manage the bean, to synchronize the bean with its underlying data store, and to provide information to the bean about its current environment.

Method

Description

ejbActivate() and ejbPassivate()

Used by the container to manage bean instances.

ejbLoad() and ejbStore()

Used to synchronize the data in the entity bean's persistent fields with the underlying data store. The implementation of these methods depends upon the bean's persistence model.

setEntityContext() and unsetEntityContext()

Used to pass information to the entity bean from the container.

ejbRemove()

Used to delete a record from the underlying data store.

    For more information about these methods, see the Java2 Enterprise APIs in the online API Reference.

Entity bean persistence models   Top of page

Since entity beans are representations of an underlying data store, the data contained in the entity instances must be synchronized with the data in the row that they represent. This process of synchronization is known as persistence.

You might think of persistence as the following database-related tasks:

Since an entity bean is a representation of the row in the underlying data store, persistence also includes the following tasks:

The mechanism to provide persistence can be explicitly programmed in the entity bean; this is known as bean-managed persistence. The mechanism to provide persistence can also be implicit or declarative; this is known as container-managed persistence.

About the methods that manage persistence

The javax.ejb.EntityBean interface includes two methods that are used for persistence management. They are:

In entity beans with container-managed persistence (referred to as CMP beans), since the container manages the data, you can leave these methods empty. If the data needs to be manipulated before it is stored in the database or after the bean is populated, you can write an implementation for either method. See the section on Extending container-managed persistence for more information.

With BMP entity beans, the bean-writer must write method implementations for both ejbLoad() and ejbStore().

About container-managed persistence

With CMP beans, the container handles all of the database connection and data access functions. The bean writer specifies, in the deployment descriptor, which of the entity bean's persistent fields the container should manage. At deployment time, the deployer maps the entity bean to a database table in the underlying data store and maps the bean's persistent fields to columns in that table. The deployer also maps any finder methods to a SilverStream expression. (You can think of this mapping as specifying the Where clause of a SQL Select statement.) The container uses these mappings to generate the appropriate JDBC and SQL calls.

You might want to use CMP, because:

You might not use CMP, because:

About bean-managed persistence

In bean-managed persistence, everything is programmatic. The bean writer writes all of the necessary code to connect to the underlying data store, to create, delete, and update the records in the underlying data store and to synchronize the entity bean's persistent fields with the underlying data store. Unlike CMP, where the container generates all of this code at deployment time, the bean-writer must include this code directly in the entity bean.

Although it is more work, you might use BMP when:

You might not use BMP because:

Modifying the persistence model

You can modify the behavior of both the BMP and the CMP models using SilverStream. The SilverStream container provides the flexibility to use certain SilverStream features as appropriate to your application. For example, if you choose BMP, you can use AgaDatas to access your data instead of JDBC. If you choose CMP, you can write code in the ejbLoad() or ejbStore() to manage some field mappings that cannot be specified in a declarative way.

    For more information on these techniques, see the section Extending container-managed persistence.

Entity bean lifecycle   Top of page

Since entity beans represent rows in the underlying data store, you need to think about them in a different way than session beans. If a session bean does not exist, you instantiate one by calling its create() method. If you call a create method on an entity bean, you actually create a record in the underlying data store; calling remove() actually deletes the underlying record. This section describes the lifecycle of entity beans and the methods used to manage them. It includes the following sections:

Finding beans

Entity beans are located using "finder methods". Finder methods represent SQL Select statements. Finder methods can return the remote object reference of a single bean, or a Collection or Enumeration of remote object references. Every entity bean must define at least the findByPrimaryKey() method.

A findByPrimaryKey() operation on a CMP bean (with a transaction in progress) executes as follows:

  1. The EJB client, for example, a SilverStream page, calls the findByPrimaryKey() method on the EJBHome. (This example assumes that the home has already been obtained.)

  2. The home object calls the container with the database request.

  3. The container passes the request to the SilverStream Server.

  4. The SilverStream Server constructs the appropriate JDBC and calls the database.

  5. If data is returned, the container creates a primary key object.

    The bean's ejbLoad() method is not called until the client calls additional business methods. This is true for both lazy and non-lazy beans.

  6. The container returns a reference to the remote object to the caller.

  7. The caller can then call the bean's business methods.

  8. When the caller does call a business method (the first time only):

Creating beans

Entity bean instances are created by calling the create() method on the EJBHome and passing enough information about the entity to construct the database row. CMP entity beans are created as follows:

  1. The EJB client calls create() on the EJBHome.

    It must pass in enough information for a database record to be created (for example, it must include all required values which are not default values).

  2. The EJBHome passes the request to the container.

  3. The container instantiates the bean and calls the setEntityContext(), ejbCreate() and ejbStore() methods.

  4. The container passes the request to the SilverStream Server which constructs the JDBC calls to insert a database record.

  5. The SilverStream Server constructs the SQL and passes the INSERT request to the DBMS.

  6. The container calls the bean's ejbPostCreate() method which allows the bean to do any kind of work that might require a valid EJBObject.

  7. The container returns a remote reference to the caller.

Instance pooling

SilverStream (Version 3.0) does not maintain a pool of idle/unused entity beans. Instead it instantiates the beans as requested. Some EJB servers might maintain such a pool to reduce the costs associated with instantiating entity beans; however, because most of the cost of activating an entity bean is in the ejbLoad() operation, which has to be done whether or not the bean is pooled, the only saving from pooling beans is in reducing memory allocation. Given the total amount of memory allocation in every Java remote call, this is insignificant.

Removing beans

Entity bean instances are removed by calling the remove() method on the EJBObject. CMP entity beans are removed as follows:

  1. The EJB client calls remove() on the EJBObject.

  2. The EJBObject passes the request to the container.

  3. The container calls the bean's setEntityContext(), ejbActivate(), and ejbLoad().

  4. The container then passes the request to the SilverStream Server.

  5. The SilverStream Server constructs the JDBC DELETE request (using optimistic concurrency) to the DBMS.

  6. If the record is deleted, the container calls ejbRemove() and then discards the bean instance.

Updating beans

Entity beans are updated by changing the value of one or more persistent fields and synchronizing the change with the data store. CMP entity beans are updated as follows (this assumes that you have already done a findByPrimaryKey()):

  1. The client calls a business method (on the EJBObject) which changes the value of a persistent field.

  2. The container detects that a persistent field has been changed, so the container calls the bean's ejbStore() method and passes the data to the SilverStream Server.

  3. The SilverStream Server constructs the appropriate JDBC call (using optimistic concurrency).

  4. The EJB client can then call other business methods on the EJBObject.

For CMP entity beans, each time that you call a method, the SilverStream client compares the value in each persistent field of the bean with the value in a cached version of the record. (This cached version resides on the server.) If the values are different, the update sequence is performed. If the values are not different, no updates are performed. This comparison can be costly. At deployment, you can specify the methods that do not modify fields. This allows the container to avoid this comparison.

    For more information, see the chapter on deploying EJBs.

BMPs and ejbStore()

For BMP entity beans, each time that you call a method, the container calls the bean's ejbStore() method. It does this because the container cannot make any assumptions about what methods do or do not update the data. As a result, the code in the ejbStore() method can be called more frequently than you might expect. Since calls to the database can have a high performance cost, you should carefully consider how to structure your code.

One technique is to set a flag that indicates whether or not a database update is actually needed. If the update is not needed (because the method did not change any data), you can skip the database access. You can see an example of this in the bean managed entity bean example in the Examples3_EJB database.

Entity beans and transactions   Top of page

Entity bean transactions are declarative and are managed by the container. Entity beans cannot:

Understanding how SilverStream caches data

Here's how SilverStream manages an entity bean's transaction participation.

  1. As changes are made to an entity bean, SilverStream writes the changes to a transaction cache.

  2. There is one cache per transaction and it is registered with the container.

  3. The changes in the transaction cache are applied, but are not committed to the database.

  4. All of the changes are applied within the same database transaction (using optimistic concurrency).

  5. Unlike a standard SilverStream transaction, however, if a single operation within the transaction is not valid, the entire transaction is not necessarily rolled back.

  6. When the transaction commits the cache is deleted. For any operations following a commit, SilverStream will have to reget the beans.

If you are familiar with the way SilverStream transactions work, this model is slightly different.

Entity beans, transactions, and performance

To improve performance, consider making any calls to an entity bean within a transaction context, even if the bean does not require a commit or a rollback. This strategy can limit the reads done on the database.

One way to implement this strategy is to write a session bean that uses bean-demarcated transactions. The session bean can then be used to wrap any entity bean calls within the scope of a javax.transaction.UserTransaction.

Here's how it works when you call a method outside of a transaction:

Here's how it works when you wrap the method calls in a transaction, and set the delay instantiation radio button to false (non-lazy):

You can see the results of this type of this strategy by running the BankDemo located in the Examples3_EJB database. For an explanation of the code, see the EJB section of the online Application Techniques.

Developing entity beans   Top of page

You can use the SilverStream IDE to develop and deploy your entity beans, or you can use a third-party IDE to develop the bean, import it into the SilverStream environment and deploy it.

    For information about developing SilverStream objects using a third-party IDE, see Writing External Java Clients.

When you begin your development efforts, you need to decide the following before you start:

Development tasks

To develop an entity bean, you need to:

Deployment tasks

To deploy an entity bean:

    For more information on each of the steps in the deployment cycle, see the chapter on deploying EJBs.

Writing the primary key class   Top of page

The primary key class typically contains fields that correspond to the primary key columns of the database table that the entity bean represents.

The primary key class must:

For more examples of primary key classes, see the Examples3_EJB database.

Writing the entity bean class   Top of page

Writing the entity bean's Java class is like developing any standard Java class. Here are the guidelines for the entity bean class:

For information about using the SilverStream IDE to write the entity bean class, see the chapter on the Business Object Designer in the online Tools Guide.

For examples illustrating how to write an entity bean, see the EJB section in the online Application Techniques Guide.

Getting the entity context

One of the container methods that you will generally write code for is the setEntityContext() method. This method is called by the container in the early stages of the beans lifecycle. It is called after creating the instance, but before calling the bean's create() method.

The container calls it so that it can pass information about the current context to the entity bean. All you need to do is save this context in a member variable so that way it is available if you need it later.

First create a member variable like this:

  protected EntityContext m_context; 

Then, in the setEntityContext() method, save it as shown here:

  m_context = entityContext1; 

Writing a CMP entity bean implementation class

This section describes how to write the implementation of the ejbCreate() method. If you use the standard CMP features, you do not need to write a body for the ejbStore() and the ejbLoad() methods for a bean with container-managed persistence.

In CMP, SilverStream calls ejbLoad() the very first time a business method is invoked. It calls ejbStore() when it submits the changes to the database.

Writing a CMP ejbCreate() method

You do not have to write any ejbCreate() methods in your bean. You only need to write one if your application needs to add records to the database. The ejbCreate() should return null.

The following example shows an ejbCreate method for the EBCompanyBean.

  public EBCompanyPrimaryKey ejbCreate(String psCompanyID,  
   String psCompanyName) throws CreateException, RemoteException
{
   m_companyid = psCompanyID;
   m_companyname = psCompanyName;
   return null;
}

About CMP Finder methods

If the bean uses CMP, then finder methods are:

    For more information on mapping CMP Finder methods, see the chapter on the JAR Designer in the online Tools Guide.

Writing a BMP entity bean implementation class

To write an entity bean that uses bean managed persistence, you need to write the implementation of the ejbCreate(), the ejbStore() and the ejbLoad() methods. You also need to write finder methods.

For information on writing a bean with BMP, see the Examples3_EJB database and the online Application Techniques guide.

Writing BMP Finder methods

The finder methods represent a way to request an existing row. You can represent that row in many different ways by writing many different finder methods. For example, you might find a customer by last name, by first name, by customer id, by zip code, by country or region. Each of these can be represented by a Finder method.

There is no limit to the number of finder methods that you can define for a single bean class.

If the bean uses BMP, then finder methods are:

Here are the rules for writing finder methods:

Writing the remote interface   Top of page

The remote interface makes the bean's business methods available to clients. Like the home interface, you do not need to write the code that implements this interface. Instead, at deployment time, the container generates a class that implements this interface. The container then uses this class to respond to business method calls from clients.

Here are the requirements for the remote interface:

    For more information about the methods on the remote interface, see the Java2 Enterprise APIs in the online API Reference.

Writing the home interface   Top of page

The home interface makes the bean's create and finder methods available to clients. It extends the javax.ejb.EJBHome interface. At deployment time, the container takes this interface and generates a class. The container then uses this class as the factory for creating all instances of the session bean.

For entity beans, every home interface must include a findByPrimaryKey() method.

Here are the requirements for writing the bean's home interface.

    For more information about the methods on the home interface, see the Java2 Enterprise APIs in the online API Reference.

Extending container-managed persistence   Top of page

You might find that you want to use container-managed persistence for your entity bean, but have some situations where the container is not able to map directly to the target environment. You might have one of the following scenarios:

For these situations, you can still use container-managed persistence by adding some extensions. This allows you to use a combination of CMP and BMP which uses the best features of both persistence models.

Here's how it works:

  1. You use CMP declarations to load and store data (all reads and writes to the database use CMP).

  2. You then write code in the ejbLoad() and ejbStore() methods to handle complex load/store algorithms. Since the ejbLoad() method is called after the bean's member variables have been populated, and the ejbStore() method is called before the bean's member variables are written to the database, you can use these methods to massage the data.

  3. You use Method-style finders for any complex finder algorithms.

Using ejbLoad() and ejbStore() for data translation

Suppose that you have a database field. Its declaration is as a single byte character field. It denotes the customer's gender and can have the value M or F. The EJB implementation you are using requires a boolean member variable m_female. To ensure the correct data conversion, you create a dummy member variable; it is a character and is called m_dbGender. You can use CMP to map the m_dbGender to the database field.

In the ejbLoad() method, you need to write the code to populate the member variable, like this:

  m_female = (m_dbGender == `F'); 

In the ejbStore() method, you need to write the code to populate the database, like this:

  m_dbGender =(m_female ?'F':'M'); 

Using ejbLoad() and ejbStore() for field mappings

To overcome the limitation when the bean's container-managed fields do not map directly to database fields, follow this strategy:

  1. Create member variables equivalent to database fields.

  2. Map these fields to the database during deployment.

  3. In the ejbLoad() method, populate the other bean members from database equivalent members.

  4. In the ejbStore() method, populate the database equivalent members from other beans.

You can use this technique for any type of data translation or massaging, including evaluations in/out field expressions, and one-to-many bean references and data aggregation.

Writing complex finder methods in CMP

In many cases your finder methods in CMP can be expressed as WHERE clauses using the SilverStream Expression Builder tool. Some finder methods cannot be expressed as a simple where clause, typically because:

For any of these cases, you can write your own finder method to perform the complex logic. At deployment time, the finder method should be marked method-style instead of expression. You can write the finder method directly in the bean class, or implement it in a subclass of the bean.

    For more information on subclassing beans, see the JAR Designer chapter of the online Tools Guide.

Method-style finder requirements

Regardless of where you implement the method, these are the steps you should follow to write the method-style finder.

  1. Write an ejbFindXXX() method in the bean implementation (as you would for BMP), or subclass.

  2. In the ejbFindXXX() method, construct a SilverStream query (using the SilverStream expression language).

  3. Use the bean's context to get the bean's home object, cast the home to AgoEJBEntityHome, and call the AgoEJBEntityHome.findByExpression() passing in the query string.

  4. At deployment time, mark the Finder method as Method-style instead of Expression.

The following code snippet illustrates this technique:

  public Collection ejbFindByEmployee(EmployeeRemoteI emp)  
   throws FinderException, RemoteException
{
// Get the employee ID
   EmployeePK epk = (EmployeePK) emp.getPrimaryKey();
// Construct the where clause
   String where = "Tasks.TaskEmpID=" + epk.m_id;

// Get the EJB home object
   AgoEJBEntityHome home = (AgoEJBEntityHome)
     m_context.getEJBHome();
// Call the findByExpression method to perform the actual query
(Collection) home.findByExpression(where, null,
     home.FIND_COLLECTION, false,0)
}

    For more information on writing a SilverStream query, see Using SilverStream Expressions.

    For more information on marking Finder methods as method-style, see the Deploying EJBs.

How CMP method-style finders differ from BMP finders

The method-style finders that you write to provide complex logic for container-managed beans are different from the Finder methods that you write for BMP beans in the following ways:

In a BMP finder, you must:

In a modified-CMP finder, you must:

Using modified-CMP with third-party beans

You can write complex finder methods or perform data translation for container-managed beans even if you do not have their Java source by using this strategy:

  1. Subclass the bean to add the database equivalent member variables.

  2. Add the desired ejbFindXXX() methods.

  3. Update the deployment descriptor to remove the unmappable member variables and add the database equivalent member variables.

  4. Change the implementation class in the Deployment descriptor to point to the subclass.

    For more information on subclassing a bean, see the chapter on the Jar Designer in the online Tools Guide. Keep in mind that subclassing a bean using the JAR Designer:

It does not automatically handle unmappable fields.






Copyright © 2000, SilverStream Software, Inc. All rights reserved.