The Data Translation Services and Data Access Services Layers

Recall that Chapter 8 discussed the six layers of an application, which are shown again in Figure 9-17. Also recall from Chapter 8 that for Remulak Productions the Data Translation Services layer will be implemented both for native JDBC calls made from JavaBeans and BMP entity beans and for CMP support within the EJB application server. We need to explore the architecture issues with these two approaches, so let's begin with the JavaBean/native JDBC approach.

Figure 9-17. Six-logical-tier model


JavaBeans with Native JDBC Support

Prior to the advent of container-managed persistence (CMP) in Enterprise JavaBeans, the only way to manage data access, with the exception of add-on persistence managers like TopLink for BEA/WebGain, was to craft them from scratch. This is still the option chosen today by many development groups. Until we see widespread support of the EJB 2.0 specification and CMP by application server vendors, it will be the favored choice. As we'll see, however, designing and building this solution represents a tremendous amount of work on the part of the development team.

JavaBeans and Native JDBC Data Management

JDBC (Java Database Connectivity) is still the technology that underlies our data translation and data access strategies. In our initial solution, we must craft it all ourselves. To do that we need an architecture that represents a repeatable approach. When this type of solution is necessary, it is best to consider interfaces first. If your design appears to be potentially repeatable across many classes, an interface is the best approach. First we must consider what the data access layer must support. Every bean (e.g., Customer and Order) needs the ability both to retrieve itself from persistent storage and to persist itself back to the same storage device.

The Remulak design approach will be to create what are called data access objects (DAOs). DAOs have been popular for many years. In my Visual Basic book, I had a similar construct called data translation classes. In Nicholas Kassem's popular book Designing Enterprise Applications with the Java 2 Platform (Addison-Wesley, 2000), the sample pet store application implements the concept of DAOs. The same DAOs will also be used for bean-managed persistence (BMP) in the Enterprise JavaBeans environment.

DAOs are meant to satisfy the data access requirements of our entity classes. Our design certainly could have implemented all the data access logic with embedded SQL statements in the entity classes themselves, but then we would have broken our motto of keeping the layers separate. Toward that end, our design will implement a DAO class for every entity class in the system. These classes will all follow the naming pattern of classnameDAO.

Next we must consider the functionality that these DAO classes should implement. This is where the idea of an interface would play quite nicely into a flexible design. These classes need to be able to retrieve information by their primary key. Because we have the luxury of every class being identified with the same surrogate key strategy, an Integer, this behavior can be common across all DAOs. These classes must also be able to insert, delete, and update themselves.

Finally, DAOs should be able to retrieve themselves by their natural identifiers if necessary. We have also ensured that this can be common because all top-level classes (e.g., Customer, Order) have a String by which they can be identified (e.g., “customer number,” “order number”). This leads us to a potential set of operations that are common:

  • insertObject(): This operation inserts records into the respective table. It requires an image of the object as input and returns nothing.

  • updateObject(): This operation updates records of the respective table. It requires an image of the object as input and returns nothing.

  • deleteObject(): This operation deletes records from the respective table. It requires the primary key identifier for the object as input and returns nothing.

  • findByPrimaryKey(): This operation retrieves the persistent state of an object. It requires the primary key identifier as input and returns an image of the object retrieved.

  • findByName(): This operation retrieves the persistent state of an object. It requires the natural String key of the object as input and returns an image of the object retrieved.

What follows here is the basis of the common interface that the Remulak project team calls DataAccess. Each DAO class will implement this interface.

package com.jacksonreed;
public interface DataAccess {
    public void insertObject(Object model) throws
                     DAOAppException, DAODBUpdateException,
                     DAOSysException;
    public void updateObject(Object model) throws
                     DAOAppException, DAODBUpdateException,
                     DAOSysException;
    public void deleteObject(Integer id) throws DAOSysException,
                     DAODBUpdateException;
    public Object findByPrimaryKey(Integer id) throws
    DAOSysException,
                     DAOFinderException;
    public Object findByName(String name) throws
                     DAOSysException, DAOFinderException;
}

In the interface description earlier, we stated that an object image is returned in the case of finders, and passed in in the case of insert and update operations. To make the interface generic, we must use the Object reference. It will then be the responsibility of the DAO class when receiving the object to cast it to the proper class. For instance, in the case of the CustomerDAO class, something like the following is necessary:

CustomerValue custVal = (CustomerValue) model;

A class that is receiving something back from a DAO, such as CustomerBean, would need a similar cast in its finder operation:

DataAccess custDAO = new CustomerDAO(transactionContext);
CustomerValue custVal = (CustomerValue)
          custDAO.findByName(customerNumber);

