Download PDF Home History |  | API Search Feedback FAQ

> ## The SavingsAccountBean Example

> The entity bean illustrated in this section represents a simple bank account. The state of `SavingsAccountBean` is stored in the `savingsaccount` table of a relational database. The `savingsaccount` table is created by the following SQL statement:

>     CREATE TABLE savingsaccount
>       (id VARCHAR(3)
>       CONSTRAINT pk_savingsaccount PRIMARY KEY,
>       firstname VARCHAR(24),
>       lastname  VARCHAR(24),
>       balance   NUMERIC(10,2));

> The `SavingsAccountBean` example requires the following code:

> - Entity bean class (`SavingsAccountBean`)
> - Home interface (`SavingsAccountHome`)
> - Remote interface (`SavingsAccount`)

> This example also uses the following classes:

> - A utility class named `InsufficientBalanceException`
> - A client class called `SavingsAccountClient`

> The source code for this example is in this directory:

>     <INSTALL>/j2eetutorial14/ejb/savingsaccount/src/

> ### Entity Bean Class

> The sample entity bean class is called `SavingsAccountBean`. As you look through its code, note that it meets the requirements of any entity bean that uses bean-managed persistence. First, it implements the following:

> - `EntityBean` interface
> - Zero or more `ejbCreate` and `ejbPostCreate` methods
> - Finder methods
> - Business methods
> - Home methods

> In addition, an entity bean class with bean-managed persistence has these requirements:

> - The class is defined as `public`.
> - The class cannot be defined as `abstract` or `final`.
> - It contains an empty constructor.
> - It does not implement the `finalize` method.

> #### The EntityBean Interface

> The `EntityBean` interface extends the `EnterpriseBean` interface, which extends the `Serializable` interface. The `EntityBean` interface declares a number of methods, such as `ejbActivate` and `ejbLoad`, which you must implement in your entity bean class. These methods are discussed in later sections.

> #### The ejbCreate Method

> When the client invokes a `create` method, the EJB container invokes the corresponding `ejbCreate` method. Typically, an `ejbCreate` method in an entity bean performs the following tasks:

> - Inserts the entity state into the database
> - Initializes the instance variables
> - Returns the primary key

> The `ejbCreate` method of `SavingsAccountBean` inserts the entity state into the database by invoking the private `insertRow` method, which issues the SQL `INSERT` statement. Here is the source code for the `ejbCreate` method:

>     public String ejbCreate(String id, String firstName,
>        String lastName, BigDecimal balance)
>        throws CreateException {

>        if (balance.signum() == -1)  {
>           throw new CreateException
>              ("A negative initial balance is not allowed.");
>        }

>        try {
>           insertRow(id, firstName, lastName, balance);
>        } catch (Exception ex) {
>            throw new EJBException("ejbCreate: " +
>               ex.getMessage());
>        }

>        this.id = id;
>        this.firstName = firstName;
>        this.lastName = lastName;
>        this.balance = balance;

>        return id;
>     }

> Although the `SavingsAccountBean` class has only one `ejbCreate` method, an enterprise bean can contain multiple `ejbCreate` methods. For an example, see the `CartBean.java` source code in this directory:

>     <INSTALL>/j2eetutorial14/examples/ejb/cart/src/

> When you write an `ejbCreate` method for an entity bean, be sure to follow these rules:

> - The access control modifier must be `public`.
> - The return type must be the primary key.
> - The arguments must be legal types for the Java RMI API.
> - The method modifier cannot be `final` or `static.`

> The `throws` clause can include the `javax.ejb.CreateException` and exceptions that are specific to your application. An `ejbCreate` method usually throws a `CreateException` if an input parameter is invalid. If an `ejbCreate` method cannot create an entity because another entity with the same primary key already exists, it should throw a `javax.ejb.DuplicateKeyException` (a subclass of `CreateException`). If a client receives a `CreateException` or a `DuplicateKeyException`, it should assume that the entity was not created.

> The state of an entity bean can be directly inserted into the database by an application that is unknown to the Sun Java System Application Server Platform Edition 8. For example, an SQL script might insert a row into the `savingsaccount` table. Although the entity bean for this row was not created by an `ejbCreate` method, the bean can be located by a client program.

> #### The ejbPostCreate Method

