Creating an Entity Bean

   

To implement BMP, you must code the callback methods within the bean class to perform the required database access. When a client calls a create method on the home interface, the container calls the corresponding ejbCreate on an instance of your bean class. The instance on which the method is called is the instance the container has selected from the pool to associate with the new entity object that's being created. Your ejbCreate method is responsible for initializing the attributes of the entity, inserting it into the database, and returning its primary key value. Remember from Chapter 5 that the signature of an ejbCreate method must have the same parameter list as the corresponding create method in the home interface, and its return type must be the primary key class. Listing 6.3 shows an example of the method you could implement for the createWithData method declared in EnglishAuctionHome . As shown here, the bean class is declared to extend the AbstractEntity class introduced in Chapter 5. This class is an adapter for the EntityBean interface. Because EnglishAuctionBean extends AbstractEntity , it isn't necessary to declare this class to implement EntityBean . However, some development tools won't recognize a class as an enterprise bean if it doesn't explicitly extend the corresponding interface.

Listing 6.3 ejbCreateWithData “A BMP ejbCreate Method Inserts an Entity into the Database
 package com.que.ejb20.auction.model;  ...  public class EnglishAuctionBean extends AbstractEntity implements EntityBean {   ...    public Integer ejbCreateWithData(String name, String description)     throws CreateException {     // throw an application exception if the name isn't valid      if ((name == null)  (name.trim().length() == 0)) {       throw new CreateException("Cannot create an auction without a name");      }      Connection con = null;      PreparedStatement stmt = null;      try {       // assign the primary key and the initialization parameters        setId(computeNextPK("auctionseq"));        setName(name);        setDescription(description);        // default to Pending status        status = IAuctionStatus.AUCTION_PENDING;        con = BMPHelper.getConnection("auctionSource");        // build a prepared statement and insert the new entity into the        // database (let the other attributes default to null)        stmt = con.prepareStatement(       "INSERT INTO auction (id, Name, Description, Status) VALUES (?,?,?,?)");        stmt.setInt(1, id.intValue());        stmt.setString(2, name);        stmt.setString(3, description);        stmt.setString(4, status);        // perform the insert and throw an exception if it fails        int rowsInserted = stmt.executeUpdate();        if (rowsInserted != 1) {         throw new EJBException(           "Could not insert the auction into the database");        }        // everything worked, return the primary key        return getId();      }      catch (SQLException e) {       // throw a system exception if a database access error occurs        throw new EJBException;      }      finally {       // close the connection        BMPHelper.cleanup(stmt, con);      }    }    ...  } 

The ejbCreateWithData method starts by performing any data validation that's required and then assigning its parameter values to the bean instance. You should report errors related to initialization data using a CreateException . You'll see more about exceptions such as CreateException in Chapter 13, "Exception Handling."

The getConnection method of BMPHelper from Listing 6.1 provides a Connection object to ejbCreateWithData that's used to build an insert statement. After the insert statement is executed, the method returns the primary key assigned to the entity object. The cleanup method of BMPHelper called from the finally block is a simple utility method used to encapsulate the checks and exception handling needed by all the callback methods to close a statement and database connection before returning. This method appears in Listing 6.4. Because the application server is managing the pooling of connections for you, you're not really closing the connection by calling the close method. Calling this method lets the container know that you're finished with the connection so that it can be returned to the pool. It's important to release a limited resource such as this, so the call to the close method (or the cleanup method in this example) should always be placed in a finally block.

Listing 6.4 cleanup “Common Behavior for Closing a Statement and a Connection
 package com.que.ejb20.common.ejb;  ...  public class BMPHelper {   ...    public static void cleanup(Statement stmt, Connection con) {     try {       if (stmt != null) {         stmt.close();        }        if (con != null) {         con.close();        }      }      catch (SQLException e) {       throw new EJBException;      }    }    ...  } 

Managing Connection References

All code examples in this chapter use the approach shown in Listing 6.3 for working with Connection objects. When a method that needs to access the database is called, the method obtains a connection from the pool, performs its work, and then releases the connection back to the pool. This is a commonly used approach, but it's not your only option. One advantage of obtaining and releasing the connection within each method is that it's simple to implement. It does require some repetitive code as part of each method call, but you can take care of that using methods like the getConnection and cleanup examples used here. Besides its simplicity, this approach also has the advantage of only tying up a connection while it's being actively used.

Another option you have is based on keeping a connection for a longer period of time. Previously in Chapter 5, you saw how the container manages the life cycle of an entity bean instance. Part of this management consists of invoking callback methods on an entity as life cycle events occur. This includes calling setEntityContext after an instance is first created and calling unsetEntityContext immediately before it's destroyed . Instead of obtaining a connection each time you need one, you could instead obtain one in setEntityContext and release it in unsetEntityContext . The obvious advantage here is that you can avoid the overhead of obtaining a connection on each call. The drawback is that you're holding a connection from the pool even when it's not being used.

The main point to be made here is that there's more than one way to deal with connections when you're using BMP. The approach shown in the examples in this chapter is adequate for what you'll typically need. If you're wondering about the performance penalty of obtaining a connection within each method, remember that these method calls don't actually require a connection to be opened and closed. Because of the automatic pooling provided to you, connections are simply pulled from the connection pool and then returned when you're finished with them. However, you might improve performance when an application's clients access only a relatively small number of entity objects by holding onto the connection during the lifetime of each instance. This advantage diminishes when the number of entities being accessed increases . Here, the number of connections required from the pool goes up even if they are not all being used simultaneously . In this situation, you're better off releasing connections and reacquiring them when you need them.

Notice that ejbCreateWithData calls a computeNextPK method to assign its primary key value. Each table in the auction database uses an integer primary key. Using a unique number or string that has nothing to do with the data for a primary key value is a common approach. Just as common is the accompanying problem of deciding how to generate these unique values when rows are inserted. Most databases provide the capability to automatically generate a unique sequence number when a row is inserted. Using these native sequence numbers removes the problem of key generation but it can lead to portability problems. Even though most databases support this feature, not all of them do, and the implementations differ in how an application retrieves a newly generated key from the database.

Another approach is to use a routine that merges information, such as the IP address or some other unique property of the server, with the current time and date to produce a unique string. This approach is reliable but it has poor performance compared to approaches that don't depend on string manipulation. A fairly simple alternative to the two mentioned so far is to implement your own sequence table in the database for each table that requires an integer primary key. A sequence table needs to hold only a single value to represent the last primary key value assigned to the table with which it's associated. This approach was selected for the auction example. Listing 6.5 shows how you can generate sequence numbers using a simple sequence table.

Listing 6.5 computeNextPK “A Method for Generating a Primary Key Value Using a Sequence Table
 package com.que.ejb20.auction.model;  ...  public class EnglishAuctionBean extends AbstractEntity implements EntityBean {   ...    protected Integer computeNextPK(String tableName) throws SQLException {     Connection con = null;      PreparedStatement stmt = null;      ResultSet rs = null;      try {       con = BMPHelper.getConnection("auctionSource");        // update the sequence value in the database        stmt = con.prepareStatement("UPDATE " + tableName +          " set next_id = next_id + 1");        if (stmt.executeUpdate() != 1) {         throw new SQLException("Error generating primary key");        }        stmt.close();        // retrieve the sequence value and use it as the primary key        stmt = con.prepareStatement("SELECT next_id from " + tableName);        rs = stmt.executeQuery();        boolean found = rs.next();        if (found) {         return new Integer(rs.getInt("next_id"));        }        else {         throw new SQLException("Error generating primary key");        }      }      finally {       // close the connection        BMPHelper.cleanup(stmt, con);      }    }    ...  } 

The sequence tables accessed by computeNextPK can be created using the following SQL:

 CREATE TABLE auctionseq (   next_id int NOT NULL  );  insert into auctionseq (next_id) values (1);  CREATE TABLE bidseq (   next_id int NOT NULL  );  insert into bidseq (next_id) values (1); 

Besides the source shown so far, ejbCreateWithData also references the IAuctionStatus interface. This interface, which appears in Listing 6.6, simply defines the strings used in the database to represent the auction state.

Listing 6.6 IAuctionStatus.java “A Declaration of the Strings Used to Report Auction State
 package com.que.ejb20.auction.model;  /**   * Title:        IAuctionStatus<p>   * Description:  Constants that define the allowed auction states<p>   */  public interface IAuctionStatus {   public static final String AUCTION_PENDING   = "Pending";    public static final String AUCTION_OPEN      = "Open";    public static final String AUCTION_CANCELLED = "Cancelled";    public static final String AUCTION_CLOSED    = "Closed";  } 

Each ejbCreate method must have a matching ejbPostCreate method that is declared with the same parameter list but with a return type of void . The container calls this method after the ejbCreate method completes. Unlike ejbCreate , your instance is associated with an entity object identity within an ejbPostCreate method. This means that you can access the methods of the instance's EntityContext to get the primary key or the EJBObject . What this offers you is the capability to define associations between the entity and other objects. If, as part of creating an entity object, you need to insert a row into another table that has a foreign key back to your entity, ejbPostCreate is the place to do it. In the case of the auction entity, its associations to its bids and the item it offers aren't established at creation, so there's nothing to do at this point. EnglishAuctionBean only needs a do-nothing implementation of this method as shown in the following:

 public void ejbPostCreateWithData(String name, String description)   throws CreateException {   // nothing to do  } 


Special Edition Using Enterprise JavaBeans 2.0
Special Edition Using Enterprise JavaBeans 2.0
ISBN: 0789725673
EAN: 2147483647
Year: 2000
Pages: 223

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net