Skip to main content

If you don't have an IBM ID and password, register here.

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. This profile includes the first name, last name, and display name you identified when you registered with developerWorks. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

 

The Go-ForIt Chronicles: Memoirs of eXtreme DragonSlayers, Part 10

Using custom finder methods to retrieve different result sets

Dan Hattenberger, e-business Architect, IBM
Dan Hattenberger
Dan Hattenberger is an e-business Architect for IBM Developer Relations Technical Consulting in Austin, Texas, providing education, enablement, and consulting to IBM business partners. He started at IBM in Rochester, Minnesota, and held various development positions ranging from channel programming for devices attached to a S/370 channel, to developing process control systems, applications and databases, to managing programmers and engineers. His career changed from development to technical and project management positions working with IBM business partners as the world struggled with Object Oriented Programming, caught fire with Java, and evolved to e-business. Dan moved to Austin, Texas, in 1995 to join Developer Relations, and last year joined the DragonSlayers. He is a certified e-business Solution Designer. You can send notes to Dan at dhatten@us.ibm.com.

Summary:  Container-managed Enterprise JavaBeans (EJB) components are used in the Java world to represent real entities stored in databases. Finder methods allow applications to work with the EJB container to properly retrieve the data from the databases and instantiate the corresponding entity EJB components. This article explains what custom finders do and shows how to use them in several examples.

Date:  01 Oct 2001
Level:  Intermediate

Comments:  

Introduction

The DragonSlayers are moving forward in the development of the Go-ForIt application. We use EJB components extensively for the major objects in our system: users, errands, and bids. We defined them as container-managed persistence (CMP) entity beans. As we learn how to use the container to accomplish exactly what we want, we're realizing that custom finders play a big role.

With bean-managed persistence, the finder methods are implemented with Java code using a database interface such as Java DataBase Connectivity (JDBC). The designer and programmer have complete control over the relationships between data source and EJB instances. This is not true with CMP, where the container does all of the database calls, and controls the storage and retrieval of the persistent fields in the EJB components into and out of some data store (or back-end process or business process). These classes and methods are generated through EJB deployment tools; the programmer defines EJB variables to be persisted, and the tools generate the code. To do this, the programmer must map each EJB variable to a real-life data field. The administrator must map the EJB schema to a real-life database or back-end process where these data fields reside.

To ensure that the container does exactly what we want when we invoke a finder method, we need a way to "help" the container find the correct EJB components based on this real-world data. The current EJB specification does not define how all of this mapping occurs, so different providers of EJB containers define this process differently. (We used the IBM WebSphere Application Server (WAS) implementation and VisualAge for Java.)

Our discussion relates to EJB components at level 1.0, which we are currently using for Go-ForIt. In level 1.0 the techniques for the finder helper interfaces we're using are supported to some extent, but are deprecated. For implementations beyond level 1.0, you would have to use a deployment descriptor extension document rather than the finder helper interface. You would define your queries in the deployment descriptor, not in Java classes. Also, we're using WAS Advanced Edition version 3.5.3. As we migrate to version 4.0 and beyond EJB level 1.0, we'll need to publish more articles related to the new ways of doing business!


The Go-ForIt user story

As you might recall from previous articles in this series, we're writing an application that allows customers to request help doing tasks, which we called errands. The errands fall into categories and subcategories defined by our application. The personal assistants (PAs), or providers of services, can choose to provide services for one or more of these categories, but not necessarily all of them. They register the categories they choose to service. We want our Web site to connect the customers to the PAs. The customers submit the errands they want done, and the PAs view the errands. If a PA chooses to perform the errand, he or she submits a bid, quoting a price for the service. If the customer accepts the bid, then the deal is made.

Customers create an errand by choosing the errand type from a list of categories and subcategories, and by providing comments that specifically define the task. PAs can view different lists of errands. Most often, PAs want to see the errands that match their profile, in which they've declared what they want to service. Customers, on the other hand, want to see a list of the errands they have submitted. Either party can choose an individual errand from the list and display its details. Eventually, both the PAs and customers will want many views of the errands, so we will need to retrieve errands from our database in multiple ways.


Architecture in the EJB style