> For each `ejbCreate` method, you must write an `ejbPostCreate` method in the entity bean class. The EJB container invokes `ejbPostCreate` immediately after it calls `ejbCreate`. Unlike the `ejbCreate` method, the `ejbPostCreate` method can invoke the `getPrimaryKey` and `getEJBObject` methods of the `EntityContext` interface. For more information on the `getEJBObject` method, see the section Passing an Enterprise Bean's Object Reference. Often, your `ejbPostCreate` methods will be empty.

> The signature of an `ejbPostCreate` method must meet the following requirements:

> - The number and types of arguments must match a corresponding `ejbCreate` method.
> - The access control modifier must be `public`.
> - The method modifier cannot be `final` or `static`.
> - The return type must be `void`.

> The `throws` clause can include the `javax.ejb.CreateException` and exceptions that are specific to your application.

> #### The ejbRemove Method

> A client deletes an entity bean by invoking the `remove` method. This invocation causes the EJB container to call the `ejbRemove` method, which deletes the entity state from the database. In the `SavingsAccountBean` class, the `ejbRemove` method invokes a private method named `deleteRow`, which issues an SQL `DELETE` statement. The `ejbRemove` method is short:

>     public void ejbRemove() {
>         try {
>             deleteRow(id);
>         catch (Exception ex) {
>             throw new EJBException("ejbRemove: " +
>             ex.getMessage());
>             }
>         }
>     }

> If the `ejbRemove` method encounters a system problem, it should throw the `javax.ejb.EJBException`. If it encounters an application error, it should throw a `javax.ejb.RemoveException`. For a comparison of system and application exceptions, see the section deploytool Tips for Entity Beans with Bean-Managed Persistence.

> An entity bean can also be removed directly by a database deletion. For example, if an SQL script deletes a row that contains an entity bean state, then that entity bean is removed.

> #### The ejbLoad and ejbStore Methods

> If the EJB container needs to synchronize the instance variables of an entity bean with the corresponding values stored in a database, it invokes the `ejbLoad` and `ejbStore` methods. The `ejbLoad` method refreshes the instance variables from the database, and the `ejbStore` method writes the variables to the database. The client cannot call `ejbLoad` and `ejbStore`.

> If a business method is associated with a transaction, the container invokes `ejbLoad` before the business method executes. Immediately after the business method executes, the container calls `ejbStore`. Because the container invokes `ejbLoad` and `ejbStore`, you do not have to refresh and store the instance variables in your business methods. The `SavingsAccountBean` class relies on the container to synchronize the instance variables with the database. Therefore, the business methods of `SavingsAccountBean` should be associated with transactions.

> If the `ejbLoad` and `ejbStore` methods cannot locate an entity in the underlying database, they should throw the `javax.ejb.NoSuchEntityException`. This exception is a subclass of `EJBException`. Because `EJBException` is a subclass of `RuntimeException`, you do not have to include it in the `throws` clause. When `NoSuchEntityException` is thrown, the EJB container wraps it in a `RemoteException` before returning it to the client.

> In the `SavingsAccountBean` class, `ejbLoad` invokes the `loadRow` method, which issues an SQL `SELECT` statement and assigns the retrieved data to the instance variables. The `ejbStore` method calls the `storeRow` method, which stores the instance variables in the database using an SQL `UPDATE` statement. Here is the code for the `ejbLoad` and `ejbStore` methods:

>     public void ejbLoad() {

>       try {
>         loadRow();
>       } catch (Exception ex) {
>         throw new EJBException("ejbLoad: " +
>           ex.getMessage());
>       }
>     }

>     public void ejbStore() {

>       try {
>         storeRow();
>       } catch (Exception ex) {
>         throw new EJBException("ejbStore: " +
>           ex.getMessage());
>       }
>     }

> #### The Finder Methods

> The finder methods allow clients to locate entity beans. The `SavingsAccountClient` program locates entity beans using three finder methods:

>     SavingsAccount jones = home.findByPrimaryKey("836");
>     ...
>     Collection c = home.findByLastName("Smith");
>     ...
>     Collection c = home.findInRange(20.00, 99.00);

> For every finder method available to a client, the entity bean class must implement a corresponding method that begins with the prefix `ejbFind`. The `SavingsAccountBean` class, for example, implements the `ejbFindByLastName` method as follows:

>     public Collection ejbFindByLastName(String lastName)
>       throws FinderException {

>       Collection result;

>       try {
>         result = selectByLastName(lastName);
>       } catch (Exception ex) {
>         throw new EJBException("ejbFindByLastName " +
>           ex.getMessage());
>       }
>       return result;
>     }

