Home TOC |
Search
Feedback |
A Bean-Managed Persistence Example
The entity bean illustrated in this section represents a simple bank account. The state of the entity bean is stored in the myaccount table of a relational database. The myaccount table was created by the following SQL statement:
CREATE TABLE myaccount (id VARCHAR(3) CONSTRAINT pk_account PRIMARY KEY, firstname VARCHAR(24), lastname VARCHAR(24), balance DECIMAL(10,2));To write an entity bean, you must provide the following code:
This example also makes use of the following classes:
The source code for this example is in the examples/src/ejb/account directory. To compile the code, go to the examples/src directory and type ant account.
Entity Bean Class
The sample entity bean class is called AccountEJB. As you look through its code, note that it meets the requirements of every entity bean:
- It implements the EntityBean interface.
- The class is defined as public.
- The class cannot be defined as abstract or final.
- It implements zero or more ejbCreate and ejbPostCreate methods.
- It implements the finder methods (only for bean-managed persistence).
- It implements the business methods.
- 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 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 AccountEJB 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 in the AccountEJB class:
public String ejbCreate(String id, String firstName, String lastName, double balance) throws CreateException { if (balance < 0.00) { 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 AccountEJB class has just one ejbCreate method, an enterprise bean may contain multiple ejbCreate methods. For an example, see the CartEJB.java source code.
When writing 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 (only for bean-managed persistence).
- The arguments must be legal types for Java RMI.
- The method modifier cannot be final or static
The throws clause may 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 may be directly inserted into the database by an application that is unknown to the J2EE server. For example, a SQL script might insert a row into the myaccount 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 Passing an Enterprise Bean's Object Reference. Often, your ejbPostCreate methods will be empty.
The signature of an ejbPostCreate 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 may include the javax.ejb.CreateException and exceptions that are specific to your application.
The ejbRemove Method
A client removes an entity bean by invoking the remove method. This invocation causes the EJB client to call the ejbRemove method, which deletes the entity state from the database. The code for the ejbRemove method in the AccountEJB class follows:
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, Handling Exceptions.
An entity bean may also be removed directly by a database deletion. For example, if a 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 may not 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 container performs these functions for you. The AccountEJB class relies on the container to synchronize the instance variables with the database. Therefore, the business methods of AccountEJB should be associated with transactions. For instructions on setting transaction attributes for methods, see the section, Running the New Enterprise Bean Wizard.
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 AccountEJB class, ejbLoad invokes the loadRow method, which issues a 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 with a 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("ejbLoad: " + ex.getMessage()); } }The Finder Methods
The finder methods allow clients to locate entity beans. The AccountClient program locates entity beans with three finder methods:
Account 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 AccountEJB entity bean 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 specific to your application, such as ejbFindByLastName and ejbFindInRange, are optional, but the ejbFindByPrimaryKey method is required. As its name infers, the ejbFindByPrimaryKey method accepts as an argument the primary key, which it uses to locate an entity bean. In the AccountEJB 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 primaryKey for both the method argument and 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 Java RMI.
- The return type must be the primary key or a collection of primary keys.
The throws clause may include the javax.ejb.FinderException, and other exceptions that are specific to your application. If a finder method returns a single primary key, it should throw the javax.ejb.ObjectNotFoundException if the requested entity does not exist. The ObjectNotFoundException is 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, allowing you to separate business logic from the database access code. The AccountEJB entity bean contains these business methods:
public void debit(double amount) throws InsufficientBalanceException { if (balance - amount < 0) { throw new InsufficientBalanceException(); } balance -= amount; } public void credit(double amount) { balance += amount; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public double getBalance() { return balance; }The AccountClient program invokes the business methods as follows:
Account duke = home.create("123", "Duke", "Earl", 0.00); duke.credit(88.50); duke.debit(20.25); double balance = duke.getBalance();The requirements for the signature of a business method are the same for both session 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 Java RMI.
The throws clause may 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.
Database Calls
The following table summarizes the database access calls in the AccountEJB class:
Table 3 SQL Statements in AccountEJB Method
SQL Statement
ejbCreate
insert
ejbFindByPrimaryKey
select
ejbFindByLastName
select
ejbFindInRange
select
ejbLoad
select
ejbRemove
delete
ejbStore
update
The business methods of the AccountEJB 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 may have chosen to access the database in the business methods of the AccountEJB class. It's a design decision that depends on the specific needs of your application.
Before accessing a database you must connect to it. For more information, see the section, Resource Connections.
Home Interface
The home interface defines the methods that allow a client to create and find an entity bean. The AccountHome interface follows:
import java.util.Collection; import java.rmi.RemoteException; import javax.ejb.*; public interface AccountHome extends EJBHome { public Account create(String id, String firstName, String lastName, double balance) throws RemoteException, CreateException; public Account findByPrimaryKey(String id) throws FinderException, RemoteException; public Collection findByLastName(String lastName) throws FinderException, RemoteException; public Collection findInRange(double low, double high) throws FinderException, RemoteException; }Each create method in the home interface must conform to these requirements:
- It has the same number and types of arguments as its matching ejbCreate method in the enterprise bean class.
- It returns the remote interface type of the enterprise bean.
- The throws clause includes the exceptions specified by the throws clause of the corresponding ejbCreate and ejbPostCreate methods.
- The throws clause contains the java.rmi.RemoteException and the javax.ejb.CreateException.
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 name of one in the entity bean class begins with ejbFind. For example, the AccountHome class defines the findByLastName method, and the AccountEJB 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 is the entity bean's remote interface type, or a collection of those types.
- The exceptions in the throws clause include those of the corresponding method in the entity bean class.
- The throws clause contains the javax.ejb.FinderException and the javax.ejb.RemoteException.
Remote Interface
The remote interface extends javax.ejb.EJBObject and defines the business methods that a client may invoke. Here is the Account remote interface:
import javax.ejb.EJBObject; import java.rmi.RemoteException; public interface Account extends EJBObject { public void debit(double amount) throws InsufficientBalanceException, RemoteException; public void credit(double amount) throws RemoteException; public String getFirstName() throws RemoteException; public String getLastName() throws RemoteException; public double getBalance() throws RemoteException; }The requirements for the method definitions in a remote interface are the same for both session 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.
Tips for Running the AccountEJB Example
Setting Up the Database
The instructions that follow explain how to use the AccountEJB example with a Cloudscape database. The Cloudscape software is included with the J2EE SDK download bundle. You may also run this example with databases provided by other vendors. (See the Release Notes of the J2EE SDK for a list of supported databases.)
- From the command-line prompt, run the Cloudscape database server by typing cloudscape -start. (When you are ready to shut down the server, type cloudscape -stop.)
- Create the myaccount database table:
Note: If you are not using a Cloudscape database, you may use the examples/src/ejb/sql/myaccount.sql script to create the myaccount table.
Running the New Enterprise Bean Wizard
To start the New Enterprise Bean Wizard, select File->New Enterprise Bean. Although the wizard displays many dialog boxes, for this example only the following dialog boxes require input.
- General Dialog Box
- Entity Settings Dialog Box
- Resource References Dialog Box:
- Click Add.
- In the Coded Name field, enter jdbc/AccountDB.
- In the Type column, select javax.sql.DataSource.
- In the Authentication column, select Container.
- Transaction Management Dialog Box
Deploying the J2EE Application
- In the deploytool, select Tools->Deploy.
- Click the radio button labelled Return Client Jar. Specify AccountAppClient.jar as the file name.
- In the JNDI Names dialog, in the Application table enter MyAccount as the JNDI name for the AccountBean.
- In the References table, enter jdbc/Cloudscape as the JNDI name for the jdbc/AccountDB reference name.
- In the References table, enter MyAccount as the JNDI name for the ejb/SimpleAccount reference, which is an enterprise bean reference in the AccountClient code.
Running the J2EE Application
- In a terminal window, set the APPCPATH environment variable to the name of the stub client JAR file (AccountAppClient.jar).
- Go to the directory containing the application EAR file.
- Run the application with the runclient script. In the following example, the application EAR file is AccountApp.ear and the name of the J2EE application client is AccountClient:
runclient -client AccountApp.ear -name AccountClient- In the login dialog, enter guest as the user name and guest123 as the password.
- The client should display the following lines in the terminal window:
balance = 68.25 balance = 32.53 456: 44.77 730: 19.53 268: 100.06 836: 32.53 456: 44.77
Home TOC |
Search
Feedback |