Prev | Next | J2EETM Developer's Guide
Entity Beans |
ACCOUNT
table of a relational database. The ACCOUNT
table was created by the following SQL statement:
To write an entity bean, you must provide the following code:CREATE TABLE account (id VARCHAR(3) CONSTRAINT pk_account PRIMARY KEY, firstname VARCHAR(24), lastname VARCHAR(24), balance DECIMAL(10,2));
)
)
doc/guides/ejb/examples/account
directory.
. As you look through its code, note that it meets the requirements of every entity bean:
EntityBean
interface.public
.abstract
or final
.ejbCreate
and ejbPostCreate
methods.finalize
method.
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.
create
method, the EJB container invokes the corresponding ejbCreate
method. Typically, an ejbCreate
method in an entity bean performs the following tasks:
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:
Although thepublic 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; }
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:
public
.final
or static
.throws
clause may include the javax.ejb.CreateException
and other exceptions that are specific to your application. An ejbCreate
method usually throws a CreateException
if an input parameter is invalid. If an ejbCreate
method cannnot 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 a non-J2EE application. For example, a SQL script might insert a row into the ACCOUNT
table. Although the entity bean for this row was not created by an ejbCreate
method, the bean can be located by a client program.
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 Entity Bean's Object Reference on page 71.) Often, your ejbPostCreate
methods will be empty.
The signature of an ejbPostCreate
must meet the following requirements:
ejbCreate
method.public
.final
or static
.void
.throws
clause may include the javax.ejb.CreateException
, and other exceptions that are specific to your application.
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:
If thepublic void ejbRemove() { try { deleteRow(id); } catch (Exception ex) { throw new EJBException("ejbRemove: " + ex.getMessage()); } }
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 on page 64.)
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.
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 on page 57.)
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 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()); } }
program locates entity beans with three finder methods:
For every finder method available to a client, the entity bean class must implement a corresponding method that begins with the prefixAccount jones = home.findByPrimaryKey("836"); . . . Collection c = home.findByLastName("Smith"); . . . Collection c = home.findInRange(20.00, 99.00);
ejbFind
. The AccountEJB
entity bean class, for example, implements the ejbFindByLastName
method as follows:
The finder methods specific to your application, such aspublic Collection ejbFindByLastName(String lastName) throws FinderException { Collection result; try { result = selectByLastName(lastName); } catch (Exception ex) { throw new EJBException("ejbFindByLastName " + ex.getMessage()); } if (result.isEmpty()) { throw new ObjectNotFoundException("No rows found."); } else { return result; } }
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:
Thepublic 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."); } }
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:
ejbFindByPrimaryKey
method must be implemented.ejbFind
.public
.final
or static
.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, it should throw a FinderException.
AccountEJB
entity bean contains these business methods:
Thepublic 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; }
AccountClient
program invokes the business methods as follows:
The requirements for the signature of a business method are the same for both session and entity beans:Account duke = home.create("123", "Duke", "Earl"); duke.credit(88.50); duke.debit(20.25); double balance = duke.getBalance();
ejbCreate
or ejbActivate
. public
.final
or static
.throws
clause may include the 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
.
AccountEJB
class:
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. See the Database Connections chapter for instructions.
AccountHome
interface follows:
Theimport java.util.Collection; import java.rmi.RemoteException; import javax.ejb.*; public interface AccountHome extends EJBHome { public Account create(String id, String firstName, String lastName) 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; }
create
methods in the home interface must conform to these requirements:
ejbCreate
method in the enterprise bean class.throws
clause includes the exceptions specified by the throws
clause of the corresponding ejbCreate
and ejbPostCreate
methods.throws
clause contains the java.rmi.RemoteException
and the javax.ejb.CreateExceptio
n.
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:
throws
clause include those of the corresponding method in the entity bean class.throws
clause contains the javax.ejb.FinderException
and the javax.ejb.RemoteException
.javax.ejb.EJBObject
and defines the business methods that a client may invoke. Here is the Account
remote interface:
The requirements for the method definitions in a remote interface are the same for both session and entity beans: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; }
throws
clause must include java.rmi.RemoteException
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.
1. From the command-line prompt, run the Cloudscape database server:
(For more information, see the section, Cloudscape Server on page 162.)cloudscape -start
2. Edit the script that creates the account database table.
In thecd $J2EE_HOME/doc/guides/ejb/examples/util
cloudTable.sh
script, change <installation-location> to the directory in which you installed the J2EE SDK.
In thecd %J2EE_HOME%\doc\guides\ejb\examples\util
cloudTable
script, change <installation-location> to the directory in which you installed the J2EE SDK.
3. Run the script that creates the account database table.
Windows:cd $J2EE_HOME/doc/guides/ejb/examples/account ../util/cloudTable.sh
Note: If you are not using a Cloudscape database, you may run thecd %J2EE_HOME%\doc\guides\ejb\examples\account ..\util\cloudTable
account/createTable.sql
script to create the account table.
AccountEJB
. (For an introduction to the wizard, see the Getting Started chapter.)
a. Select the Entity radio button.
b. In the Display Name field, enter AccountBean
.
Entity Settings Dialog Box:
Select the radio button labelled "Bean managed persistence."Resource References Dialog Box:
a. Click Add.
b. In the Coded Name field, enter jdbc/AccountDB
.
c. In the Type column, select javax.sql.DataSource.
d. In the Authentication column, select Container.Transaction Management Dialog Box:
For the business methods, in the Transaction Type column select Required. (The business methods aredebit
,credit
,getFirstName
,getLastName
, andgetBalance
.)
2. In the second dialog box, for the AccountBean entry in the Component/Reference Name field, enter MyAccount
in the JNDI Name field.
3. For the jdbc/AccountDB entry, enter jdbc/Cloudscape
in the JNDI Name field.