Oracle® Application Server TopLink Application Developer's Guide
10g Release 2 (10.1.2) Part No. B15901-01 |
|
Previous |
Next |
The OracleAS TopLink query framework enables you to construct finders, which are queries that retrieve entity beans. This section describes OracleAS TopLink support for finders, and includes discussions on the following topics and techniques:
To define a finder method for an entity bean that uses the OracleAS TopLink query framework, follow these steps:
Declare the finder in the ejb-jar.xml
file.
Define the finder method.
For EJB 1.1 beans, define the method on the entity bean remote interface.
For EJB 2.0 beans, define the method on the entity bean remoteHome
or localHome
interface.
Use OracleAS TopLink Mapping Workbench to change any options on finders.
If required, create an implementation for the query. Some query options require a query definition in code on a helper class, but most common queries do not.
When you use OracleAS TopLink CMP, define finder methods on the bean Home
interface, not in the entity bean itself. OracleAS TopLink CMP provides this functionality and offers several strategies to create and customize finders. The EJB container and OracleAS TopLink automatically generate the implementation.
The ejb-jar.xml
file contains the EJB entity bean information of a project, including definitions for any finders used for the beans. To create and maintain the ejb-jar.xml
file, use either a text editor or the OracleAS TopLink Sessions Editor.
The entity
tag encapsulates a definition for an EJB entity bean. Each bean has its own entity
tag that contains several other tags that define bean functionality, including bean finders.
Example 6-76 illustrates the structure of a typical finder defined within the ejb-jar.xml
file.
Example 6-76 A Simple Finder Within the ejb-jar.xml File
<entity>... <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>...</entity>
The entity
tag contains zero or more query
elements. Each query
tag corresponds to a finder method defined on the bean home or local Home
interface.
Note: You can share a single query between bothHome interfaces, as follows:
|
Here are the elements defined in the query
section of the ejb-jar.xml
file:
description
(optional): Provides a description of the finder.
query-method
: Specifies the method for a finder or ejbSelect
query.
method-name
: Specifies the name of a finder or select method in the entity bean implementation class.
method-params
: Contains a list of the fully-qualified Java type names of the method parameters.
method-param
: Contains the fully-qualified Java type name of a method parameter.
result-type-mapping
(optional): Specifies how to map an abstract schema type returned by a query for an ejbSelect
method. You can map the type to an EJBLocalObject
or EJBObject
type. Valid values are Local or Remote
ejb-ql
: Used for all EJB QL finders. It contains the EJB QL query string that defines the finder or ejbSelect
query. Leave this element empty for non-EJB QL finders.
Call finders enable you to create queries dynamically and generate the queries at runtime rather than deployment time. Call finders pass an OracleAS TopLink SQLCall
or StoredProcedureCall
as a parameter and return an Enumeration
.
OracleAS TopLink provides the implementation for Call finders. To use this feature in a bean, add the following finder definition to the Home
interface of your bean.
public Enumeration findAll(Call call) throws RemoteException, FinderException;
When you execute a Call finder, OracleAS TopLink creates the call on the client using the OracleAS TopLink interface oracle.toplink.queryframework.Call
. This call has three implementors: EJBQLCall
, SQLCall
and StoredProcedureCall
.
To define finder query logic, use OracleAS TopLink expressions. Expression finders support dynamic queries that you generate at runtime rather than deployment time. To use an expression finder, pass the expression as a parameter to a finder that returns an Enumeration
.
EJB QL is the standard query language defined in the EJB 2.0 specification. OracleAS TopLink supports EJB QL for both EJB 1.1 and EJB 2.0 beans. EJB QL finders enable you to specify an EJB QL string as the implementation of the query.
EJB QL offers several advantages:
It is the EJB 2.0 standard for queries.
You can use it to construct most queries.
You can implement dependent object queries with it.
The disadvantage of EJB QL is that it is difficult to use when you construct complex queries.
To create an EJB QL finder under EJB 1.1:
Declare the finder on the remote
interface.
Start OracleAS TopLink Mapping Workbench.
Choose the Queries > Finders > Named Queries tab for the bean.
Add a finder and give it a name that matches the method name you declared in Step 1.
Set the required parameters.
Set Query Format to EJB QL, and enter the EJB QL query in the Query String field.
To create an EJB QL finder under EJB 2.0:
Declare the finder on either the localHome
or the remoteHome
interface.
Start OracleAS TopLink Mapping Workbench.
Re-import the ejb-jar.xml
file to synchronize the project to the file.
OracleAS TopLink Mapping Workbench synchronizes changes between the project and the ejb-jar.xml
file.
The following is an example of a simple EJB QL query that requires one parameter. In this example, the question mark (Ò?Ó) in?1
specifies a parameter.
SELECT OBJECT(employee) FROM Employee employee WHERE (employee.name =?1)
To create an EJB QL finder for a CMP bean:
Declare the finder in the ejb-jar.xml
file, and enter the EJB QL string in the ejb-ql
tag.
Declare the finder on the Home
interface, the localHome
interface, or both, as required.
Start OracleAS TopLink Mapping Workbench.
Specify the ejb-jar.xml
file location and choose File > Updated Project from the ejb-jar.xml
file to read in the finders.
Choose the Queries > Finders > Named Queries tab for the bean.
Add a finder, and give it the same name as the finder you declared on your bean's home. Then add any required parameters.
Select and configure the finder.
The following is an example of a simple EJB QL query that requires one parameter. In this example, the question mark (Ò?Ó) in?1
specifies a parameter.
SELECT OBJECT(employee) FROM Employee employee WHERE (employee.name =?1)
To execute a query normally, you supply either a reference class or a SELECT
clause.
The basic API for a ReadAll
query with EJB QL is:
ReadAllQuery setEJBQLString("...")
Example 6-80 ReadAllQuery Using EJB QL
ReadAllQuery theQuery = new ReadAllQuery(); theQuery.setReferenceClass(EmployeeBean.class); theQuery.setEJBQLString("SELECT OBJECT(emp) FROM EmployeeBean emp"); … Vector returnedObjects = (Vector)aSession.executeQuery(theQuery);
Example 6-81 ReadAllQuery Using EJB QL and Passing Arguments
This code creates, populates, and passes a vector of arguments into the executeQuery
method
// First define the query ReadAllQuery theQuery = new ReadAllQuery(); theQuery.setReferenceClass(EmployeeBean.class); theQuery.setEJBQLString("SELECT OBJECT(emp) FROM EmployeeBean emp WHERE emp.firstName = ?1"); theQuery.addArgument("1"); ... // Next define the Arguments Vector theArguments = new Vector(); theArguments.add("Bob"); ... // Finally execute the query passing in the arguments Vector returnedObjects = (Vector)aSession.executeQuery(theQuery, theArguments);
When you execute EJB QL directly against the session, it returns a vector of the objects specified by the reference class. The basic API is as follows:
aSession.readAllObjects(<ReferenceClass>, <EJBQLCall>)
Example 6-82 EJB QL Session Query
/* <EJBQLCall> is the EJBQL string to be executed and <ReferenceClass> is the return class type */ // Call ReadAllObjects on a session. Vector theObjects = (Vector)aSession.readAllObjects(EmployeeBean.class, new EJBQLCall( "SELECT OBJECT (emp) from EmployeeBean emp));
You can use custom SQL code to specify finder logic. SQL enables you to implement logic that may not be possible to express with OracleAS TopLink expressions or EJB QL.
To create a SQL finder:
Declare the finder in the ejb-jar.xml
file, and leave the ejb-ql
tag empty.
Start OracleAS TopLink Mapping Workbench.
Specify the ejb-jar.xml
file location and choose File > Updated Project from the ejb-jar.xml
file to read in the finders.
Choose the Queries > Named Queries tab for the bean.
Select the finder, check the SQL radio button, and enter the SQL string.
Configure the finder.
The following is an example of a simple SQL finder that requires 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)
OracleAS TopLink provides several predefined finders you can use to execute dynamic queries, in which the logic is determined by the user at runtime. The OracleAS TopLink runtime reserves the names for these finders; they cannot be reused for other finders.
The predefined finders are:
EJBObject findOneByEJBQL(String ejbql, Vector args) Collection findManyByEJBQL(String ejbql, Vector args) EJBObject findOneBySQL(String sql, Vector args) Collection findManyBySQL(String sql, Vector args) EJBObject findOneByQuery(DatabaseQuery query, Vector args) Collection findManyByQuery(DatabaseQuery query, Vector args)
Note: With EJB 2.0, if the finder is located on a local home, then replaceEJBObject with EJBLocalObject in finders that contain findOneby .
|
You can also use each of these finders without a vector of arguments. For example, EJBObject findOneByEJBQL(String ejbql)
is a valid dynamic finder, but you must replace the return type of EJBObject
with your bean's component interface.
Declare the finder in the ejb-jar.xml
file, and leave the ejb-ql
tag empty.
Declare the finder on the Home
interface, the localHome
interface, or both, as required.
Start OracleAS TopLink Mapping Workbench.
Specify the ejb-jar.xml
file location and choose File > Updated Project from the ejb-jar.xml
file to read in the finders.
Go to the Queries > Named Queries tab for the bean.
Select and configure the finder.
Notes: If the advanced query options in "Advanced Finder Options" are not required, then you need only complete steps 1 and 2.Do not configure any query options for the |
In addition to the preceding dynamic finder, OracleAS TopLink provides a default findAll
query that returns all the beans of a given type. As with other dynamic finders, the OracleAS TopLink runtime reserves the name findAll
.
For more information about defining and configuring the finder, see "Creating a Dynamic Finder".
OracleAS TopLink creates the findByPrimaryKey
finder to a bean class when the class initializes. You can configure the findByPrimaryKey
finder with the various OracleAS TopLink query options.
Because the EJB 2.0 specification requires the container to implement the findByPrimaryKey
call on each bean Home
interface, do not delete this finder from a bean.
ReadAll finders enable you to create dynamic queries that you generate at runtime rather than deployment time. To use a ReadAll finder, pass an OracleAS TopLink ReadAllQuery
as a parameter to a finder that returns an Enumeration
.
OracleAS TopLink provides an implementation for ReadAll finders. To use this feature in a bean, add the following finder definition to the Home
interface of your bean.
public Enumeration findAll(ReadAllQuery query) throws RemoteException, FinderException;
To execute a ReadAll finder, create the query on the client.
To optimize performance, choose the finder type that best suits your needs.
Using OracleAS TopLink expressions offers the following advantages:
Version controlled, standardized queries to Java code
Ability to simplify most complex operations
A more complete set of querying features than is available through EJB QL
Because expressions enable you to specify finder search criteria based on the object model, they are frequently the best choice for constructing your finders.
For more information about implementing finders using OracleAS TopLink expressions, see "Expression Finders".
Redirect finders enable you to implement a finder that is defined on an arbitrary helper class as a static method. When you invoke the finder, OracleAS TopLink redirects the call to the specified static method.
Redirect queries are complex and require an extra helper method to define the query. However, because they support complex logic, they are often the best choice when you need to implement logic unrelated to the bean on which the redirect method is called.
Using SQL to define a finder offers the following advantages:
SQL enables you to implement logic that cannot be expressed when you use EJB QL or the OracleAS TopLink expression framework.
It allows for the use of a stored procedure instead of OracleAS TopLink generated SQL.
There may be cases in which custom SQL will improve performance.
SQL finders also have the following disadvantages:
Writing complex custom SQL statements requires a significant maintenance effort if the database tables change.
The hard coded SQL limits portability to other databases.
No validation is performed on the SQL String. Errors in the SQL will not be detected until runtime.
The use of SQL for a function other than SELECT
may result in unpredictable errors.
The ejbSelect method is a query method intended for internal use within an entity bean instance. Specified on the abstract bean itself, the ejbSelect
method is not directly exposed to the client in the home or component interface. Defined as abstract, each bean can include zero or more such methods.
Select methods have the following characteristics:
The method name must have ejbSelect
as its prefix.
It must be declared as public.
It must be declared as abstract.
The throws
clause must specify the javax.ejb.FinderException
, although it may also specify application-specific exceptions as well.
Under EJB 2.0, the result-type-mapping
tag in the ejb-jar.xml
file determines the return type for ejbSelects
. Set the flag to Remote
to return EJBObjects
; set it to Local
, to return EJBLocalObjects
.
The format for an ejbSelect
method definition looks like this:
public abstract type ejbSelect<METHOD>(...);
The ejbSelect
query return type is not restricted to the entity bean type on which the ejbSelect
is invoked. Instead, it can return any type corresponding to a container-managed relationship or container-managed field.
Although 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. This creates a query that is logically scoped to a particular entity bean instance.
To create an ejbSelect:
Declare the ejbSelect
in the ejb-jar.xml
file, enter the EJB QL string in the ejb-ql
tag, and specify the return type in the result-type-mapping
tag (if required).
Declare the ejbSelect
on the abstract bean class.
Start OracleAS TopLink Mapping Workbench.
Specify the ejb-jar.xml
file location, and choose File > Updated Project from the ejb-jar.xml
file to read in the finders.
Choose the Queries > Named Queries tab for the bean.
Select and configure the ejbSelect
query.
The default finder configuration is appropriate for most applications. However, finders also allow for several advanced configuration options.
You can apply various configurations to the underlying query to achieve the correct caching behavior for the application. Several ways are available to control the caching options for queries. For most queries, you can set caching options with OracleAS TopLink Mapping Workbench. For more information, see ÒCaching objectsÓ in the Oracle Application Server TopLink Mapping Workbench User's Guide.
You can set the caching options on a per-finder basis. Table 6-8 lists the valid values.
Table 6-8 Finder Caching Options
This Setting . . . | Causes Finders to . . . | When the Search Involves a Finder That . . . |
---|---|---|
ConformResultsInUnitOfWork (default)
|
Check the Unit of Work cache before querying the session cache or the database. The results of the finder always conform to uncommitted new objects, deleted objects, and changed objects. | Returns either a single bean or a collection. |
DoNotCheckCache
|
Query the database, bypassing the OracleAS TopLink internal caches. | Returns either a single bean or a collection. |
CheckCacheByExactPrimaryKey
|
Check the session cache for the object. | Contains only a primary key, and returns a single bean. |
CheckCacheByPrimaryKey
|
Check the session cache for the object. | Contains a primary key (and may contain other search parameters), and returns a single bean. |
CheckCacheThenDatabase
|
Search the session cache before accessing the database | Returns a single bean. |
CheckCacheOnly
|
Search against the session cache, but not the database. | Returns either a single bean or a collection. |
For more information about the OracleAS TopLink queries as well as the OracleAS TopLink Unit of Work and how it integrates with JTS, see Chapter 7, "Transactions".
Note: To apply caching options to finders with manually created (findOneByQuery , findManyByQuery ) queries, use the OracleAS TopLink API.
|
By default, OracleAS TopLink adds all returned objects to the session cache. However, if you know the set of returned objects is large, and you want to avoid the expense of storing these objects, you can disable this behavior. To override the default configuration, implement the dontMaintainCache()
call on the query, or disable returned object caching for the query in OracleAS TopLink Mapping Workbench.
For more information about disabling caching for returned finder results, see the Oracle Application Server TopLink Mapping Workbench User's Guide.
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 OracleAS TopLink Mapping Workbench) causes the query to refresh the nonprimary key attributes of the object with the returned information. This occurs on findByPrimaryKey
finders as well as all expression and SQL finders for the bean.
If you build a query in Java code, you can set this option by including the refreshIdentityMapResult()
method. This method automatically cascades changes to privately owned parts of the beans. If you require different behavior, then configure the query using a dynamic finder instead.
Caution: When you invoke this option from within a transaction, the refresh overwrites object attributes, including any that have not yet been written to the database. |
If your application includes an OptimisticLock
field, then use the refresh cache option in conjunction with the onlyRefreshCacheIfNewerVersion()
option. This ensures that the application refreshes objects in the cache 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.
Large result sets can be resource intensive to collect and process. To give the client more control over the returned results, configure OracleAS TopLink finders to use cursors. This combines OracleAS TopLink CursoredStream
with the ability of the database to cursor data, and breaks up the result set into smaller, more manageable pieces.
The behavior of a finder including a cursored stream differs from other finder as follows:
Only the elements requested by the client are sent to the client.
Nothing is cached on the client in the CursoredEnumerator
.
If you use the transactional attribute REQUIRED for your entity bean, then you must wrap all reads in a UserTransaction begin()
and commit()
to ensure that reads beyond the first page of the cursor have a transaction in which to work.
You can configure any finder that returns a java.util.Enumeration
(under EJB 1.1) or a java.util.Collection
(under EJB 2.0) to use a cursor. When you create the query for the finder, add the useCursoredStream()
option to enable cursoring.
Example 6-84 Cursored Stream in a Finder
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", query);
OracleAS TopLink offers additional elements for traversing finder results. These elements include:
hasMoreElements()
: Returns a boolean indicating whether there are any more elements in the result set.
nextElement()
: Returns the next available element.
nextElements(int count)
: Retrieves a Vector
of at most count
elements from the available results, depending on how many elements remain in the result set.
close()
: Closes the cursor on the server. The client must send this message, or the database connection does not close.
Example 6-85 illustrates client-code executing a cursored finder.
Example 6-85 Cursored Finder Under EJB 1.1
import oracle.toplink.ejb.cmpwls11. 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();
As with EJB 1.1, OracleAS TopLink offers additional elements for traversing finder results under EJB 2.0. These elements include:
isEmpty()
: As with java.util.Collection
, isEmpty()
returns a boolean indicating whether the Collection
is empty.
size()
: As with java.util.Collection
, size()
returns an integer indicating 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
.
OracleAS TopLink also offers an extended protocol for oracle.toplink.ejb.cmp.wls.CursoredIterator
(based on java.util.Iterator
):
close()
: Closes the cursor on the server. The client must send this message to close the database connection.
hasNext()
: Returns a boolean indicating whether any more elements are in the result set.
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 remain in the result set.
Example 6-86 illustrates client code executing a cursored finder.
Example 6-86 Cursored Finder Under EJB 2.0
//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();