Let's start with a look at entity EJB components and some of the elements in an implementation. The EJB component is a Java class managed by an EJB container, or the environment in which the EJB component resides. Clients are Java classes that use the EJB component and access the component indirectly through the container using the Home and Remote interfaces. The elements are shown in the figure below.


Figure 1. Architecture
How the EJB elements work together

In our case, we defined a CMP entity bean named ErrandBean that represents an errand. The errand bean resides within a container, which controls the lifecycle of the bean and all interactions with the bean. The client represents an application that will use the bean. ErrandBeanController, a session bean, is the client in this application.

Access to the container and the bean are handled through two interfaces:

Home interface
Creates a new bean, finds a specific bean or set of beans, and destroys the bean. These are tasks done by the container.
Remote interface
Implements the public bean methods that will be exposed to the client. To the client, the method invocation on the Remote interface looks like a direct call to the bean; in reality, the method on the Remote interface is handled by the container, which invokes corresponding methods on the bean. The method on the bean returns a result to the container and the container, using the Remote interface, returns a result to the client.

Instance pooling

When the EJB server starts it creates one or more containers to manage the beans. EJB servers are developed by different companies. Some support instance pooling where the container will, to save processing time later, instantiate a number of beans at startup time and keep them in a pool until needed. The number and types of beans in the instance pool are controlled by the EJB server administrator. When an application needs an instance of one of our ErrandBeans, the container retrieves the data from the datastore and populates one of the pooled ErrandBeans, creating an instance of the one we need.

Eventually, all the beans in the pool are activated and the container needs another instance. At this point, the container passivates one of the beans not currently in use. The container

  • Stores the current state of the bean, making it available for reuse
  • Retrieves the data from the database for the bean being requested
  • Populates the bean with this instance data, creating the needed instance.

In the Java world (outside of containers), one object can instantiate another object and begin to invoke methods on that object immediately. In the world of EJBs, however, the container manages the lifecycle of the beans, and does the instantiation. When an application (client) wants to access an EJB, it must ask the container for the instance of the bean. The container (in our case, WAS Advanced Edition) pools instances of the beans, re-using them as needed. When a client wants to use a bean, the container "finds" that instance in its pool of beans and returns the Remote interface for that bean. In some cases, there is no data in the database corresponding to the requested bean; the container returns a null, as there is no bean matching the client's request.

An EJB server can have several containers, with each supporting different classes of beans, so the client must first locate the Home interface for the type of bean it needs. In our implementation, this location is kept in a directory supported by a Java naming directory service. Our client, the ErrandControllerBean, makes a lookup call through the Java Naming Directory Interface (JNDI), specifying the class of bean (ErrandBean). This returns an instance of the Home interface. Using the Home interface, the client then finds the specific instance or instances needed by that application by invoking one of the finder methods on the Home interface. Every EJB must have at least one findByPrimaryKey finder method. In the case where the application needs a single bean, the client can always find the bean (if it exists) using findByPrimaryKey and providing the primary key of the bean.

So what is a primary key? In our ErrandBean, the primary key is an integer -- a sequence number we call the errand ID. You define the primary key when you define the bean. It is common to define an EJB component to correspond to a row in a database, where each persisted variable in the EJB component corresponds to a field (column) in the row. The primary key of the EJB component, made up of one or more persisted variables, corresponds to the primary key in the database. The EJB specification states that the key must be a unique value represented by a serializable object. We also use a primary key class, constructed from the values comprising the EJB primary key, to make it easy to specify the primary key as a single entity.


Finder methods

Now, back to finder methods. findByPrimaryKey is one finder method, but you may want to retrieve a bean using something else. Our application will retrieve errands specific to certain users, so we will want to define finder methods that return the errands entered by a certain customer, or errands for which a PA might submit a bid, or all errands of a certain type. Each of these finder methods will accept different parameters, and each may return one errand or multiple errands. Finder methods return the bean's Remote interface or an enumeration of Remote interfaces.

All finder methods are handled by the container. How does a container know how to retrieve the data corresponding to the persisted fields in the bean? Look at the figure below.


Figure 2. Mapping beans to data
How data maps to variables

