Tutorial Index Page
Bean-managed Persistence in Entity Beans
Now we know enough to return to entity beans and handle the second type
of entity beans -- those with "bean-managed persistence".
As we saw in working with our entity beans, the EJB container takes
care of making sure the entity bean object stays around. Behind the
scenes, the EJB container loads the data from the database, stores the
data from the database, searches for the data etc. This is known
as "container managed persistence".
While this is very convenient, sometimes it may be useful to handle
the persistence in a different way. For instance, you may want a
particular database organization. Or you may want to send the data
to some central server rather than a database. Or you may want to
use a database that does not support JDBC. In such cases, you as
the bean programmer can take over the responsibility of managing the "persistence"
of the data -- of where the data stays when the server is not running or
when the bean is not in memory. This is known as "bean-managed persistence."
(Though sometimes people are known to use bean-managed persistence because
from a reading of EJB teaching material, they are able to understand the
concept of saving data and restoring data, but fail to understand container
managed persistence. This is not recommended as a good design
reason for using bean-managed persistence! In any event, anybody
who has followed this tutorial should be very comfortable with container-managed
persistence and should have no reason to view it as something particularly
mysterious.)
When you use bean-managed persistence, there are various entry points
you implement in your EJB implementation. In your examination of
the Blizzard generated frameworks, you should have seen empty functions
ejbCreate,
ejbPostCreate,
ejbLoad
and ejbStore. The container insures that these are called
at various points in the EJB lifecycle. The ejbCreate function
is called in response to a create function call in the home interface.
The ejbPostCreate function is called after the EJB has been created.
The ejbRemove function is called when the EJB needs to be deleted.
The ejbLoad and ejbStore functions are of particular interest in bean-managed
persistence. In bean-managed persistence, you are required to use
these functions to load the data from the persistent storage into the bean,
and store the data from the bean back in to the persistent storage.
In addition, in bean-managed persistence you are required to supply
ejbFind*
methods matching all the find* methods of the home interface. The
job of these methods is to find the bean's primary key (or set of primary
keys) as specified by the arguments, and to give this primary key back
to the container. Since all beans at least have one finder method
findByPrimaryKey, the bean class must at least contain an ejbFindByPrimaryKey
method.
Let us turn our original "Props" bean into a BMP (short form of "bean-managed-persistence")
bean. We already have our database tables. We need to (1) tell
the container that the EJB is bean managed, and (2) implement the various
BMP functions.
To tell the container the Props bean will now be bean managed, edit
the deployment descriptor and change the <persistence-type>
to Bean, and remove the two <cmp-field> entries.
To implement the BMP functions, we will need two new imports since we
will be doing JDBC. Add "import java.sql.*" and "import
javax.sql.*" to the implementation file, PropsBean.java.
Following are some rather simple implementations of the functions.
First of all, in "ejbCreate" we write code to insert a row in
"PropsTable". (Note: Make sure the SQL syntax
is correct for your database. All occurrences of "value"
are quoted below because "value" is a SQL-92 reserved word. But the
quoting characters can be different for different databases, and the double
quote character used here may not the right one. If it proves to
be too much trouble, you may just want to use a different name instead
of "value" and change the column name in the database.)
public java.lang.String ejbCreate( String key, String
value )
throws javax.ejb.CreateException,
java.rmi.RemoteException
{
this.key = key;
this.value = value;
boolean duplicateKey = false;
try {
InitialContext ctx
= new InitialContext();
DataSource ds = (DataSource)
ctx.lookup( "java:comp/env/jdbc/propsDataSource" );
Connection conn =
ds.getConnection();
PreparedStatement
stmt = conn.prepareStatement(
"SELECT * FROM PropsTable WHERE KEY=?" );
stmt.setString( 1,
key );
ResultSet rs = stmt.executeQuery();
duplicateKey = rs.next();
rs.close();
stmt.close();
if ( ! duplicateKey
) {
stmt = conn.prepareStatement(
"INSERT INTO PropsTable (KEY,\"VALUE\") VALUES (?,?)");
stmt.setString( 1, key );
stmt.setString( 2, value );
stmt.executeUpdate();
stmt.close();
}
conn.close();
} catch (Exception ex) {
throw new java.rmi.RemoteException(
"ejbCreate Error", ex );
}
if ( duplicateKey )
throw new javax.ejb.DuplicateKeyException();
return null;
}
Similarly, in the ejbLoad and ejbStore we load the
bean data from the PropsTable, and save the bean data in PropsTable,
respectively.
public void ejbLoad()
throws java.rmi.RemoteException
{
boolean found = false;
try {
InitialContext
ctx = new InitialContext();
DataSource
ds = (DataSource)
ctx.lookup( "java:comp/env/jdbc/propsDataSource" );
Connection
conn = ds.getConnection();
PreparedStatement
stmt = conn.prepareStatement(
"SELECT \"VALUE\" FROM PropsTable WHERE KEY=?");
stmt.setString(
1, key );
ResultSet
rs = stmt.executeQuery();
if ( rs.next())
{
found = true;
value = rs.getString(1);
}
rs.close();
stmt.close();
conn.close();
} catch (Exception ex) {
throw
new java.rmi.RemoteException( "ejbLoad Error", ex );
}
if ( ! found )
throw
new java.rmi.RemoteException( "Bean not found" );
}
The implementation of ejbStore is similar, but the database access part
is
PreparedStatement stmt = conn.prepareStatement(
"UPDATE PropsTable SET
\"VALUE\"=? WHERE KEY=?");
stmt.setString( 1, value );
stmt.setString( 2, key );
if ( stmt.executeUpdate() > 0 )
found = true;
Similarly, the ejbRemove will delete a particular row from the table.
Finally, the implemention of ejbFindByPrimaryKey returns a
String.
It can just return its argument after checking to make sure it exists in
the database. If it is not in the database, throw a FinderException.
To implement other finders, you can look up in the database and find
all matching key(s). If you expect your finder to always return one
key, declare it as returning the primary key type (String in this
case.) Otherwise, declare it as returning a Collection or
Enumeration,
and return a Collection or Enumeration of the keys you
find.
Make these changes, rebuild your Props EJB and test it. The behavior
should be identical to the CMP version. The only difference is, this
time we are doing all the database acess ourselves instead of letting the
container do it for us.
In general, it is a good idea to use CMP (container-managed-persietence)
when possible. Not only is it easier, the typical professionally
written EJB container would often do a better job of optimized database
access and error checking than most programmers (unless, of course, you
are a database guru!) But in case you ever need to use a persistent
storage that is not a database or if you need a particular database organization,
BMP is a mechanism available in EJBs, short of going to a session bean
and making all JDBC related decisions yourself.
Exercise:
-
Continuing the examples above, add a
"Collection findValueEquals( String cmp );"
finder for the Props EJB that returns a Collection of all beans
whose value field equals the argument passed.
-
Implement your Employee EJB using BMP.
|