Creating a BMP Implementation
It may seem awkward that we started with what might seem like a very hands-on implementation in Chapter 11, followed by the very hands-off implementation that CMP provides, only to end with another relatively hands-on implementation with BMP. But there's a reason. I firmly believe that as the EJB vendor community embraces the EJB 2.0 specification, and in particular its support for a common CMP standard, fewer projects will choose BMP as a solution. It may be necessary sometimes, but lifting the burden of writing and managing SQL calls from the project team eliminates at least 20 to 25 percent of the effort. It also shortens the testing cycle; the ripple effect is exponential.
Let's examine what is necessary to make the Maintain Relationships use-case a BMP implementation. Much of the work is already done for us. Here are the relevant issues:
The work that was done to RemulakServlet, the JSPs, and the UCMaintainRltnshp use-case controller to support the enhanced CMP implementation remains the same for BMP.
The entity beans using CMP require substantial changes, but in structure only, to support BMP. Modified versions of the DAO classes introduced in Chapter 11 will be required to handle the SQL interaction with the database.
The changes boil down to adding calls to the DAO classes within the appropriate callback methods. Let's start by looking at CustomerBean as a BMP entity:
package com.jacksonreed; import java.io.Serializable; import java.util.Enumeration; import java.util.Vector; import java.util.Collection; import javax.ejb.CreateException; import javax.ejb.DuplicateKeyException; import javax.ejb.EJBException; import javax.ejb.EntityBean; import javax.ejb.EntityContext; import javax.ejb.FinderException; import javax.ejb.NoSuchEntityException; import javax.ejb.ObjectNotFoundException; import javax.ejb.RemoveException; import java.rmi.RemoteException; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; import java.util.List; import java.util.Iterator; import java.util.ArrayList; public class CustomerBean implements EntityBean { final static boolean VERBOSE = true; private EntityContext ctx; private Integer customerId; private String customerNumber; private String firstName; private String middleInitial; private String prefix; private String suffix; private String lastName; private String phone1; private String phone2; private String EMail; private ArrayList roleValue; private ArrayList orderValue;
The beginning of the bean looks very much like its Customer Bean counterpart in Chapter 11, with the exception that it implements EntityBean. I won't show all the accessors for the attributes; they appear just as they do in the Chapter 11 version. The big difference with BMP is that the accessors aren't declared abstract public. Next we have the callback operations we are obligated to implement because we're dealing with an entity bean. We will focus on the ejbCreate() method:
public void ejbCreate(CustomerValue custVal) throws CreateException { customerInsert(custVal); }
This method, which the UCMaintainRltnshp control class accesses via the matching create() stub in the CustomerBeanHome interface, invokes a local operation, customerInsert():
public void customerInsert(CustomerValue custVal) throws EJBExeption { try { setCustomerValue(custVal); DataAccess custDAO = new CustomerDAO(); custDAO.insertObject(custVal); custDAO = null; } catch (DAODBUpdateException ae) { throw new EJBException (ae); } catch (DAOSysException se) { throw new EJBException (se); } }
The customerInsert() operation is the same one as in Chapter 11just slimmed down a bit. There is no longer a TransactionContext object being passed in or one being sent out with the creation of the CustomerDAO object, but we are working through the DataAccess interface just as before.
Let's now visit the CustomerDAO class, which has a new constructor:
public CustomerDAO() throws DAOSysException { InitialContext initCtx = null; try { initCtx = new InitialContext(); DataSource ds = (javax.sql.DataSource) initCtx.lookup("java:comp/env/jdbc/remulak-bea"); dbConnection = ds.getConnection(); } catch(NamingException ne) { log("Failed to lookup JDBC Datasource. Please double check that"); log("the JNDI name defined in the resource-description of the "); log("EJB's weblogic-ejb-jar.xml file is the same as the JNDI"); log("name "); log("for the Datasource defined in your config.xml."); throw new DAOSysException(ne); } finally { try { if(initCtx != null) initCtx.close(); } catch(NamingException ne) { log("Error closing context: " + ne); throw new DAOSysException(ne); } } }
In the constructor we attain a reference to our JDBC data source and establish a connection. The lookup is done through JNDI, and this part of the application will also vary depending on the container product. The difference will be more syntax versus functionality. The lookup must happen regardless of the vendor. What follows is the insertObject() operation that the CustomerBean's ejbCreate() operation instigated:
public void insertObject(Object model) throws DAOSysException, DAODBUpdateException { CustomerValue custVal = (CustomerValue) model; PreparedStatement stmt = null; try { String queryStr = "INSERT INTO " + "T_Customer" + " (" + "customerId, " + "customerNumber, " + "firstName, " + "middleInitial, " + "prefix, " + "suffix, " + "lastName, " + "phone1, " + "phone2, " + "eMail) " + "VALUES " + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; stmt = dbConnection.prepareStatement(queryStr); int i = 1; stmt.setInt(i++, custVal.getCustomerId().intValue()); stmt.setString(i++, custVal.getCustomerNumber()); stmt.setString(i++, custVal.getFirstName()); stmt.setString(i++, custVal.getMiddleInitial()); stmt.setString(i++, custVal.getPrefix()); stmt.setString(i++, custVal.getSuffix()); stmt.setString(i++, custVal.getLastName()); stmt.setString(i++, custVal.getPhone1()); stmt.setString(i++, custVal.getPhone2()); stmt.setString(i++, custVal.getEMail()); int resultCount = stmt.executeUpdate(); if ( resultCount != 1 ) throw new DAODBUpdateException ("ERROR inserting Customer in" + " Customer_TABLE!! resultCount = " + resultCount); } catch(SQLException se) { throw new DAOSysException ("Unable to insert item " + custVal.getCustomerId() + " \n" + se); } finally { closeStatement(stmt); closeConnection(); } }
The logic is identical to the CustomerDAO version from Chapter 11. The only minor exception is that the reference to the Transaction Context object is gone and the connection itself is maintained locally in the dbConnection attribute, which was set in the constructor.
That completes our BMP preview. The complete source code is available (as described in preface of this book) to allow a full investigation of the differences. All the other operations in the bean follow the same pattern that we see with the insert.