HOWTO: Implementing Collections of Value Objects for MVC Apps with BC4J

HOWTO: Implementing Collections of Value Objects for MVC Apps with BC4J

An Oracle JDeveloper How To Document
November 2002

Product Versions

This document was written for Oracle9i JDeveloper versions 9.0.2 and 9.0.3.

Contents

        Overview
        Creating a MVC Model Layer Using BC4J
        Writing a Sample View Layer Client
        Conclusion

NOTE:

This is one of many BC4J-related HowTo articles that you can find at /products/jdev/howtos/index.html


Overview

J2EE developers talk a lot about Value Objects. Value Objects, also known as Data Transfer Objects, are a J2EE Design Pattern for grouping a set of related attributes together to shuttle data between your "model" layer and "view" layer in a Model-View-Controller application architecture. While sometimes used alone, typically developers work with collections or lists of Value Objects, for example so that a JSP page in the View layer can present a list of employee information retrieved by the Model layer.

When developers code value objects by hand, they typically model them as a simple JavaBean like the following  EmpValueObject class:

package test.common;
import java.io.Serializable;
// Typical Value Object Implementation as a JavaBean
public class EmpValueObject implements Serializable {
  String  _name;
  Float   _salary;
  Integer _id;
  public String getName()           { return _name;    }
  public Float getSalary()          { return _salary;  }
  public Integer getId()            { return _id;      }
  public void setName(String value) { _name   = value; }
  public void setSalary(Float value){ _salary = value; }
  public void setId(Integer value)  { _id     = value; }
  public EmpValueObject(){}
}

After creating the value objects, in most J2EE projects using the MVC architecture, a developer will create a service interface. The service interface isolates the View layer from the persistent business objects in the Model, and provides a central point of access to the various collections of value objects needed by the View layer. For a simple application working with employees, this service interface might look like this:

package test.common;
// Service interface to expose collections of value objects to the View layer
public interface EmpModel {
  // Developers frequently use Vector, List, or Collection to return a set of Value Objects
  java.util.Vector     vectorOfEmpsInDept(int deptno);
  java.util.Collection collectionOfEmpsInDept(int deptno);
  java.util.List       listOfEmpsInDept(int deptno);
}

Then some class provides an implementation of this  EmpModel interface for the View layer tier to use. With this service interface in place, the usage pattern in the View layer code usually goes like this:

  1. Use a factory to obtain an instance of the  EmpModel interface
  2. Call a method on the  EmpModel interface to return an appropriate  Collection,  List, or  Vector of value objects
  3. Create an  Iterator to iterate the value object instances

The routine coding pattern for this might look like the following code snippet:

//-------------------------------------------------------------
// Routine code in the "View" layer to access a collection/list
// of business information from the "Model" layer
//-------------------------------------------------------------
EmpModel empModel = EmpModelFactory.createEmpModel();
  :
List list = empModel.listOfEmpsInDept(10);
Iterator it = list.iterator();
while (it.hasNext()) {
  // Get the next instance of our value object from the list
  EmpValueObject empValObj = (EmpValueObject)it.next();
  // Use getter methods on the value object to access the data
  System.out.println(empValObj.getName()+","+empValObj.getId()+","+empValObj.getSalary());      
}

The implementation of the method  listOfEmpsInDept(int deptno) usually contains code that interacts with an underlying Data Access Object (another J2EE Design Pattern) that encapsulates the JDBC code to actually retrieve the employee data from the database.

So if we take a quick inventory of the objects that the typical J2EE developer adopting a Model-View-Controller architecture must create to put some business information on a web page, we have the following:

  1. A "model" class that provides methods to expose collections of value objects to the View layer ( EmpModel)
  2. A factory object that returns an implementation of the model class ( EmpModelFactory)
  3. A value object class that holds the interesting attributes needed by the view layer ( EmpValueObject, holding  Name,  Id, and  Salary)
  4. A Data Access Object class that encapsulates the JDBC code to query the employee name, id, and salary from the underlying database, and
  5. If the user interface needs to present a longer list of business information a "page at a time", then in addition a "value list handler" class is usually required to manage these details.