These object images being sent back and forth are also following a common design technique used to minimize network traffic. If the Java components accessing the entity bean components were on different machines, each get and set call to each attribute would be an expensive trip across the network. One way to reduce this overhead is to create an image of the object's state and pass it back and forth. With this approach, only one element is passed.

A class that does this contains nothing but attributes and get and set operations for these attributes. The format for naming such classes will be classnameValue. Here's the class definition for CustomerValue:

package com.jacksonreed;

import java.util.ArrayList;

public class CustomerValue implements java.io.Serializable {

   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;

   public CustomerValue() {
      roleValue = new ArrayList();
      orderValue = new ArrayList();
   }

   public Integer getCustomerId() {
      return customerId;
   }

   public void setCustomerId(Integer val) {
      customerId = val;
   }
   public String getCustomerNumber() {
      return customerNumber;
   }
   public void setCustomerNumber(String val) {
      customerNumber = val;
   }
   public String getFirstName(){
      return firstName;
   }
   public void setFirstName(String val){
      firstName = val;
   }
   public String getMiddleInitial(){
      return middleInitial;
   }
   public void setMiddleInitial(String val){
      middleInitial = val;
   }
   public String getPrefix(){
      return prefix;
   }
   public void setPrefix(String val){
      prefix = val;
   }
   public String getSuffix(){
      return suffix;
   }
   public void setSuffix(String val){
      suffix = val;
   }
   public String getLastName(){
      return lastName;
   }
   public void setLastName(String val){
      lastName = val;
   }
   public String getPhone1(){
      return phone1;
   }
   public void setPhone1(String val){
      phone1 = val;
   }
   public String getPhone2(){
      return phone2;
   }
   public void setPhone2(String val){
      phone2 = val;
   }
   public String getEMail(){
      return EMail;
   }
   public void setEMail(String val){
      EMail = val;
   }
   public ArrayList getRoleValue(){
      return roleValue;
   }
   public void setRoleValue(ArrayList val){
      roleValue = val;
   }
   public ArrayList getOrderValue(){
      return orderValue;
   }
   public void setOrderValue(ArrayList val){
      orderValue = val;
   }
}

So just what does a DAO class that implements the DataAccess interface look like? Let's look at a few snippets of CustomerDAO and in particular the operations that realize the interface. Here we show only the class definition and the updateObject() operation:

package com.jacksonreed;

import java.io.*;
import java.sql.*;
import java.text.*;
import java.util.*;
import javax.sql.*;

public class CustomerDAO implements Serializable, DataAccess {
    private transient TransactionContext globalTran = null;
    private transient CustomerValue custVal  = null;

    public CustomerDAO(TransactionContext transactionContext) {
          globalTran = transactionContext;
    }
    public void updateObject(Object model) throws
                    DAOSysException, DAODBUpdateException {

        CustomerValue custVal = (CustomerValue) model;
        PreparedStatement stmt = null;
        try {
            String queryStr = "UPDATE " + "T_Customer" +
              " SET " + "customerNumber = ?, " +
              "firstName = ?, " +
              "middleInitial = ?, " +
              "prefix = ?, " +
              "suffix = ?, " +
              "lastName = ?, " +
              "phone1 = ?, " +
              "phone2 = ?, " +
              "eMail = ? " +
              "WHERE customerId = ?";

            stmt =

globalTran.getDBConnection().prepareStatement(queryStr);

            int i = 1;
            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());
            stmt.setInt(i++, custVal.getCustomerId().intValue());

            int resultCount = stmt.executeUpdate();
            if ( resultCount != 1 ) {
                 throw new DAODBUpdateException ("ERROR updating
                           Customer in"
          + " Customer_TABLE!! resultCount = " + resultCount);
            }
        } catch(SQLException se) {
            throw new DAOSysException("Unable to update item " +
                  custVal.getCustomerId() + "  n" + se);
        } finally {
            globalTran.closeStatement(stmt);
            log("CustomerDAO: closeStatement");
        }
    }
}

Here we see that native JDBC calls are being made to execute the SQL statement stmt.executeUpdate(). Notice also in the signature of the constructor for CustomerDAO that there is an object of type TransactionContext being passed in from the caller, CustomerBean. This object allows coordination of the many database activities and is the unifying element that allows unit-of-work transaction management. Chapter 11 will say more about transaction management and Transac tionContext. Figure 9-18 is a component diagram of the data access architecture for our JavaBean/DAO strategy.

Figure 9-18. JavaBean/DAO strategy


Figure 9-19 is a class diagram representing the interface and its relationship to the DAO classes. Again, this would apply to all entity beans for Remulak; we are showing only those that relate to the Maintain Relationships use-case.

