In bean-managed persistence, the find methods
in the remote or local home interface must match the
ejbFind()
methods in the actual bean class. In
other words, for each method named
find<
SUFFIX
>()
in a home interface, there must be a corresponding
ejbFind<
SUFFIX
>()
method in the entity bean class with the same arguments and
exceptions. When a find method is invoked on an EJB home, the
container delegates the find()
method to a
corresponding ejbFind()
method on the bean
instance. The bean-managed entity is responsible for locating records
that match the find requests. There are two find methods in
ShipHomeRemote
:
public interface ShipHomeRemote extends javax.ejb.EJBHome { public ShipRemote findByPrimaryKey(Integer primaryKey) throws FinderException, RemoteException; public Collection findByCapacity(int capacity) throws FinderException, RemoteException; }
Here are the signatures of the corresponding
ejbFind()
methods in the
ShipBean
:
public class ShipBean implements javax.ejb.EntityBean { public Integer ejbFindByPrimaryKey(Integer primaryKey) throws FinderException {} public Collection ejbFindByCapacity(int capacity) throws FinderException {} }
Aside from the names, there’s a significant difference between
these two groups of methods. The find methods in the home interface
returns either an EJB object implementing the bean’s remote
interface—in this case, ShipRemote
—or
a collection of EJB objects in the form of a
java.util.Enumeration
or
java.util.Collection
. The
ejbFind()
methods in the bean class, on the other
hand, return either a primary key for the appropriate bean—in
this case, Integer
—or a collection of
primary keys. The methods that return a single value (whether a
remote/local interface or a primary key) are used whenever you need
to look up a single reference to a bean. If you are looking up a
group of references (for example, all ships with a certain capacity),
you have to use the method that returns either the
Collection
or Enumeration
type.
In either case, the container intercepts the primary keys and
converts them into remote references for the client.
The EJB 2.0 specification recommends that EJB 2.0 bean-managed
persistence beans use the
Collection
type
instead of the
Enumeration
type.
This recommendation is made so that BMP beans are more consistent
with EJB 2.0 CMP beans, which use only the
Collection
type.
It shouldn’t come as a surprise that the type returned—whether it’s a primary key or a remote (or local, in EJB 2.0) interface—must be appropriate for the type of bean you’re defining. For example, you shouldn’t put find methods in a Ship EJB to look up and return Cabin EJB objects. If you need to return collections of a different bean type, use a business method in the remote interface, not a find method from one of the home interfaces.
In EJB 2.0, the EJB container takes care of returning the proper
(local or remote) interface to the client. For example, the Ship EJB
may define a local and a remote home interface, both of which have a
findByPrimaryKey()
method. When findByPrimary()
is invoked on the
local or remote interface, it will be delegated to the
ejbFindByPrimary()
key method. After the
ejbFindByPrimaryKey()
method executes and returns the primary key,
the EJB container takes care of returning a
ShipRemote
or ShipLocal
reference to the client, depending on which home interface (local or
remote) was used. The EJB container also handles this for
multi-entity find methods, returning a collection of remote
references for remote home interfaces or local references for local
home interfaces.
Both find methods defined in the ShipBean
class
throw an EJBException
if a failure in the request
occurs when an SQL exception condition is encountered.
findByPrimaryKey()
throws an
ObjectNotFoundException
if no records in the database match the id
argument. This is exception should always be thrown by single-entity
find methods if no entity is found.
The
findByCapacity()
method returns an empty collection if no
SHIP
records with a matching capacity are found;
multi-entity find methods do not throw
ObjectNotFoundExceptions
if no entities are found.
It is mandatory for all entity remote and local home interfaces to
include the findByPrimaryKey()
method. This method
returns the remote interface type (in this case,
Ship
). It declares one parameter, the primary key
for that bean type. With local home interfaces, the return type of
any single-entity finder method is always the bean’s local
interface. With remote home interfaces, the return type of any
single-entity find method is the remote interface. You cannot deploy
an entity bean that doesn’t include a
findByPrimaryKey()
method in its home interfaces.
Following the rules outlined earlier, we can define two
ejbFind()
methods in ShipBean
that match the two find()
methods defined in the
ShipHome
:
public Integer ejbFindByPrimaryKey(Integer primaryKey) throws FinderException, { Connection con = null; PreparedStatement ps = null; ResultSet result = null; try { con = this.getConnection(); ps = con.prepareStatement("select id from Ship where id = ?"); ps.setInt(1, primaryKey.intValue()); result = ps.executeQuery(); // Does the ship ID exist in the database? if (!result.next()) { throw new ObjectNotFoundException("Cannot find Ship with id = "+id); } } catch (SQLException se) { throw new EJBException(se); } finally { try { if (result != null) result.close(); if (ps != null) ps.close(); if (con!= null) con.close(); } catch(SQLException se) { se.printStackTrace(); } } return primaryKey; } public Collection ejbFindByCapacity(int capacity) throws FinderException { Connection con = null; PreparedStatement ps = null; ResultSet result = null; try { con = this.getConnection(); ps = con.prepareStatement("select id from Ship where capacity = ?"); ps.setInt(1,capacity); result = ps.executeQuery(); Vector keys = new Vector(); while(result.next()) { keys.addElement(result.getObject("id")); } return keys; } catch (SQLException se) { throw new EJBException (se); } finally { try { if (result != null) result.close(); if (ps != null) ps.close(); if (con!= null) con.close(); } catch(SQLException se) { se.printStackTrace(); } } }
The mandatory findByPrimaryKey()
method uses the
primary key to locate the corresponding database record. Once it has
verified that the record exists, it simply returns the primary key to
the container, which then uses the key to activate a new instance and
associate it with that primary key at the appropriate time. If no
record is associated with the primary key, the method throws an
ObjectNotFoundException
.
The
ejbFindByCapacity()
method returns a
collection
of primary keys that match the criteria
passed into the method. Again, we construct a prepared statement that
we use to execute our SQL query. This time, however, we expect
multiple results, so we use the java.sql.ResultSet
to iterate through the results, creating a vector of primary keys for
each SHIP_ID
returned.
Find methods are not executed on bean instances that are currently
supporting a client application. Only bean instances that are not
currently assigned to an EJB object (i.e., instances in the instance
pool) are supposed to service find requests, which means that the
ejbFind()
methods in the bean instance have
somewhat limited use of the EntityContext
. The
EntityContext
methods
getPrimaryKey()
and
getEJBObject()
will throw exceptions because the bean instance is in the pool and is
not associated with a primary key or EJB object when the
ejbFind()
method is called.
Where do the objects returned by find methods originate? This seems
like a simple enough question, but the answer is surprisingly
complex. Remember that find methods aren’t executed by bean
instances that are actually supporting the client; rather, the
container selects an idle bean instance from the instance pool to
execute the method. The container is responsible for creating the EJB
objects and local or remote references for the primary keys returned
by the ejbFind()
method in the bean class. As the
client accesses these remote references, bean instances are swapped
into the appropriate EJB objects, loaded with data, and made ready to
service the client’s
requests.