Business Components for Java provides a ready-made implementation of all of the above ingredients to make quick work of the task without having to hand code any of these participant classes yourself. The next section explains how.

Creating a MVC Model Layer Using BC4J

Here are the step by step instructions on how to build this typical Model layer using BC4J. Once the model layer is built, we'll see in the following section how to use this model layer from your MVC view layer client.

  1. Startup Oracle9i JDeveloper and create a new empty project in a new workspace
  2. Select New Business Components Package... from the right-mouse menu on your project.
  3. Provide a name for the package that your business components will live in, let's say  test, and set the connection name you want to use, then click (Finish).

    The connection name should map to the SCOTT schema, containing the EMP table.

  4. We'll use a view object component to handle the value objects list of employee information we need.

    Select the  test package in the System Navigator and choose New View Object... from its right-mouse menu.

  5. Call the new view object  EmpsInDepartment, and click (Next>) until you get to "Step 5 of 6: Query" page.

    • Type in the following query in the "Query Statement" box:

      select empno as "Id",
             ename as "Name",
             sal   as "Salary"
      from emp
      where deptno = ?
    • Make sure you check the "Use ?-Style Parameters" checkbox. Then click (Next>)
    • On the "Step 6 of 6: Java" panel of the wizard, check the "Generate Java File" checkbox under "View Row Class", and leave the "Generate Accessors" checkbox checked. Then click (Finish)
  6. Select the  EmpsInDepartment view object in the System Navigator and choose Edit EditEmpsInDepartment... from the right-mouse menu.

    • Click on the "Attribute Settings" tab. This shows you the attributes that will be included in the value object's in the list produced by executing this view object's query.

      Select the  Id attribute in the poplist. Set its datatype to  Integer. Select the  Salary attribute in the poplist, and set its datatype to  Float.

      Of course, the default datatype for numbers ( oracle.jbo.domain.Number) will work just fine as well, but here we illustrate that we have fine control over the datatype of each attribute the value object.

    • We can also control which accessor method (or other custom methods) will appear on the value object the client will see.

      Click on the "Client Row Methods" tab and click the (>>) button to shuttle all of the accessor methods into the selected list.

      Then click (Finish).

  7. We'll use an application module component to implement the service interface for our value object collections needed by the view layer.

    Select the  test package in the System Navigator and choose  New Application Module... from its right-mouse menu.

  8. Call the new application module  EmpModel, and click (Next>) from the next page. On the "Data Model" panel of the wizard, select the  EmpsInDepartment view object and click the (>) arrow button to shuttle it into the selected list. Then click (Finish>)

With these steps, we have created:

Class/InterfaceDescription and Patterns That It Implements
View Object  EmpsInDepartment

Handles JDBC data access, and manages our lists of value objects based on employee information (Patterns: Value List Handler, Page-by-Page Iterator, Fast-Lane Reader, Data Access Object)

Value Object  EmpsInDepartmentRow

Holds the Name, Id, and Salary attribute of an employee, and provides accessor methods. (Pattern: Value Object)

Application Module  EmpModel

Exposes collections of value objects to the "View" layer. (Pattern: Service Interface for View Layer)

Later we'll see that BC4J provides a factory to easily get an instance of our  EmpModel and work with it. That factory is generic to all application module classes, so it doesn't need to be specifically generated for the  EmpModel.

First let's write some custom code on our  EmpsInDepartment view object to encapsulate the setting of the bind variable.

Expand the icon in the system navigator for the  EmpsInDepartment and double-click on the  EmpsInDepartmentImpl.java class to see it in the code editor. Even though this is generated code, the view object "Impl" class is designed to have your custom code added right to it. Your custom code does not get "blown away" when you revisit the re-entrant view object wizard later to change declarative settings of the component.

We add the following method to the  EmpsInDepartmentImpl.java file:

// Encapsulate the settings of this View Object's Where Clause
public void setDepartmentNumber(int deptno) {
  setWhereClauseParam(0,new Integer(deptno));
}

Then, we can expose the view object method to clients by visiting the View Object editor, clicking on the "Client Methods" tab, shuttling this new  setDepartmentNumber method into the selected list, and pressing (Finish).