> The finder methods that are specific to your application, such as `ejbFindByLastName` and `ejbFindInRange`, are optional, but the `ejbFindByPrimaryKey` method is required. As its name implies, the `ejbFindByPrimaryKey` method accepts as an argument the primary key, which it uses to locate an entity bean. In the `SavingsAccountBean` class, the primary key is the `id` variable. Here is the code for the `ejbFindByPrimaryKey` method:

>     public String ejbFindByPrimaryKey(String primaryKey)
>       throws FinderException {

>       boolean result;

>       try {
>         result = selectByPrimaryKey(primaryKey);
>       } catch (Exception ex) {
>         throw new EJBException("ejbFindByPrimaryKey: " +
>           ex.getMessage());
>       }

>       if (result) {
>         return primaryKey;
>       }
>       else {
>         throw new ObjectNotFoundException
>           ("Row for id " + primaryKey + " not found.");
>       }
>     }

> The `ejbFindByPrimaryKey` method may look strange to you, because it uses a primary key for both the method argument and the return value. However, remember that the client does not call `ejbFindByPrimaryKey` directly. It is the EJB container that calls the `ejbFindByPrimaryKey` method. The client invokes the `findByPrimaryKey` method, which is defined in the home interface.

> The following list summarizes the rules for the finder methods that you implement in an entity bean class with bean-managed persistence:

> - The `ejbFindByPrimaryKey` method must be implemented.
> - A finder method name must start with the prefix `ejbFind`.
> - The access control modifier must be `public`.
> - The method modifier cannot be `final` or `static`.
> - The arguments and return type must be legal types for the Java RMI API. (This requirement applies only to methods defined in a remote--and not a local--home interface.)
> - The return type must be the primary key or a collection of primary keys.

> The `throws` clause can include the `javax.ejb.FinderException` and exceptions that are specific to your application. If a finder method returns a single primary key and the requested entity does not exist, the method should throw the `javax.ejb.ObjectNotFoundException` (a subclass of `FinderException`). If a finder method returns a collection of primary keys and it does not find any objects, it should return an empty collection.

> #### The Business Methods

> The business methods contain the business logic that you want to encapsulate within the entity bean. Usually, the business methods do not access the database, and this allows you to separate the business logic from the database access code. The `SavingsAccountBean` class contains the following business methods:

>     public void debit(BigDecimal amount)
>        throws InsufficientBalanceException {

>        if (balance.compareTo(amount) == -1) {
>            throw new InsufficientBalanceException();
>        }
>        balance = balance.subtract(amount);
>     }

>     public void credit(BigDecimal amount) {

>        balance = balance.add(amount);
>     }

>     public String getFirstName() {

>        return firstName;
>     }

>     public String getLastName() {

>        return lastName;
>     }

>     public BigDecimal getBalance() {

>        return balance;
>     }

> The `SavingsAccountClient` program invokes the business methods as follows:

>     BigDecimal zeroAmount = new BigDecimal("0.00");
>     SavingsAccount duke = home.create("123", "Duke", "Earl",
>         zeroAmount);
>     ...
>     duke.credit(new BigDecimal("88.50"));
>     duke.debit(new BigDecimal("20.25"));
>     BigDecimal balance = duke.getBalance();

> The requirements for the signature of a business method are the same for session beans and entity beans:

> - The method name must not conflict with a method name defined by the EJB architecture. For example, you cannot call a business method `ejbCreate` or `ejbActivate`.
> - The access control modifier must be `public`.
> - The method modifier cannot be `final` or `static`.
> - The arguments and return types must be legal types for the Java RMI API. This requirement applies only to methods defined in a remote--and not a local--home interface.

> The `throws` clause can include the exceptions that you define for your application. The `debit` method, for example, throws the `InsufficientBalanceException`. To indicate a system-level problem, a business method should throw the `javax.ejb.EJBException`.

> #### The Home Methods

> A home method contains the business logic that applies to all entity beans of a particular class. In contrast, the logic in a business method applies to a single entity bean, an instance with a unique identity. During a home method invocation, the instance has neither a unique identity nor a state that represents a business object. Consequently, a home method must not access the bean's persistence state (instance variables). (For container-managed persistence, a home method also must not access relationships.)

