Tutorials
EnterPrise Java Beans
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:

  1. Continuing the examples above, add a
    1. "Collection findValueEquals( String cmp );"
    finder for the Props EJB that returns a Collection of all beans whose value field equals the argument passed.
  2. Implement your Employee EJB using BMP. 
 
Next tutorial: Security in EJBs