Next let's write some custom code in our  EmpModel application module.

Expand the icon in the system navigator for the  EmpModel and double-click on the  EmpModelImpl.java class to see it in the code editor.

First we add a private method to return a  RowSet of employees based on a  deptno department number passed in. We'll reuse this method in several of the example methods we're about to write:

/**
* Return the RowSet of Emps for department id passed in
*/
private RowSet rowsetForEmpsInDepartment(int deptno) {
  EmpsInDepartment eid = getEmpsInDepartment();
  // Use custom method on the VO to encapsulate WHERE Clause details
  eid.setDepartmentNumber(10);
  eid.executeQuery();
  // ViewObject interface extends RowSet so we can just return this VO as RS
  return eid;
}

As we hinted at above, BC4J provides an  EmpsInDepartmentRow value object for our use, along with a remoteable collection implementation called  RowSet. However, if the developer is used to using the standard J2SE  Vector,  Collection, or  List interfaces, we'll illustrate how to use those as well.

The BC4J-supplied  RowSet implementation works seamlessly with the BC4J-generated value objects, so we can write a simple method like the following to return the  RowSet of  EmpsInDepartmentRow value objects to the view layer:

/**
* Return a RowSet of EmpsInDepartmentRow instances
*/
public RowSet rowsetOfEmpsInDept(int deptno) {
  return rowsetForEmpsInDepartment(deptno);
}

When using  Vector,  Collection, or  List, you need to use a hand-written JavaBean as your value object like the  EmpValueObject we showed at the beginning of this article. The code to return a Vector, List, and Collection of  EmpValueObject instances looks like this:

/**
* Return a Vector of EmpValueObject instances
*/
public Vector vectorOfEmpsInDept(int deptno) {
  Vector v = new Vector();
  RowSet rs = rowsetForEmpsInDepartment(deptno);
  while (rs.hasNext()) {
    // Instantiate the hand-written value object and add to vector
    v.add(empValObjectForEmpRow((EmpsInDepartmentRow)rs.next()));
  }
  return v;
}

/**
* Return a list of EmpValueObject instances
*/
public List listOfEmpsInDept(int deptno) {
  ArrayList al = new ArrayList();
  RowSet rs = rowsetForEmpsInDepartment(deptno);
  while (rs.hasNext()) {
    // Instantiate the hand-written value object and add to list
    al.add(empValObjectForEmpRow((EmpsInDepartmentRow)rs.next()));
  }
  return al;
}

/**
* Return a collection of EmpValueObject instances
*/
public Collection collectionOfEmpsInDept(int deptno) {
  // The java.util.List interface extends java.util.Collection interface
  return listOfEmpsInDept(deptno);
}

So, it takes a little more work and an extra hand-written value object to do what BC4J provides for free, but it's worth implementing all the permutations anyway for practice. Notice that in the Vector, List, and Collection cases, we're using a helper method named  empValObjectForEmpRow which does the job of copying the data from the  EmpsInDepartmentRow value object that BC4J provides into the hand-written value object that can be serialized as part of

/**
* Create an EmpValueObject instance from an EmpsInDepartmentRow
*/
private EmpValueObject empValObjectForEmpRow(EmpsInDepartmentRow empRow) {
  EmpValueObject empValObj = new EmpValueObject();
  empValObj.setName(empRow.getName());
  empValObj.setSalary(empRow.getSalary());
  empValObj.setId(empRow.getId());
  return empValObj;
}

Now that we've written our four public methods on our  EmpModel implementation class, we can expose these methods to clients by visiting the Application Module editor for EmpModel and clicking on the Client Methods tab. By holding down the [Ctrl] key while clicking, we can multi-select the four method names in the list on the left:

  •  rowsetOfEmpsInDept
  •  vectorOfEmpsInDept
  •  listOfEmpsInDept
  •  collectionOfEmpsInDept

and then click on the (>) button to shuttle these methods into the Selected list. When we click (Finish), the BC4J design time facilities will automatically create a new  EmpModel interface for us, containing these four methods.

