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.
|