Skip Headers

Oracle9iAS TopLink CMP for Users of BEA WebLogic Guide
Release 2 (9.0.3)

Part Number B10065-01
Go To Documentation Library
Home
Go To Solution Area
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Go to previous page Go to next page

5
Defining and Executing Finders

TopLink provides a feature-rich query framework in which complex database queries can be constructed and executed to retrieve entity beans. TopLink Container-Managed Persistence enables you to define the finder methods on the home interface, but does not require you to implement them in the entity bean. TopLink Container-Managed Persistence provides this required functionality, and offers a number of strategies for creating and customizing finders. The EJB container and TopLink automatically generate the implementation.

Defining finders in TopLink

The general steps required to successfully define a finder method for an entity bean using TopLink Container-Managed Persistence's query framework are as follows:

  1. Declare finders in the ejb-jar.xml file.

  2. Define the finder method on the entity bean's home and/or local home interface(s) (as required by the EJB specification)

  3. Use the Mapping Workbench to change any options on finders.

  4. If required, create an implementation for the query. Some query options require that the query be defined in code on a helper class, but this is not required for most queries.

ejb-jar.xml Finder Options

The ejb-jar.xml file specifies all of the EJB 2.0 specification related information for a bean, including the definitions for any finders that are to be used for that bean. The ejb-jar.xml file may be created and edited using a text editor, or it may be created using the Mapping Workbench. All finders are defined within the ejb-jar.xml file with a structure similar to the following example.

Example 5-1 A simple finder within an ejb-jar.xml file

...
<query>
   <query-method>
      <method-name>findLargeAccounts</method-name>
         <method-params>
         <method-param>double</method-param>
         </method-params>
   </query-method>
   <ejb-ql><![CDATA[SELECT OBJECT(account) FROM AccountBean account WHERE account.balance > ?1]]></ejb-ql>
</query>
...

Query Section - XML Elements

The ejb-jar.xml file can contain zero or more <query> elements in the <entity> tag. Each one of these <query> tags corresponds to a finder method that is defined on the bean's home or local home interface. If you define the same finder (same name, return type, and parameters) on both home interfaces, then only a single <query> element is defined in the ejb-jar.xml file and they must share the same TopLink query.

The elements that are defined in the <query> section of the ejb-jar.xml file are:

Choosing the best finder type for your query

TopLink supports five general types of finders:

Most finders can be defined using the EJBQL mechanism. However, the other mechanisms have their own advantages:

For more information about defining finders, see the Oracle9iAS TopLink Mapping Workbench Reference Guide.

Using EJBQL

EJBQL is the standard query language defined in the EJB 2.0 specification and is available for use in TopLink with both 1.1 and 2.0 beans. EJBQL finders enable a specific EJBQL string to be specified as the implementation of the query.

Advantages

EJBQL offers several advantages in that it:

Disadvantages

Some complex queries may be difficult to define using EJBQL.

Creating an EJBQL finder

To create an EJBQL finder
  1. Declare the finder in the ejb-jar.xml and enter the EJBQL string in the ejb-ql tag.

  2. Declare the finder on the Home interface, the LocalHome interface, or both as required.

  3. Start the Mapping Workbench.

  4. Specify the ejb-jar.xml location and select File > Updated Project from ejb-jar.xml to read in the finders.

  5. Go to the Queries > Named Queries tab for the bean.

  6. Select and configure the finder.

Following is an example of a simple EJBQL query that takes one parameter. In this example, the question mark ("?") is used to bind the argument name within the EJBQL string.

SELECT OBJECT(employee) FROM Employee employee WHERE (employee.name =?1)


Notes:

  • The argument (bolded in the example) must be a numeric value.

  • Employee (bolded in the example) refers to the <abstract-schema-name> defined for that particular bean.


For more information on EJBQL, see the Oracle9iAS TopLink Foundation Library Guide.

Using the TopLink Expression framework

Finders can take advantage of TopLink's rich expressions framework to define the logic of the query.

Advantages