So, we now have three interfaces in our project:

InterfaceDescription
View Object  EmpsInDepartment

Exposing our custom  setDepartmentNumber method to clients...

Value Object  EmpsInDepartmentRow

Exposing our typesafe getter/setter methods for our employee data values to clients...

Application Module  EmpModel

Exposing the four custom methods in our service interface for our view layer:  rowsetOfEmpsInDept,  vectorOfEmpsInDept,  listOfEmpsInDept,  collectionOfEmpsInDept...

While we created our BC4J components in the  test package, BC4J automatically creates these interfaces in the  test.common package for us, emphasizing the fact that they are common interfaces that can be used on both the client tier and the business tier.

Another naming pattern that BC4J uses to help us remember which tier our classes belong in is the  Impl suffix on classes in our project like:

  •  EmpsInDepartmentImpl.java - our view object implementation class
  •  EmpsInDepartmentRowImpl.java - our value object implementation class
  •  EmpModelImpl.java - our service interface implementation class

These classes should never appear in client-side code. As we'll see in the next section, client code should always refer to methods on the BC4J framework interfaces, or on the custom extensions to those interfaces that the BC4J framework creates and manages as we expose custom methods on View Objects, Value Objects (also known as View Object Rows), and Application Modules.

Writing a Sample View Layer Client

With our simple business tier functionality built, and our custom methods exposed, we can concentrate on writing the client code that makes use of it. While our example will be a simple console program that outputs data to  System.out, the techniques illustrated by this example are valid for any Java client code.

First we need to use a factory to obtain an instance of our  EmpModel service interface to work with. BC4J provides several ways to acquire application modules, but the easiest is to use the  oracle.jbo.client.Configuration class.

Configurations are named lists of runtime parameters that affect how the client connects to the business tier. By default our  EmpModel application module will get a configuration created named  EmpModelLocal, with parameters setup for the client to connect to the business tier as a set of local java classes. If we later choose to deploy our application module as an EJB Session Bean, BC4J with again by default create a configuration named  EmpModel9iAS with parameters set appropriately for a remote EJB-based connection from client to business tier. Using the BC4J framework's Configuration class, and an appropriate configuration name, you can easily get an instance of an application module like this:

import test.common.EmpModel;
:
String _am = "test.EmpModel";  // Full name of application module
String _cf = "EmpModelLocal";  // Name of configuration
EmpModel empModel = (EmpModel)Configuration.createRootApplicationModule(_am,_cf);

you can use this instance of  EmpModel as you need to, and then when you are finished using it, you use a companion method on the  Configuration object to release your application module instance:

Configuration.releaseRootApplicationModule(empModel, true /* Remove AM instance? */);

If you pass  true as the second argument to  releaseRootApplicationModule, then the application module instance you were working with will be destroyed. If instead you pass false, the application module instance will be kept in a pool for subsequent reuse in the same process.

The following program illustrates using the technique above to exercise all of the different custom methods we exposed above to retrieve and iterate over:

  1. A  java.util.List of hand-written  EmpValueObject value objects
  2. A  java.util.Vector of hand-written  EmpValueObject value objects
  3. A  java.util.Collection of hand-written  EmpValueObject value objects
  4. A  oracle.jbo.RowSet of BC4J-generated, typesafe  EmpsInDepartmentRow value objects

All four of the above value object collections are retrieved by invoking a custom method on the  EmpModel service interface. Examples 5, 6, and 7 in the  TestClient program then go on to illustrate some of the BC4J value object collection functionality that is possible directly using the base BC4J client interface  oracle.jbo.ApplicationModule, from which our  EmpModel extends.

Specifically, these last three examples illustrate retrieving and iterating:

  1. A  RowSet of BC4J-generated, typesafe  EmpsInDepartmentRows value objects, using  findViewObject()
  2. A  RowSet of generic  oracle.jbo.Row using the same custom method as in (4) above.
  3. A  RowSet of generic  oracle.jbo.Row using  findViewObject()

Here's the sample code:

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import oracle.jbo.ApplicationModule;
import oracle.jbo.Row;
import oracle.jbo.RowSet;
import oracle.jbo.RowSetIterator;
import oracle.jbo.client.Configuration;
import oracle.jbo.domain.Number;

