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 application 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 ShipRemotefindByPrimaryKey
(Integer primaryKey) throws FinderException, RemoteException; public CollectionfindByCapacity
(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 IntegerejbFindByPrimaryKey
(Integer primaryKey) throws FinderException {} public CollectionejbFindByCapacity
(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 return 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 specification recommends that 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 CMP beans, which use only the Collection
type. The
Enumeration
type is
an artifact of EJB 1.0 and 1.1 and is maintained for backwards
compatibility.
It shouldn’t come as a surprise that the type returned—whether it’s a primary key or a remote (or local) 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.
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 or local interface type
(ShipRemote
or ShipLocal
). 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
ShipHomeRemote
:
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.