Using TopLink expressions to access the database has some advantages over using EJBQL:

Example
A sub-query expression using a comparison and count operation

This code queries all employees that have more than 5 managed employees.

ExpressionBuilder emp = new ExpressionBuilder();
ExpressionBuilder managedEmp = new ExpressionBuilder();
ReportQuery subQuery = new ReportQuery(Employee.class, managedEmp);
subQuery.addCount();
subQuery.setSelectionCriteria (managedEmp.get("manager").equal(emp));
Expression exp = emp.subQuery(subQuery).greaterThan(5);


Note:

This type of query is only possible using the TopLink expression framework.


Disadvantages

The disadvantages to using TopLink Expressions in finders are:

Creating an Expression Finder

  1. Declare the finder in the ejb-jar.xml and leave the ejb-ql tag empty. This step is optional but should be performed in order to maintain compliance with the EJB 2.0 spec.

  2. Declare the finder on the Home interface, the LocalHome interface, or both as required.

  3. Create an amendment method as described in "Creating amendment methods for Expression finders".

  4. Start the Mapping Workbench.

  5. Select Advanced Properties > After Load from the menu for the bean.

  6. Enable the amendment method for the descriptor by specifying the class and name of the static method.

Building an expression

An example of an expression is as follows:

builder.get("address").get("city").equal(theCity);

This represents the query logic, or the "selection criteria" for the query. The logical translation for this query is:

To introduce the basics of constructing a query expression, examine each element of this expression:

The first get in this statement specifies the address attribute, while the second get examines city, which is an attribute of the attribute, address.

Creating amendment methods for Expression finders

A TopLink Expression query must first be implemented and then registered with the runtime within a "TopLink descriptor amendment" method. Define the named query in the static amendment method, and add the query to the TopLink descriptor's QueryManager. The named query must be defined based on the following:

For more information on configuring an amendment method, see the Oracle9iAS TopLink Mapping Workbench Reference Guide.

Using Dynamic finders

The EJB 2.0 specification allows for finders defined in the ejb-jar.xml file as queries, with their search criteria specified as EJBQL query strings. TopLink expands on the specification, enabling you to create queries using other query formats such as SQL, expressions, dynamic query objects, and Redirects (see "Choosing the best finder type for your query").

In addition to this support, TopLink provides a number of predefined finders that can be used for executing dynamic queries (queries for which the logic is determined by the user at run-time). The names for these finders are reserved by the TopLink runtime and cannot be reused for other finders.

The predefined finders are:

Each of these finders can also be used without the Vector of arguments. For example, EJBObject findOneByEJBQL(String ejbql) is a valid dynamic finder. The return type of "EJBObject" is replaced by the component interface of your bean.

Creating a Dynamic finder

To create a Dynamic finder
  1. Declare the finder in the ejb-jar.xml file and leave the ejb-ql tag empty.

  2. Declare the finder on the Home interface, the LocalHome interface, or both as required.

  3. Start the Mapping Workbench.

  4. Specify the ejb-jar.xml location and select File > Updated Project from ejb-jar.xml to read in the finders.

  5. Go to the Queries > Named Queries tab for the bean.

  6. Select and configure the finder.


    Notes:

    • If the advanced query options described in "Advanced finder options" are not required, only steps 1 and 2 needs to be completed.

    • The findOneByQuery and findManyByQuery dynamic finders should not have any query options configured for them. The reason is the query is created at runtime by the client and passed as a parameter to the finder. Any query options that you wish to set should be done on that query.


Using findAll

Like the dynamic finders, the name findAll is reserved by the TopLink runtime and cannot be reused for other finders. For more information on defining and configuring the finder, see "Creating a Dynamic finder".

Using findByPrimaryKey

The findByPrimaryKey finder is always created in the Mapping Workbench on the initial loading of a bean class. Like other finders, the findByPrimaryKey finder can be configured with the various query options that TopLink provides (see "Advanced finder options") but can also be deleted from the Mapping Workbench project. In this case, however, a warning is issued informing the user that the default container findByPrimaryKey options will be active. The EJB 2.0 specification requires that the findByPrimaryKey call is present on the home interface, but should not have a query entry in the ejb-jar.xml file.