All EJB servers allow a method to map the source of the data back to the container and, eventually, to the EJB component. This information is created by the tools for the container. Because the EJB 1.0 specification does not dictate the format of the deployment descriptors, different containers handle deployment descriptors differently. In WAS, you define datastores by giving them a name and a database URL. This maps the database to a name used within the server. When creating the EJB component, you match the bean with the datastore name. You also define a schema map that maps the persistent variables in the EJB component to the fields in a table schema. These mappings are maintained in the deployment descriptors, not in the EJB component, allowing a system administrator to change the database or table names without requiring a change to the EJB components.

Now we have a mapping of the EJB component to the table in the database, and we have mappings of the variables to the fields in the table. The last mapping we need is between the finder methods and the result rows in the database. You saw how the findByPrimaryKey causes the container to retrieve a row in a database using the primary key of the EJB component as the primary key of the row in the database. But how does the container know how to retrieve other result sets based on requests for EJB components? In WAS, this is handled by defining finder methods on the Home interface called custom finders.

Finder methods can have any input parameters. As long as there is a way to find the right EJB or set of EJBs based on the input parameters, you can construct a query that will return the rows from a database needed to instantiate the bean or beans. However, the container tools cannot possibly generate all the different queries to cover all the possibilities; findByPrimaryKey is pretty obvious, but others are not. You need to help the container get to the right records in the database. This is where custom finder helpers come in.

There are four types of custom finders that you can use:

  • SQL SELECT
  • SQL WHERE
  • SQL Method
  • EJB Query language (EJB level 1.1 only)

We used the second, SQL WHERE, so that's what I describe in this article. You can find information about the other options in the IBM WebSphere InfoCenter (see Resources).


FinderHelper

The FinderHelper interface is made up of strings that contain the SQL WHERE clauses to be used by the container. There is one clause per finder method, and the clause is appended to an SQL statement in the query initiated by the container as it executes the finder method. Examples help explain this concept. The example below shows two finder methods and the FinderHelper interface.

   ...
   public com.goforit.ejb.Errand findNewestErrand(int arg0) 
     throws java.rmi.RemoteException, javax.ejb.FinderException;
   public java.util.Enumeration findAllErrands() 
     throws java.rmi.RemoteException, javax.ejb.FinderException;  
   public com.goforit.....

The two methods above are defined in the Home interface. The first method returns the errand with the highest errand ID. The second returns an enumeration of all the errands. Now look at the FinderHelper interface that is used in conjunction with these two finder methods.

public interface ErrandBeanFinderHelper {
	public final static String findNewestErrandWhereClause = 
		"ERRANDID = (SELECT MAX(ERRANDID) FROM GOFORIT.ERRAND)"; 
	public final static String findAllErrandsWhereClause = 
		"1 = 1"; 
	}

Notice the naming convention (bean class name plus FinderHelper) used for the interface. In this example, the enterprise bean is named ErrandBean. In the first string, again a naming convention is followed -- finder method name plus WhereClause. Also, GOFORIT.ERRAND must match the database alias as defined in the genericFindSqlString field in the persister class. The string consists of the SQL predicate for a WHERE clause on a query statement. The full query, using the first WHERE clause, would look as follows.

   SELECT * FROM GOFORIT.ERRAND WHERE 
     ERRANDID = (SELECT MAX(ERRANDID) FROM GOFORIT.ERRAND;)

Now look at the second finder method, findAllErrands(). The select statement for this method would normally look as follows.

   SELECT * FROM GOFORIT.ERRAND;

This statement needs no WHERE clause, because we want to retrieve all the rows. However, the container will append a WHERE clause anyway, so we need to supply a valid predicate. In this case, any predicate that evaluates to true will work. Look at the findAllErrandsWhereClause in the FinderHelper. Using this predicate, the container will execute the SQL statement in the sample below. The string 1 =1 will always evaluate to true, so all rows meet the criteria and are returned in the result set.

   SELECT * FROM GOFORIT.ERRAND WHERE 1 = 1;