Figure 9-19. UML class diagram depicting the DataAccess interface


Remember how to interpret this class diagram? DataAccess is an interface and it is realized, or implemented, by the three DAO classes. The three JavaBean classes all depend on this interface.

Enterprise JavaBeans and Data Management

EJB offers two varieties of data management: bean-managed persistence (BMP) and container-managed persistence (CMP). All Enterprise JavaBeans implement a callback model to allow the container product to manage the state of the bean. This is necessary because the container in which they run must be able to communicate with the bean at various times within the bean's lifecycle. Although we will learn more about these callback mechanisms later, it is the responsibility of the container to invoke the necessary operations—for example, ejbCreate()—on the basis of how clients of the bean interact with it.

In the case of BMP, you still must write SQL code. However, the good news is that we are able to use the same DAO strategy and modules with very few changes over what was built to work for the non-EJB environment. So with the exception of ensuring that we place the proper calls to the DAO objects in the right callback operations implemented in our EJBs, the EJB strategy is very similar in functionality to what we have already built. Figure 9-18 would apply for BMP as well; the only difference would be that instead of plain JavaBeans interfacing with the DAO classes, they would be entity EJBs implementing bean-managed persistence. We will see more of BMP using our DAO objects when we build the EJB solution in Chapter 12.

Things are really different with the next data management strategy: container-managed persistence (CMP). Because the folks at Sun needed to greatly improve the consistency of their CMP message with EJB 1.1, they decided to totally revamp how CMP works with EJB 2.0. EJB 2.0 is not backward compatible with EJB 1.1. Although the two can still coexist, once you have experienced CMP under EJB 2.0, you won't even think about going back to EJB 1.1 CMP. I will even go a step further and say that once you experience CMP, it will be very difficult to go back to BMP.