import test.common.EmpModel;
import test.common.EmpValueObject;
import test.common.EmpsInDepartment;
import test.common.EmpsInDepartmentRow;

public class TestClient {
  public static void main(String[] args){
    String _am = "test.EmpModel"; // Full name of application module

// The only thing that needs to change to run this same code
// in a three-tier deployment is the name of the configuration
// you want to use.
//
//    String _cf = "EmpModel9iAS";  // Name of remote configuration

    String _cf = "EmpModelLocal";  // Name of configuration

    // Cast ApplicationModule to custom EmpModel interface to call custom methods
    EmpModel empModel = (EmpModel)Configuration.createRootApplicationModule(_am,_cf);

    p("1: Retrieve/iterate List of User-Created Value Objects from AM Custom Method");
    List list = empModel.listOfEmpsInDept(10);
    Iterator it = list.iterator();
    while (it.hasNext()) {
      // Here we use our hand-created EmpValueObject
      EmpValueObject empValObj = (EmpValueObject)it.next();
      p(empValObj.getName()+","+empValObj.getId()+","+empValObj.getSalary());      
    }

    p("2: Retrieve/iterate Vector of User-Created Value Objects from AM Custom Method");
    Vector v = empModel.vectorOfEmpsInDept(10);
    it = v.iterator();
    while (it.hasNext()) {
      EmpValueObject empValObj = (EmpValueObject)it.next();
      p(empValObj.getName()+","+
                         empValObj.getId()+","+
                         empValObj.getSalary());
    }
    
    p("3: Retrieve/iterate Collection of User-Created Value Objects from AM Custom Method");
    Collection coll = empModel.collectionOfEmpsInDept(10);
    it = coll.iterator();
    while (it.hasNext()) {
      // Here we use our hand-created EmpValueObject
      EmpValueObject empValObj = (EmpValueObject)it.next();
      p(empValObj.getName()+","+empValObj.getId()+","+empValObj.getSalary());      
    }
    
    p("4: Retrieve/iterate RowSet of Typesafe EmpsInDepartmentRows from AM Custom Method");
    RowSet rs = empModel.rowsetOfEmpsInDept(10);
    // Not strictly needed since RowSet interface inherits from RowSetIterator
    // and for convenience BC4J aggregates a default iterator "built-in" to the
    // RowSet interface implementation object, but here we can create a secondary
    // iterator to illustrate the parallel programming pattern with Vector, List,
    // and Collection. Iterators can be named. Passing null says you don't care
    // about the name
    RowSetIterator rsit = rs.createRowSetIterator(null);
    while (rsit.hasNext()) {
      // Rather than needing a user-created value object, we use the
      // typesafe EmpsInDepartmentRow interface provided by BC4J
      // Same typesafe benefits, same network traffic savings, no hand-coded
      // value object to write for each query we use. BC4J does it for us.
      EmpsInDepartmentRow empValObj = (EmpsInDepartmentRow)rsit.next();
      p(empValObj.getName()+","+empValObj.getId()+","+empValObj.getSalary());      
    }

    p("5: Retrieve/iterate RowSet of Typesafe EmpsInDepartmentRows using findViewObject");
    // Cast to our custom interface EmpsInDepartment to call our custom VO methods
    EmpsInDepartment eid = (EmpsInDepartment)empModel.findViewObject("EmpsInDepartment");
    // Use custom method on VO custom interface to encapsulate where clause details
    eid.setDepartmentNumber(10);
    eid.executeQuery();
    // EmpsInDepartment interface extends ViewObject interface which extends
    // RowSet interface which extends RowSetIterator. So, here we show that we
    // can use the iterator behavior that's built-in to RowSet for convenience
    // instead of doing further casting
    while (eid.hasNext()) {
      EmpsInDepartmentRow empValObj = (EmpsInDepartmentRow)rs.next();
      p(empValObj.getName()+","+empValObj.getId()+","+empValObj.getSalary());      
    }

    p("6: Retrieve/iterate RowSet of Generic Rows from AM Method");
    rs = empModel.rowsetOfEmpsInDept(10);
    while (rs.hasNext()) {
      // Rather than using a typesafe Row subinterface like EmpsInDepartmentRow
      // above, here we just use the base Row interface and call getAttribute()
      // instead of typesafe getter methods
      Row empRow = rs.next();
      p(empRow.getAttribute("Name")+","+empRow.getAttribute("Id")+","+
        empRow.getAttribute("Salary"));
    }

    p("7: Retrieve/iterate RowSet of Generic Rows using findViewObject");
    // Since findViewObject() is on the ApplicationModule interface, we
    // illustrate here that we can invoke it without using our
    // typesafe TestModule interface. Also, recall that ViewObject extends
    // RowSet so if it's more clear for our code, we can directly use
    // the RowSet interface here to refer to the ViewObject's default rowset
    // that it provides (through aggregation).
    ApplicationModule testAppModule = empModel;
    rs = testAppModule.findViewObject("EmpsInDepartment");

    // Rather than calling the encapsulated setDepartmentMethod on our
    // custom EmpsInDepartment interface as we did above, here we show
    // the client directly setting the where clause parameters.
    // NOTE: Params are zero-based, so 0 is the first param in the SQL.
    rs.setWhereClauseParam(0,new Integer(10));
    rs.executeQuery();    
    while (rs.hasNext()) {
      Row empRow = rs.next();
      p(empRow.getAttribute("Name")+","+empRow.getAttribute("Id")+","+
        empRow.getAttribute("Salary"));
    }

    // Release the AppModule (optionally back to the pool if we pass 'false'
    Configuration.releaseRootApplicationModule(empModel,true);
  }
  private static void p(String m){System.out.println(m);}
}