Using redirect finders

Redirect finders enable you to specify a finder for which the implementation is defined in code as a static method on an arbitrary helper class. When the finder is invoked, the call is re-directed to the specified static method.

The finder can have any arbitrary parameters or none at all. If the finder includes parameters, they are packaged into a vector and passed to the redirect method.

Advantages

Redirect finders provide client parameter-passing flexibility. Compared to other finder types in which the parameters are relatively simple objects used to match against an entity bean's attributes, redirect finders may include arguments that are not linked to these values, because the finder implementation is completely defined by the bean developer. The redirect method typically contains the logic required to extract the relevant data from the parameters and use it to construct a TopLink query.

Disadvantages

Redirect queries are complex and often more difficult to configure. they also require an extra helper method to define the query.

To create a redirect finder
  1. Declare the finder in the ejb-jar.xml leaving the ejb-ql tag empty.

  2. Declare the finder on the Home interface, the localHome interface, or both as required.

  3. Create an amendment method (see "Creating amendment methods for Expression finders").

  4. Start the Mapping Workbench.

  5. Select Advanced Properties > After Load from the menu for the bean.

  6. Enable the amendment method for the descriptor by specifying the class and name of the static method.

The amendment method should then add a query to the descriptor's QueryManager as follows:

ReadAllQuery query = new ReadAllQuery();query.setRedirector(new
MethodBaseQueryRedirector (examples.ejb.cmp20.advanced.
FinderDefinitionHelper.class, "findAllEmployeesByStreetName"));
descriptor.getQueryManager().addQuery ("findAllEmployeesByStreetName", query);

examples.ejb.cmp20.advanced.FinderDefinitionHelper includes a static method findAllEmployeesByStreetName(Session session, Vector args) which executes the query. It is up to the implementor of the query method to ensure that the proper types are returned. For methods returning more than one bean, the return type must be java.util.Vector. TopLink converts this result to java.util.Enumeration (or Collection) if required.


Note:

The redirect method also takes a TopLink Session as a parameter. For more information on TopLink Session, see "Database Sessions" in the Oracle9iAS TopLink Foundation Library Guide.


The redirect method must return either a single entity bean (Object) or a Vector. The possible method signatures are:

Example 5-2 A simple Redirect query implementation:

public static Vector findAllEmployeesByStreetName(Session s, Vector args) {
   ReadAllQuery raq = new ReadAllQuery();
   raq = raq.setReferenceClass(EmployeeBean.class);
   raq.addArgument("streetName");
   ExpressionBuilder builder = newExpressionBuilder();
   .raq.setSelectionCriteria(builder.get("address")
   .get("street").equal(args.elmentAt(0)));
   return (Vector)s.executeQuery(raq);
}

At run time when the client invokes the finder from the entity bean's home, the arguments are automatically packaged into the args Vector (in order of appearance from the finder's method signature) for use within the static method. The code implementing the Redirect finder can then use any necessary APIs to extract information out of the arguments (once retrieved from the args Vector) for use within a TopLink expression.

Using SQL

SQL type finders allow a specific SQL string to be specified as the implementation of the query.

Advantages

The advantages of using SQL include:

Disadvantages

This approach is generally not recommended if the query can be created using any of the other options, because

Creating an SQL finder

To create an SQL finder
  1. Declare the finder in the ejb-jar.xml and leave the ejb-ql tag empty.

  2. Start the Mapping Workbench.

  3. Specify the ejb-jar.xml location and select File > Updated Project from ejb-jar.xml to read in the finders.

  4. Go the Queries > Named Queries tab for the bean.

  5. Select the finder, check the SQL radio button and enter the SQL string.

  6. Configure the finder.

Following is an example of a simple SQL finder that takes one parameter. In this example, the hash-character '#' is used to bind the argument projectName within the SQL string.