Let's take a look at one more example. The findRecentErrands finder method below will return the errands whose errand IDs are greater than parameter arg0 Remember that the errand IDs, which are integers, are assigned in ascending numerical order as the errands are created, so the errands with the higher ID numbers are the ones created most recently.

   ...
   public com.goforit.ejb.Errand findNewestErrand() 
     throws java.rmi.RemoteException, javax.ejb.FinderException;
   public java.util.Enumeration findAllErrands() 
     throws java.rmi.RemoteException, javax.ejb.FinderException; 
   public java.util.Enumeration findRecentErrands(int arg0) 
     throws java.rmi.RemoteException, javax.ejb.FinderException;  
   public com.goforit.....

The WHERE clause for this finder is fairly simple. In the sample below, the WHERE clause uses ? as a placeholder for the input parameter on the finder method. In this case, the value of arg0 from the findRecent Errands method will be substituted in the SQL statement. The result set will contain all errands whose errand ID is higher than the integer passed to the method.

public interface ErrandBeanFinderHelper {
	public final static String findNewestErrandWhereClause = 
		"ERRANDID = (SELECT MAX(ERRANDID) FROM GOFORIT.ERRAND)"; 
	public final static String findAllErrandsWhereClause = 
		"1 = 1"; 
	public final static String findRecentErrandsWhereClause = 
	"ERRANDID > ?"; 
	}


The persister class

There is one more piece of the puzzle to consider. You need to define a class of type EJSJDBCPersister for the database tables you will use. There are several key pieces of this class. The first is a definition of the table to be used. The following code sample shows the class definition and the definition for our table alias.

....
public class EJSJDBCPersisterErrandBean extends EJSJDBCPersister 
    implements EJSFinderErrandBean, VapEJSJDBCFinderStatementHelper {
	final static String[] createTableSqlStrings = {
	"CREATE TABLE goforit.Errand ( status VARCHAR(30), 
		    description VARCHAR(30), category VARCHAR(30), 
		    errandid INTEGER NOT NULL, subcategory VARCHAR(30), 
		    duedate DATE, timestamp VARCHAR(30), comments VARCHAR(1024), 
		    User_userid VARCHAR(30) NOT NULL)",
    "ALTER TABLE goforit.Errand ADD CONSTRAINT ErrandPK PRIMARY KEY ( errandid)"
	};
....

The first string defines the table and the second defines the key. You must also specify some default information, as follows.

....
	final static String genericFindSqlString = 
	     "SELECT T1.status, T1.description, T1.category, T1.errandid, 
	     T1.subcategory, T1.duedate, T1.timestamp, T1.comments, 
	     T1.User_userid FROM goforit.Errand  T1 WHERE ";
....

This string defines the front end of the query string. Our WHERE clauses will be appended to this string to create the complete SQL query. Be aware that there are some other definitions that need to be supplied in EJSJDBCPersister; there is an update statement, a delete statement, and, if you want to support multiple databases, other table definitions for the different database types.


Summary

Custom finder helpers allow applications to work with the EJB container to access the appropriate EJBs. The FinderHelper class lets you help the container retrieve the appropriate information from the database. I hope this article gave you a useful introduction to these helpful classes and methods.


Stay tuned...

Watch for our next installment of the Go-ForIt chronicles, where we'll cover testing the application. To see the previous articles in our tale of dragonslaying, go to our overview.


Resources

About the author

Dan Hattenberger

Dan Hattenberger is an e-business Architect for IBM Developer Relations Technical Consulting in Austin, Texas, providing education, enablement, and consulting to IBM business partners. He started at IBM in Rochester, Minnesota, and held various development positions ranging from channel programming for devices attached to a S/370 channel, to developing process control systems, applications and databases, to managing programmers and engineers. His career changed from development to technical and project management positions working with IBM business partners as the world struggled with Object Oriented Programming, caught fire with Java, and evolved to e-business. Dan moved to Austin, Texas, in 1995 to join Developer Relations, and last year joined the DragonSlayers. He is a certified e-business Solution Designer. You can send notes to Dan at dhatten@us.ibm.com.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in

If you don't have an IBM ID and password, register here.


Forgot your IBM ID?


Forgot your password?
Change your password


By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. This profile includes the first name, last name, and display name you identified when you registered with developerWorks. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)


By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Sample IT projects
ArticleID=86645
ArticleTitle=The Go-ForIt Chronicles: Memoirs of eXtreme DragonSlayers, Part 10
publish-date=10012001
author1-email=
author1-email-cc=

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).