> Typically, a home method locates a collection of bean instances and invokes business methods as it iterates through the collection. This approach is taken by the `ejbHomeChargeForLowBalance` method of the `SavingsAccountBean` class. The `ejbHomeChargeForLowBalance` method applies a service charge to all savings accounts that have balances less than a specified amount. The method locates these accounts by invoking the `findInRange` method. As it iterates through the collection of `SavingsAccount` instances, the `ejbHomeChargeForLowBalance` method checks the balance and invokes the `debit` business method. Here is the source code of the `ejbHomeChargeForLowBalance` method:

>     public void ejbHomeChargeForLowBalance(
>         BigDecimal minimumBalance, BigDecimal charge)
>         throws InsufficientBalanceException {

>        try {
>            SavingsAccountHome home =
>            (SavingsAccountHome)context.getEJBHome();
>            Collection c = home.findInRange(new BigDecimal("0.00"),
>                minimumBalance.subtract(new BigDecimal("0.01")));

>            Iterator i = c.iterator();

>            while (i.hasNext()) {
>               SavingsAccount account = (SavingsAccount)i.next();
>               if (account.getBalance().compareTo(charge) == 1) {
>                  account.debit(charge);
>               }
>            }

>        } catch (Exception ex) {
>            throw new EJBException("ejbHomeChargeForLowBalance: "
>                + ex.getMessage());
>        }
>     }

> The home interface defines a corresponding method named `chargeForLowBalance` (see Home Method Definitions). Because the interface provides the client view, the `SavingsAccountClient` program invokes the home method as follows:

>     SavingsAccountHome home;
>     ...
>     home.chargeForLowBalance(new BigDecimal("10.00"),
>       new BigDecimal("1.00"));

> In the entity bean class, the implementation of a home method must adhere to these rules:

> - A home method name must start with the prefix `ejbHome`.
> - The access control modifier must be `public`.
> - The method modifier cannot be `static`.

> The `throws` clause can include exceptions that are specific to your application; it must not throw the `java.rmi.RemoteException`.

> #### Database Calls

> Table 26-1 summarizes the database access calls in the `SavingsAccountBean` class. The business methods of the `SavingsAccountBean` class are absent from the preceding table because they do not access the database. Instead, these business methods update the instance variables, which are written to the database when the EJB container calls `ejbStore`. Another developer might have chosen to access the database in the business methods of the `SavingsAccountBean` class. This choice is one of those design decisions that depend on the specific needs of your application.

> Before accessing a database, you must connect to it. For more information, see Chapter 31.

> Table 26-1 SQL Statements in SavingsAccountBean
> Method | SQL Statement
> --- | ---
> `ejbCreate` | `INSERT`
> `ejbFindByPrimaryKey` | `SELECT`
> `ejbFindByLastName` | `SELECT`
> `ejbFindInRange` | `SELECT`
> `ejbLoad` | `SELECT`
> `ejbRemove` | `DELETE`
> `ejbStore` | `UPDATE`

> ### Home Interface

> The home interface defines the `create`, finder, and home methods. The `SavingsAccountHome` interface follows:

>     import java.util.Collection;
>     import java.math.BigDecimal;
>     import java.rmi.RemoteException;
>     import javax.ejb.*;

>     public interface SavingsAccountHome extends EJBHome {

>         public SavingsAccount create(String id, String firstName,
>             String lastName, BigDecimal balance)
>             throws RemoteException, CreateException;

>         public SavingsAccount findByPrimaryKey(String id)
>             throws FinderException, RemoteException;

>         public Collection findByLastName(String lastName)
>             throws FinderException, RemoteException;

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

>         public void chargeForLowBalance(BigDecimal minimumBalance,
>            BigDecimal charge)
>            throws InsufficientBalanceException, RemoteException;
>     }

> #### create Method Definitions

> Each `create` method in the home interface must conform to the following requirements:

> - It must have the same number and types of arguments as its matching `ejbCreate` method in the enterprise bean class.
> - It must return the remote interface type of the enterprise bean.
> - The `throws` clause must include the exceptions specified by the `throws` clause of the corresponding `ejbCreate` and `ejbPostCreate` methods.
> - The `throws` clause must include the `javax.ejb.CreateException.`
> - If the method is defined in a remote--and not a local--home interface, then the `throws` clause must include the `java.rmi.RemoteException`.

> #### Finder Method Definitions

> Every finder method in the home interface corresponds to a finder method in the entity bean class. The name of a finder method in the home interface begins with `find`, whereas the corresponding name in the entity bean class begins with `ejbFind`. For example, the `SavingsAccountHome` class defines the `findByLastName` method, and the `SavingsAccountBean` class implements the `ejbFindByLastName` method. The rules for defining the signatures of the finder methods of a home interface follow.