SELECT * FROM EJB_PROJECT WHERE (PROJ_NAME = #projectName)

Using ejbSelect

ejbSelects are similar to finders in function, but they can only be invoked from within a bean. Like finders, ejbSelects are defined in the ejbjar.xml using a <query> entry and can have various options configured using the Mapping Workbench. Also like finders, ejbSelects require a SELECT clause in addition to FROM and WHERE clauses.

However, ejbSelects differ from regular finders in the following ways:

Understanding select methods

Select methods are query methods intended for internal use within an entity bean instance. Unlike finder methods, select methods are not specified in the entity bean's home interface but on the abstract bean itself.

The format for an ejbSelect method definition looks like this:

public abstract type ejbSelect<METHOD>(...);

The select method represents a query method that is not directly exposed to the client in the home or component interface. It is defined as being abstract, and each bean can include zero or more such methods.

Even though the select method is not based on the identity of the entity bean instance on which it is invoked, it can use the primary key of an entity bean as an argument to an ejbSelect<METHOD> to define a query that is logically scoped to a particular entity bean instance.

Select methods have the following characteristics:

The return type for ejbSelects that return entities is determined by the <result-type-mapping> tag in the ejb-jar.xml. If the flag is set to Remote, then EJBObjects are returned; if set to Local, then EJBLocalObjects are returned.

Creating an ejbSelect
  1. Declare the ejbSelect in the ejb-jar.xml, enter the EJBQL string in the <ejb-ql> tag, and specify the return type in the <result-type-mapping> tag (if required).

  2. Declare the ejbSelect on the abstract bean class.

  3. Start the Mapping Workbench.

  4. Specify the ejb-jar.xml location and select File > Updated Project from ejb-jar.xml to read in the finders.

  5. Go the Queries > Named Queries tab for the bean.

  6. Select and configure the ejbSelect query.

Advanced finder options

There are a number of options that can be used by the experienced TopLink developer. These options should only be used when the developer has a complete understanding of the consequences of making changes to them.

Caching options

Various configurations can be applied to the underlying query to achieve the correct caching behavior for the application. There are several ways to control the caching options for queries.

For most queries, caching options can be set in the Mapping Workbench (see "Caching objects" in the Mapping Workbench Reference Guide).

The caching options can be set on a per-finder basis. The valid values are:

For more information about TopLink queries as well as the TopLink UnitOfWork and how it integrates with JTS, see "Database Sessions" in the Oracle9iAS TopLink Foundation Library Guide.


Note:

For finders whose queries are manually created (findOneByQuery, findManyByQuery), caching options must be applied manually using TopLink for Java APIs.


Disabling caching of returned finder results

By default, TopLink adds to the cache all returned objects whose primary keys are not currently in the cache. This can be disabled if the client knows that the set of returned objects is very large and wants to avoid the expense of storing these objects. This option is configurable through the Mapping Workbench or on the TopLink query API for queries using dontMaintainCache().

Caching of returned finder results can also be disabled in the Mapping Workbench. For more information on disabling caching for returned finder results, see the Oracle9iAS TopLink Mapping Workbench Reference Guide.

Refreshing finder results

A finder may return information from the database for an object whose primary key is already in the cache. When set to true, the refresh cache option in the Mapping Workbench indicates that the object's non-primary key attributes are refreshed with the returned information. This occurs on findByPrimaryKey finders as well as all EXPRESSION and SQL finders for that bean when set at the bean attributes level.

When refreshing is enabled, the refreshIdentityMapResult() method is invoked on the query. This is configured to automatically cascade private parts. If behavior other than private object cascading is desired, use a dynamic finder.


Caution:

When issuing refreshing finders while in user transactions, refreshing the object may cause changes already made to that object during that transaction to be lost.


In the case where an OptimisticLock field is in use, the refresh cache option can be used in conjunction with the onlyRefreshCacheIfNewerVersion() option. In that case, the non-primary key attributes are refreshed only if the version of the object in the database is newer than the version in the cache.

For finders that have no refresh cache setting, the onlyRefreshCacheIfNewerVersion() method has no effect.

Managing large result sets

Finders can return large result sets which can be resource intensive to collect and process. To give the client more control over the returned results, TopLink finders can be configured to use cursors. This leverages TopLink's CursoredStream and a database's cursoring ability to break up the result set into smaller, more manageable pieces.

Building the query

Any finder that returns a java.util.Enumeration under EJB 1.1 or a java.util.Collection under EJB 2.0 can be configured to use a cursor. When the query is created for the finder, useCursoredStream() enables cursoring.

Example
A query that uses a CursoredStream
ReadAllQuery raq = new ReadAllQuery();
ExpressionBuilder bldr = new ExpressionBuilder();
raq.setReferenceClass(ProjectBean.class);
raq.useCursoredStream();
raq.addArgument("projectName");
raq.setSelectionCriteria(bldr.get("name").
like(bldr.getParameter("projectName")));
descriptor.getQueryManager().addQuery ("findByNameCursored");

Executing the finder from the client in EJB 1.1

An extended protocol is available on the client in oracle.toplink.ejb.cmp.wls11.CursoredEnumerator (based on java.util.Enumeration):

hasMoreElements()

As with java.util.Enumeration, this method returns a boolean indicating if any elements remain.

nextElement()

As with java.util.Enumeration, this method returns the next available element.

nextElements(int count)

Retrieve a Vector of at most count elements from the available results, depending on how many elements are left to read.

close()

close the cursor on the server. It is mandatory that the client send this message when it is done with the results.

The behavior differs from a normal finder as follows:

The following example illustrates client-code executing a cursored finder:

import oracle.toplink.ejb.cmpwaswls11. CursoredEnumerator;
//... other imports as necessary
getTransaction().begin();
CursoredEnumerator cursoredEnumerator = (CursoredEnumerator)getProjectHome()
 .findByNameCursored("proj%");

Vector projects = new Vector();
for (int index = 0; index < 50; i++) {
   Project project = (Project)cursoredEnumerator.nextElement();
   projects.addElement(project);
}

// Rest all at once ...
Vector projects2 = cursoredEnumerator.nextElements(50);
cursoredEnumerator.close();
getTransaction().commit();

Executing the finder from the client in EJB 2.0

An extended protocol is available for the client in oracle.toplink.ejb.cmp.wls.CursoredCollection (based on java.util.Collection):

isEmpty()

As with java.util.Collection, isEmpty() returns a boolean indicating if the Collection is empty or not.

size()

As with java.util.Collection, size() returns an integer which is the number of elements in the Collection.

iterator()

As with java.util.Collection, iterator() returns a java.util.Iterator for enumerating the elements in the Collection.

An extended protocol is also available for oracle.toplink.ejb.cmp.wls.CursoredIterator (based on java.util.Iterator):

close()

closes the cursor on the server. It is mandatory that the client send this message when it is done with the results.

hasNext()

returns a boolean indicating if there is a next element.

next()

returns the next available element.

next(int count)

retrieves a Vector of at most count elements from the available results, depending on how many elements are left to read.

This behavior differs from a normal finder as follows:

The following example illustrates client-code executing a cursored finder

//import both CursoredCollection and CursoredIterator
import oracle.toplink.ejb.cmp.wls.*;
//... other imports as necessary
getTransaction().begin();
CursoredIterator cursoredIterator = (CursoredIterator)getProjectHome().findByNameCursored("proj%")
 .iterator();
Vector projects = new Vector();
for (int index = 0; index < 50; i++) {
   Project project = (Project)cursoredIterator.next();
   projects.addElement(project); !
}
// Rest all at once ...
Vector projects2 = cursoredIterator.next(50);
cursoredIterator.close();
getTransaction().commit();


Go to previous page Go to next page
Oracle
Copyright © 2002 Oracle Corporation.

All Rights Reserved.
Go To Documentation Library
Home
Go To Solution Area
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index