My review of CMP will be cursory here, although in later chapters we will see quite a bit of application code using it. I would highly recommend that you read a book devoted to EJB application architectures, such as Enterprise JavaBeans by Richard Monson-Haefel (published by O'Reilly, 2000). For the inquisitive mind, however, the brief review I present here will, I think, provide a good overview as it relates to Remulak.

First, it is important to understand that with CMP, you write absolutely no SQL code. This will take some getting used to. The closest thing to SQL you will write is something called EJB-QL, and only then if you have specific select requests of your entity beans (e.g., “Show me all orders over a certain dollar amount”). For simple CRUD activities, CMP handles it all. Of particular importance, it handles the relationships between beans such as one to one, one to many, and many to many. There is no fiddling with foreign keys once the application server is aware of the relationships. The simple act of an entity bean, such as Order, storing OrderLine objects in its own ArrayList object will result in rows being inserted into both the T_ORDER and T_ORDERLINE tables with all the appropriate foreign key information being set.

Figure 9-20 is an overview of an EJB container (in our case BEA's WebLogic) and some of its components. A key link between your Java code and the resulting RDBMS activity is formed by the deployment descriptors. The descriptors describe what Sun calls an abstract persistence schema. This schema maps physical columns in the tables to operations and relationships in your code. Other EJB vendors will have more or less the same descriptors, but what goes into the descriptors as pertains to the RDMBS components is specified by the EJB 2.0 specification, not the EJB vendor.

Figure 9-20. Deployment descriptors and bean relationships


Schemas are just XML that describes the data aspects of your beans. The following snippet of the ejb-jar.xml file for Remulak describes the persistent view of the OrderBean object (for space reasons I have removed the other objects). The descriptor also takes into account the relationships they may have with other beans, as well as transaction requirements. This descriptor is the pivotal component that ties your beans to the back-end persistence mechanisms.

<ejb-jar>
   <enterprise-beans>
      <entity>
         <ejb-name>OrderBean</ejb-name>
         <home>com.jacksonreed.OrderHome</home>
         <remote>com.jacksonreed.Order</remote>
         <ejb-class>com.jacksonreed.OrderBean</ejb-class>
         <persistence-type>Container</persistence-type>
         <prim-key-class>java.lang.Integer</prim-key-class>
         <reentrant>False</reentrant>
         <cmp-version>2.x</cmp-version>
         <abstract-schema-name>OrderBean</abstract-schema-name>
         <cmp-field>
            <field-name>orderId</field-name>
         </cmp-field>
         <cmp-field>
            <field-name>orderNumber</field-name>
         </cmp-field>
         <cmp-field>
            <field-name>orderDateTime</field-name>
         </cmp-field>
         <cmp-field>
            <field-name>terms</field-name>
         </cmp-field>
         <cmp-field>
            <field-name>salesPerson</field-name>
         </cmp-field>
         <cmp-field>
            <field-name>discount</field-name>
         </cmp-field>
         <cmp-field>
            <field-name>courtesyMessage</field-name>
         </cmp-field>
         <primkey-field>orderId</primkey-field>
         <query>
            <query-method>
               <method-name>findAllOrders</method-name>
               <method-params/>
            </query-method>
            <ejb-ql><![CDATA[ WHERE orderId IS NOT NULL]]> </ejb-ql>
         </query>
         <query>
            <query-method>
               <method-name>findByOrderNumber</method-name>
               <method-params>
                  <method-param>java.lang.String</method-param>
               </method-params>
            </query-method>
            <ejb-ql><![CDATA[ WHERE orderNumber = ?1]]>
            </ejb-ql>
         </query>
      </entity>
      <ejb-relation>
         <ejb-relation-name>Order-OrderLine</ejb-relation-name>
         <ejb-relationship-role>
            <ejb-relationship-role-name>OrderLineBean-Has-
            OrderBean</ejb-relationship-role-name>
            <multiplicity>many</multiplicity>
            <role-source>
               <ejb-name>OrderLineBean</ejb-name>
            </role-source>
         </ejb-relationship-role>
         <ejb-relationship-role>
            <ejb-relationship-role-name>OrderBean-Has-
            OrderLineBean</ejb-relationship-role-name>
            <multiplicity>one</multiplicity>
            <role-source>
               <ejb-name>OrderBean</ejb-name>
            </role-source>
            <cmr-field>
                  <cmr-field-name>orderLines</cmr-field-name>
                  <cmr-field-type>java.util.Collection</cmr-
                  field-type>
               </cmr-field>
            </ejb-relationship-role>
         </ejb-relation>
         <ejb-relation>
            <ejb-relation-name>Customer-Order</ejb-relation-name>
            <ejb-relationship-role>
               <ejb-relationship-role-name>OrderBean-Has-
               CustomerBean</ejb-relationship-role-name>
               <multiplicity>many</multiplicity>
               <role-source>
                  <ejb-name>OrderBean</ejb-name>
               </role-source>
               <cmr-field>
                  <cmr-field-name>customer</cmr-field-name>
               </cmr-field>
            </ejb-relationship-role>
            <ejb-relationship-role>
               <ejb-relationship-role-name>CustomerBean-Has-
               OrderBean</ejb-relationship-role-name>
               <multiplicity>one</multiplicity>
               <role-source>
                  <ejb-name>CustomerBean</ejb-name>
               </role-source>
               <cmr-field>
                  <cmr-field-name>orders</cmr-field-name>
                  <cmr-field-type>java.util.Collection</cmr-field-
                  type>
               </cmr-field>
            </ejb-relationship-role>
         </ejb-relation>
         <ejb-relation>
            <ejb-relation-name>Product-OrderLine</ejb-relation-
            name>
            <ejb-relationship-role>
               <ejb-relationship-role-name>OrderLineBean-Has-
               ProductBean</ejb-relationship-role-name>
               <multiplicity>many</multiplicity>
               <role-source>
                  <ejb-name>OrderLineBean</ejb-name>
               </role-source>
               <cmr-field>
                  <cmr-field-name>product</cmr-field-name>
               </cmr-field>
         </ejb-relationship-role>
         <ejb-relationship-role>
            <ejb-relationship-role-name>ProductBean-Has-
            OrderLineBean</ejb-relationship-role-name>
            <multiplicity>one</multiplicity>
            <role-source>
               <ejb-name>ProductBean</ejb-name>
            </role-source>
            <cmr-field>
               <cmr-field-name>orderLines</cmr-field-name>
               <cmr-field-type>java.util.Collection</cmr-
               field-type>
            </cmr-field>
         </ejb-relationship-role>
      </ejb-relation>
   </enterprise-beans>
   <assembly-descriptor>
      <container-transaction>
         <method>
            <ejb-name>OrderBean</ejb-name>
            <method-intf>Remote</method-intf>
            <method-name>*</method-name>
         </method>
         <trans-attribute>Required</trans-attribute>
      </container-transaction>
   </assembly-descriptor>
</ejb-jar>

Notice that the tags define the table to which the entity bean is persisted, as well as relationships that this bean has with other entity beans. Multiplicity is also specified with the <multiplicity> tag, as well as the Java variable and its type for storing the relationships in the <cmr-field> and subordinate tags. The other two descriptors contain information such as the data source to use when accessing the application's data.

Regardless of the EJB vendor, today with the EJB 2.0 specification it is much more feasible to transport CMP beans between vendor containers. The only changes would be to replace the descriptors with the new target vendor's descriptors and redeploy the application.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.189.178.53