As the example illustrates, working with a BC4J  RowSet uses the same familiar coding patterns as working with a  java.util.Collection, however a BC4J  RowSet interface is a collection with a few more tricks up its sleeve. In fact, in addition to making it possible to use automatically-generated value objects, a further benefit of using the BC4J  RowSet is that the value objects in a  RowSet can be optionally fully updateable. This is in sharp contrast to a  java.util.Collection of hand-created value objects, which are typically strictly read-only in nature due to the complexity of keeping any updates made in sync with your business logic tier. BC4J offers significant help here by automating this synchronization, just by using a  RowSet instead.

Our  EmpsInDepartment view object created above was a simple, read-only collection of database query results. However, if you were working with a  RowSet produced by a BC4J view object that you related to one or more entity objects at design time, then that  RowSet automatically becomes a collection of fully-updateable value objects.

This means that rather than having to manage the updates to your business objects yourself, the BC4J framework allows your client to simply call appropriate setter methods to modify the attributes of a value object row in any way necessary, and then commit the changes. All subsequent business logic enforcement and business object persistence is handled automatically.

Conclusion

In this brief article, we've seen how easy it is to use the BC4J framework to create a model layer for a Model-View-Controller application architecture, and how easy it is to access the various collections of value objects of business information through a service interface from our view layer.

We've seen the best practice techniques allowing custom methods on our application module to be exposed to clients in a way that allows our deployment architecture to change at any time, without causing ripple effects to our application code. This means your BC4J business tier can be deployed as simple Java classes in your Servlet/Web tier, or as an EJB Session Facade in your EJB tier with the flick of a switch and no client code changes.

And finally, we've seen that while possible to use BC4J in combination with "classic" collection classes/interfaces like  Vector,  List, and  Collection from the  java.util package together with hand-created value objects, the BC4J framework offers more functional, more targeted value object collection functionality that follows the same familiar programming paradigms. The examples in this article have illustrated how BC4J's  ViewObject,  RowSet, and  Row interfaces can be used either in a generic way or a typesafe way with automatically-generated custom interfaces, providing a "collections of value objects" implementation that can optionally be fully-updateable with no extra programming work by the developer.

E-mail this page
Printer View Printer View
Oracle Is The Information Company About Oracle | Oracle RSS Feeds | Careers | Contact Us | Site Maps | Legal Notices | Terms of Use | Privacy