Context
The client requires a list of items from the service for presentation.
The number of items in the list is unknown and can be quite large in many
instances.
Problem
Most Java 2 Platform, Enterprise
Edition (J2EE) applications have a search and query requirement to search
and list certain data. In some cases, such a search and query operation
could yield results that can be quite large. It is impractical to return
the full result set when the client's requirements are to traverse the
results, rather than process the complete set. Typically, a client uses
the results of a query for read-only purposes, such as displaying the
result list. Often, the client views only the first few matching records,
and then may discard the remaining records and attempt a new query. The
search activity often does not involve an immediate transaction on the
matching objects. The practice of getting a list of values represented
in entity beans by calling an ejbFind() method, which returns
a collection of remote objects, and then calling each entity bean to get
the value, is very network expensive and is considered a bad practice.
There are consequences associated with using Enterprise JavaBeans
(EJB) finder methods that
result in large results sets. Every container implementation has a certain
amount of finder method overhead for creating a collection of EJBObject
references. Finder method behavior performance varies, depending on a
vendor's container implementation. According to the EJB specification,
a container may invoke ejbActivate() methods on entities
found by a finder method. At a minimum, a finder method returns the primary
keys of the matching entities, which the container returns to the client
as a collection of EJBObject references. This behavior applies for all
container implementations. Some container implementations may introduce
additional finder method overhead by associating the entity bean instances
to these EJBObject instances to give the client access to those entity
beans. However, this is a poor use of resources if the client is not interested
in accessing the bean or invoking its methods. This overhead can significantly
impede application performance if the application includes queries that
produce many matching results.
Forces
-
The application client needs an efficient
query facility to avoid having to call the entity bean's ejbFind()
method and invoking each remote object returned.
-
A server-tier caching mechanism is needed
to serve clients that cannot receive and process the entire results
set.
-
A query that is repeatedly executed on
reasonably static data can be optimized to provide faster results.
This depends on the application and on the implementation of this
pattern.
-
EJB finder methods are not suitable for
browsing entire tables in the database or for searching large result
sets from a table.
-
Finder methods may have considerable overhead
when used to find large numbers of result objects. The container may
create a large number of infrastructure objects to facilitate the
finders.
-
EJB finder methods are not suitable for
caching results. The client may not be able to handle the entire result
set in a single call. If so, the client may need server-side caching
and navigation functions to traverse the result set.
-
EJB finder methods have predetermined
query constructs and offer minimum flexibility. The EJB specification
2.0 allows a query language, EJB QL, for container-managed entity
beans. EJB QL makes it easier to write portable finders and offers
greater flexibility for querying.
-
Client wants to scroll forward and backward within a result set.
Solution
Use a Value List Handler to control the search, cache the results,
and provide the results to the client in a result set whose size and traversal
meets the client's requirements.
This pattern creates a ValueListHandler to control query execution functionality
and results caching. The ValueListHandler directly accesses a DAO that
can execute the required query. The ValueListHandler stores the results
obtained from the DAO as a collection of Transfer Objects. The client
requests the ValueListHandler to provide the query results as needed.
The ValueListHandler implements an Iterator pattern [GoF] to provide the
solution.
Structure
The class diagram in Figure 8.29 illustrates the Value List Handler pattern.
Figure 8.29 Value List Handler Class Diagram
Participants and Collaborations
The sequence diagram in Figure 8.30 shows the interactions for the Value
List Handler.
Figure 8.30 Value List Handler Sequence Diagram
ValueListIterator
This interface may provide iteration facility with the following example
methods:
-
getSize() obtains the size
of the result set.
-
getCurrentElement() obtains
the current Transfer Object from the list.
-
getPreviousElements(int howMany)
obtains a collection of Transfer Objects that are in the list prior
to the current element.
-
getNextElements(int howMany)
obtains a collection of Transfer Objects that are in the list after
the current element.
-
resetIndex() resets the index to the start of the list.
Depending on the need, other convenience methods can be included to be
part of the ValueListIterator interface.
ValueListHandler
This is a list handler object that implements the ValueListIterator interface.
The ValueListHandler executes the required query when requested by the
client. The ValueListHandler obtains the query results, which it manages
in a privately held collection represented by the ValueList object. The
ValueListHandler creates and manipulates the ValueList collection. When
the client requests the results, the ValueListHandler obtains the Transfer
Objects from the cached ValueList, creates a new collection of Transfer
Objects, serializes the collection, and sends it back to the client. The
ValueListHandler also tracks the current index and size of the list.
DataAccessObject
The ValueListHandler can make use of a DataAccessObject to keep separate
the implementation of the database access. The DataAccessObject provides
a simple API to access the database (or any other persistent store), execute
the query, and retrieve the results.
ValueList
The ValueList is a collection (a list) that holds the results of the
query. The results are stored as Transfer Objects. If the query fails
to return any matching results, then this list is empty. The ValueListHandler
session bean caches ValueList to avoid repeated, unnecessary execution
of the query.
TransferObject
The TransferObject represents an object view of the individual record
from the query's results. It is an immutable serializable object that
provides a placeholder for the data attributes of each record.
Strategies
Java Object Strategy
The ValueListHandler can be implemented as an arbitrary Java object.
In this case, the ValueListHandler can be used by any client that needs
the listing functionality. For applications that do not use enterprise
beans, this strategy is useful. For example, simpler applications may
be built using servlets, JavaServer Pages (JSP) pages, Business Delegates, and DAOs. In this scenario,
the Business Delegates can use a ValueListHandler implemented as a Java
object to obtain list of values.
Stateful Session Bean Strategy
When an application uses enterprise beans in the business tier, it may
be preferable to implement a session bean that uses the ValueListHandler.
In this case, the session bean simply fronts an instance of a ValueListHandler.
Thus, the session bean may be implemented as a stateful session bean to
hold on to the list handler as its state, and thus may simply act as a
facade (see "Session Facade" on page 291) or as a proxy.
Consequences
-
Provides Alternative to EJB Finders
for Large Queries
Typically, an EJB finder method is a resource-intensive and an expensive
way of obtaining a list of items, since it involves a number of EJBObject
references. The Value List Handler implements a session bean that
uses a DAO to perform the query and to create a collection of Transfer
Objects that match the query criteria. Because Transfer Objects have
relatively low overhead compared to EJBObject references and their
associated infrastructure, this pattern provides benefits when application
clients require queries resulting in large result sets.
-
Caches Query Results on Server Side
The result set obtained from a query execution needs to be cached
when a client must display the results in small subsets rather than
in one large list. However, not all browser-based clients can perform
such caching. When they cannot, the server must provide this functionality.
The Value List Handler pattern provides a caching facility in the
Value List Handler session bean to hold the result set obtained from
a query execution. The result set is a collection of Transfer Objects
that can be serialized if required.
When the client requests a collection, or a subset of a collection,
the handler bean returns the requested results as a serialized collection
of Transfer Objects. The client receives the collection and now has
a local copy of the requested information, which the client can display
or process. When the client needs an additional subset of the results,
it requests the handler to return another serialized collection containing
the required results. The client can process the query results in
smaller, manageable chunks. The handler bean also provides the client
with navigation facilities (previous and next) so that the results
may be traversed forward and backward as necessary.
-
Provides Better Querying Flexibility
Adding a new query may require creating a new finder method or modifying
an existing method, especially when using bean-managed entity beans.
(With bean-managed entity beans, the developer implements the finder
methods in the bean implementation.) With a container-managed entity
bean, the deployer specifies the entity bean finder methods in the
bean's deployment descriptor. Changes to a query for a container-managed
bean require changes to the finder method specification in the deployment
descriptor. Therefore, finder methods are ill-suited to handle query
requirements that change dynamically. You can implement a Value List
Handler to be more flexible than EJB finder methods by providing ad
hoc query facilities, constructing runtime query arguments using template
methods, and so forth. In other words, a Value List Handler developer
can implement intelligent searching and caching algorithms without
being limited by the finder methods.
-
Improves Network Performance
Network performance may improve because only requested data, rather
than all data, is shipped (serialized) to the client on an as-needed
basis. If the client displays the first few results and then abandons
the query, the network bandwidth is not wasted, since the data is
cached on the server side and never sent to the client. However, if
the client processes the entire result set, it makes multiple remote
calls to the server for the result set. When the client knows in advance
that it needs the entire result set, the handler bean can provide
a method that sends the client the entire result set in one method
call, and the pattern's caching feature is not used.
-
Allows Deferring Entity Bean Transactions
Caching results on the server side and minimizing finder overhead
may improve transaction management. When the client is ready to further
process an entity bean, it accesses the bean within a transaction
context defined by the use case. For example, a query to display a
list of books uses a Value List Handler to obtain the list. When the
user wants to view a book in detail, it involves the book's entity
bean in a transaction.
Sample Code
Implementing the Value List Handler as a Java Object
Consider an example where a list of Project business objects are to be
retrieved and displayed. The Value List Handler pattern can be applied
in this case. The sample code for this implementation is listed in Example
8.29 as ProjectListHandler, which is responsible to provide the list of
Projects. This class extends the ValueListHandler base class,
which provides the generic iteration functionality for all Value List
Handler implementations in this application. The ValueListHandler
sample code is listed in Example 8.30. The ValueListHandler
implements the generic iterator interface ValueListIterator ,
which is shown in Example 8.32. The relevant code sample from the
data access object ProjectDAO, used by ValueListHandler to
execute the query and obtain matching results, is shown in Example 8.31.
Example 8.29 Implementing Value List Handler Pattern
package corepatterns.apps.psa.handlers;
import java.util.*;
import corepatterns.apps.psa.dao.*;
import corepatterns.apps.psa.util.*;
import corepatterns.apps.psa.core.*;
public class ProjectListHandler
extends ValueListHandler {
private ProjectDAO dao = null;
// use ProjectTO as a template to determine
// search criteria
private ProjectTO projectCriteria = null;
// Client creates a ProjectTO instance, sets the
// values to use for search criteria and passes
// the ProjectTO instance as projectCriteria
// to the constructor and to setCriteria() method
public ProjectListHandler(ProjectTO projectCriteria)
throws ProjectException, ListHandlerException {
try {
this.projectCriteria = projectCriteria;
this.dao = PSADAOFactory.getProjectDAO();
executeSearch();
} catch (Exception e) {
// Handle exception, throw ListHandlerException
}
}
public void setCriteria(ProjectTO projectCriteria) {
this.projectCriteria = projectCriteria;
}
// executes search. Client can invoke this
// provided that the search criteria has been
// properly set. Used to perform search to refresh
// the list with the latest data.
public void executeSearch()
throws ListHandlerException {
try {
if (projectCriteria == null) {
throw new ListHandlerException(
"Project Criteria required...");
}
List resultsList =
dao.executeSelect(projectCriteria);
setList(resultsList);
} catch (Exception e) {
// Handle exception, throw ListHandlerException
}
}
} |
The Value List Handler is a generic iterator class that provides the
iteration functionality.
Example 8.30 Implementing Generic ValueListHandler Class
package corepatterns.apps.psa.util;
import java.util.*;
public class ValueListHandler
implements ValueListIterator {
protected List list;
protected ListIterator listIterator;
public ValueListHandler() {
}
protected void setList(List list)
throws IteratorException {
this.list = list;
if(list != null)
listIterator = list.listIterator();
else
throw new IteratorException("List empty");
}
public Collection getList(){
return list;
}
public int getSize() throws IteratorException{
int size = 0;
if (list != null)
size = list.size();
else
throw new IteratorException(...); //No Data
return size;
}
public Object getCurrentElement()
throws IteratorException {
Object obj = null;
// Will not advance iterator
if (list != null)
{
int currIndex = listIterator.nextIndex();
obj = list.get(currIndex);
}
else
throw new IteratorException(...);
return obj;
}
public List getPreviousElements(int count)
throws IteratorException {
int i = 0;
Object object = null;
LinkedList list = new LinkedList();
if (listIterator != null) {
while (listIterator.hasPrevious() && (i < count)){
object = listIterator.previous();
list.add(object);
i++;
}
}// end if
else
throw new IteratorException(...); // No data
return list;
}
public List getNextElements(int count)
throws IteratorException {
int i = 0;
Object object = null;
LinkedList list = new LinkedList();
if(listIterator != null){
while( listIterator.hasNext() && (i < count) ){
object = listIterator.next();
list.add(object);
i++;
}
} / / end if
else
throw new IteratorException(...); // No data
return list;
}
public void resetIndex() throws IteratorException{
if(listIterator != null){
listIterator = list.ListIterator();
}
else
throw new IteratorException(...); // No data
}
...
} |
Example 8.31 ProjectDAO Class
package corepatterns.apps.psa.dao;
public class ProjectDAO {
final private String tableName = "PROJECT";
// select statement uses fields
final private String fields = "project_id, name," +
"project_manager_id, start_date, end_date, " +
" started, completed, accepted, acceptedDate," +
" customer_id, description, status";
// the methods relevant to the ValueListHandler
// are shown here.
// See Data Access Object pattern for other details.
...
private List executeSelect(ProjectTO projCriteria)
throws SQLException {
Statement stmt= null;
List list = null;
Connection con = getConnection();
StringBuffer selectStatement = new StringBuffer();
selectStatement.append("SELECT "+ fields +
" FROM " + tableName + "where 1=1");
// append additional conditions to where clause
// depending on the values specified in
// projCriteria
if (projCriteria.projectId != null) {
selectStatement.append (" AND PROJECT_ID = '" +
projCriteria.projectId + "'");
}
// check and add other fields to where clause
...
try {
stmt = con.prepareStatement(selectStatement);
stmt.setString(1, resourceID);
ResultSet rs = stmt.executeQuery();
list = prepareResult(rs);
stmt.close();
}
finally {
con.close();
}
return list;
}
private List prepareResult(ResultSet rs)
throws SQLException {
ArrayList list = new ArrayList();
while(rs.next()) {
int i = 1;
ProjectTO proj = new
ProjectTO(rs.getString(i++));
proj.projectName = rs.getString(i++);
proj.managerId = rs.getString(i++);
proj.startDate = rs.getDate(i++);
proj.endDate = rs.getDate(i++);
proj.started = rs.getBoolean(i++);
proj.completed = rs.getBoolean(i++);
proj.accepted = rs.getBoolean(i++);
proj.acceptedDate = rs.getDate(i++);
proj.customerId = rs.getString(i++);
proj.projectDescription = rs.getString(i++);
proj.projectStatus = rs.getString(i++);
list.add(proj);
}
return list;
}
...
} |
Example 8.32 ValueListIterator Class
package corepatterns.apps.psa.util;
import java.util.List;
public interface ValueListIterator {
public int getSize()
throws IteratorException;
public Object getCurrentElement()
throws IteratorException;
public List getPreviousElements(int count)
throws IteratorException;
public List getNextElements(int count)
throws IteratorException;
public void resetIndex()
throws IteratorException;
// other common methods as required
...
} |
Related Patterns
-
Iterator [GoF]
This Value List Handler pattern is based on Iterator pattern, described
in the GoF book, Design Patterns: Elements of Reusable Object-Oriented
Software.
-
Session Facade
Since the Value List Handler is a session bean, it may appear as a
specialized Session Facade. However, in isolation, it is a specialized
session bean rather than a specialized Session Facade. A Session Facade
has other motivations and characteristics (explained in the Session
Facade pattern), and it is much coarser grained.
Contact Us© 2001-2002 Sun Microsystems,
Inc. All Rights Reserved.
|