> - The number and types of arguments must match those of the corresponding method in the entity bean class.
> - The return type must be the entity bean's remote interface type or a collection of those types.
> - The exceptions in the `throws` clause must include those of the corresponding method in the entity bean class.
> - The `throws` clause must contain the `javax.ejb.FinderException`.
> - If the method is defined in a remote--and not a local--home interface, then the `throws` clause must include the `java.rmi.RemoteException`.

> #### Home Method Definitions

> Each home method definition in the home interface corresponds to a method in the entity bean class. In the home interface, the method name is arbitrary, provided that it does not begin with `create` or `find`. In the bean class, the matching method name begins with `ejbHome`. For example, in the `SavingsAccountBean` class the name is `ejbHomeChargeForLowBalance`, but in the `SavingsAccount
> Home` interface the name is `chargeForLowBalance`.

> The home method signature must follow the same rules specified for finder methods in the preceding section (except that a home method does not throw a `FinderException`).

> ### Remote Interface

> The remote interface extends `javax.ejb.EJBObject` and defines the business methods that a remote client can invoke. Here is the `SavingsAccount` remote interface:

>     import javax.ejb.EJBObject;
>     import java.rmi.RemoteException;
>     import java.math.BigDecimal;

>     public interface SavingsAccount extends EJBObject {

>         public void debit(BigDecimal amount)
>             throws InsufficientBalanceException, RemoteException;

>         public void credit(BigDecimal amount)
>             throws RemoteException;

>         public String getFirstName()
>             throws RemoteException;

>         public String getLastName()
>             throws RemoteException;

>         public BigDecimal getBalance()
>             throws RemoteException;
>     }

> The requirements for the method definitions in a remote interface are the same for session beans and entity beans:

> - Each method in the remote interface must match a method in the enterprise bean class.
> - The signatures of the methods in the remote interface must be identical to the signatures of the corresponding methods in the enterprise bean class.
> - The arguments and return values must be valid RMI types.
> - The `throws` clause must include `java.rmi.RemoteException.`

> A local interface has the same requirements, with the following exceptions:

> - The arguments and return values are not required to be valid RMI types.
> - The `throws` clause does not include `java.rmi.RemoteException.`

> ### Running the SavingsAccountBean Example

> Before you run this example, you must define the data source, create the database, and deploy the `SavingsAccountApp.ear` file.

> #### Defining the Data Source

> Follow the instructions in Creating a Data Source. This data source is a factory for database connections. For more information, see DataSource Objects and Connection Pools.

> #### Creating the Database Table

> The instructions that follow explain how to use the `SavingsAccountBean` example with PointBase, the database software that is included in the Application Server bundle.

> 1. Start the PointBase server. For instructions, see Starting and Stopping the PointBase Database Server.
> 2. Create the `savingsaccount` database table by running the `create.sql` script.
> 1. In a terminal window, go to this directory:

> `<``INSTALL``>/j2eetutorial14/examples/ejb/savingsaccount/`

> 2. Type the following command, which runs the `create.sql` script:

> `asant create-db_common`

> #### Deploying the Application

> 1. In `deploytool`, open the `SavingsAccountApp.ear` file, which resides in this directory:

> `<``INSTALL``>/j2eetutorial14/examples/ejb/provided-ears/`

> 2. Deploy the `SavingsAccountApp` application.
> 3. In the Deploy Module dialog box, do the following:
> 1. Select the Return Client JAR checkbox.
> 2. In the field below the check box, enter the following:

> `<``INSTALL``>/j2eetutorial14/examples/ejb/savingsaccount`

> For detailed instructions, see Deploying the J2EE Application.

> #### Running the Client

> To run the `SavingsAccountClient` program, do the following:

> 1. In a terminal window, go to this directory:

> `<``INSTALL``>/j2eetutorial14/examples/ejb/savingsaccount/`

> 2. Type the following command on a single line:

> `appclient -client SavingsAccountAppClient.jar`

> 3. The client should display the following lines:

> `balance = 68.25
> balance = 32.55
> 456: 44.77
> 730: 19.54
> 268: 100.07
> 836: 32.55
> 456: 44.77
> 4
> 7`

> To modify this example, see the instructions in Modifying the J2EE Application.

Download PDF Home History |  | API Search Feedback FAQ

All of the material in The J2EE(TM) 1.4 Tutorial is copyright-protected and may not be published in other works without express written permission from Sun Microsystems.