Skip Headers
Oracle® Application Server TopLink Application Developer's Guide
10g Release 2 (10.1.2)
Part No. B15901-01
  Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Previous
Previous
Next
Next
 

Executing Queries

OracleAS TopLink provides several options to execute queries, including:

Session Queries

The Session class and its subclasses (including DatabaseSession and UnitOfWork) provide methods to read, create, modify, and delete objects stored in a database. These methods, known as query methods, enable you to create queries against the object model. Session queries are easy to use and are flexible enough to perform most database operations.

The DatabaseSession class provides direct support to read and modify the database by offering read, write, insert, update, and delete operations.

The UnitOfWork class also provides methods to modify data. The Unit of Work is a safer approach to data modification than the DatabaseSession methods, because it isolates changes until they are complete. Whenever possible, use the Unit of Work to write or update rather than the write, insert, update, and delete methods available in the database session.

For more information, see "Unit of Work Basics".

Reading Objects from the Database

The session provides the following methods to access the database:

  • The readObject() method reads a single object from the database. Use this method with a primary key when looking for a specific object.

  • The readAllObjects() method reads multiple objects from the database. Use this method to return a group of objects that match the selection criteria.

  • The refreshObject() method refreshes objects in the cache with data from the database.

Read Operation

The readObject() method retrieves a single object from the database. The application must specify the class of object to read. If no object matching the criteria is found, null is returned.

For example, the basic read operation is:

session.readObject(MyDomainObject.class);

This example returns the first instance of MyDomainObject that is found in the table used for MyDomainObject. OracleAS TopLink provides the Expression class to specify querying parameters for a specific object.

When you search for a single, specific object using a primary key, the readObject() method is more efficient than the readAllObjects() method because readObject() can find an instance in the cache without accessing the database. Because a readAllObjects() operation does not know how many objects match the criteria, it always searches the database to find matching objects, even if it finds matching objects in the cache.

Example 6-37 readObject() Using an Expression

import oracle.toplink.sessions.*;
import oracle.toplink.expressions.*;

/* Use an expression to read in the Employee whose last name is Smith. Create an expression using the Expression Builder and use it as the selection criterion of the search */
Employee employee = (Employee) session.readObject(Employee.class, new ExpressionBuilder().get("lastName").equal("Smith"));

Read All Operation

The readAllObjects() method retrieves a Vector of objects from the database and does not order the returned objects. If the query does not find any matching objects, it returns an empty Vector.

Specify the class for the query. You can also include an expression to define more complex search criteria, as illustrated in Example 6-38.

Example 6-38 readAllObjects() Using an Expression

// Returns a Vector of employees whose employee salary > 10000
Vector employees = session.readAllObjects(Employee.class,new ExpressionBuilder.get("salary").greaterThan(10000));

Refresh Operation

The refreshObject() method causes OracleAS TopLink to update the object in memory with data from the database. This operation refreshes any privately owned objects as well.


Note:

A privately owned object is one that cannot exist without its parent, or source object.

Writing Objects to the Database

The Unit of Work provides the safest mechanism for writing objects in most OracleAS TopLink applications. However, when you can safely write directly to the database (for example, in a single-user or a two-tier application), session methods are the most efficient database writing tool. Database session provides the following methods to write to a database:

  • writeObject()

  • writeAllObjects()

  • insertObject()

  • updateObject()

  • deleteObject()

Writing a Single Object to the Database

When you invoke the writeObject() method, the method performs a does-exist check to determine whether an object exists. If the object exists, then writeObject() updates the object; if it does not exist, then writeObject() inserts a new object.

The writeObject() method writes privately owned objects in the correct order to maintain referential integrity.

Call the writeObject() method when you cannot verify that an object exists on the database.

Example 6-39 Writing a Single Object Using writeObject()

//Create an instance of employee and write it to the database
Employee susan = new Employee();
susan.setName("Susan");
...
//Initialize the susan object with all other instance variables
session.writeObject(susan); 

Writing All Objects to the Database

You can call the writeAllObjects() method to write multiple objects to the database. The writeAllObjects() method performs the same does-exist check as the writeObject() method and then performs the appropriate insert or update operations.

Example 6-40 Writing Several Objects Using writeAllObjects()

// Read a Vector of all the current employees in the database.
Vector employees = (Vector) session.readAllObjects(Employee.class);
...//Modify any employee data as necessary
//Create a new employee and add it to the list of employees
Employee susan = new Employee();
...
//Initialize the new instance of employee
employees.add(susan);
/* Write all employees to the database. The new instance of susan which is not currently in the database will be inserted. All the other employees which are currently stored in the database will be updated */
session.writeAllObjects(employees);

Adding New Objects to the Database

The insertObject() method creates a new object on the database, but does not perform the does-exist check before it attempts the insert operation. The insertObject() method is more efficient than the writeObject() method if you are certain that the object does not yet exist on the database. If the object does exist, the database throws an exception when you execute the insertObject() call.

Modifying Existing Objects in the Database

The updateObject() method updates existing objects in the database, but does not perform the does-exist check before it attempts the update operation. The updateObject() is more efficient than the writeObject()method if you are certain that the object does exist in the database. If the object does not exist, the database throws an exception when you execute the updateObject() call.

Deleting Objects in the Database

To delete an OracleAS TopLink object from the database, read the object from the database and then call the deleteObject() method. This method deletes both the specified object and any privately owned data.

Query Objects

Query objects are the standard devices OracleAS TopLink uses to interact with the database. They support database commands such as create, read, update, and delete, and accept search criteria specified in several ways, including OracleAS TopLink expressions.

OracleAS TopLink provides you with direct access to query objects, which support more complex queries than the session query API. You can build custom query objects to improve application performance or to support complex queries. Use the custom query object classes you create with the session or a descriptor's query manager to:

  • Create new query operations.

  • Create named queries registered with the session.

  • Customize thedefault database operations of the session, such as readObject() and writeObject().

OracleAS TopLink Mapping Workbench provides graphical tools to create query objects. Although this section discusses query objects in the context of Java code, we recommend that you create query objects in OracleAS TopLink Mapping Workbench.

Query Object Components

OracleAS TopLink uses query objects to store information about a database query. A complete query object stores information about:

  • The query type, specified by the query object class

  • The class that the query accesses (the reference class)

  • The query execution, which can be through SQL, a database call or an OracleAS TopLink expression

Creating a Query Object

The following steps illustrate how to create a query object in Java code.


Step 1 Specify the query type to initialize the query object

To execute a query, select one of the following query object classes:

  • ReadAllQuery: Reads a collection of objects

  • ReadObjectQuery: Reads a single object

  • ReportQuery: Reads information about objects

  • DeleteObjectQuery: Removes an object from the database

  • InsertObjectQuery: Inserts new objects into the database

  • UpdateObjectQuery: Updates existing objects

  • WriteObjectQuery: Writes an object to the database, either with an insert (for new objects) or an update (for existing objects)

To execute SQL expressions, use the following query object classes:

  • ValueReadQuery: Returns a single data value

  • DirectReadQuery: Returns a collection of column values; can be used for direct collection queries

  • DataReadQuery: Executes a SQL SELECT, returns a collection of database row (map) objects

  • DataModifyQuery: Executes a nonselecting SQL string

Step 2 Set the reference class

The reference class specifies the class against which the query runs. Use the setReferenceClass() call to select a searchable class.

Step 3 For read queries, configure the query for execution

To specify how a query executes, call one of the following methods:

  • setSelectionCriteria(): Passes an expression to the query object

  • setSQLString(): Passes a SQL string

  • setCall(): Passes a database call

This setting is optional. If you do not specify read criteria, a ReadAllQuery returns every object of the reference class in the database, and a ReadObjectQuery returns the first object it encounters.

Step 4 Add query arguments

You can pass arguments to the query object by calling addArgument() in addition to the executeQuery() method. Arguments describe the objects for the query to return.

Step 5 Register the query object with the session

After initialization, use the addQuery() method to register the query object with the session. Name the query when you register it, which enables you to call the query by name. The session then manages the query for you.

Registering the query object with the session is optional. If you do not register the query object, then specify the entire query every time you execute it, or manage it manually outside of the session.

Step 6 Execute the query

To execute the query, use the executeQuery() call to call the object by name. As required, provide values for any defined arguments.

Read Query Object Examples

Although query objects support writing to a database, reading is their most common use. This section provides several examples of the use of query objects for reading the database.

Example 6-41 illustrates a simple read query. It uses an OracleAS TopLink expression, but does not use its own arguments for the query. Instead, it relies on the search parameters the expression provides. This example builds the expression within its code, but does not register the query with the session.

Example 6-41 A Simple ReadAllQuery

// This example returns a Vector of employees whose employee ID is > 100.

// Initialize the query object by specifying the query type
ReadAllQuery query = new ReadAllQuery();

//Set the reference class for the query.
query.setReferenceClass(Employee.class);

/* Configure the query execution. Because this example uses an expression, it uses the setSelectionCriteria call */
query.setSelectionCriteria(new ExpressionBuilder.get("id").greaterThan(100)); 

// Execute the query
Vector employees = (Vector) session.executeQuery(query);

Example 6-42 illustrates a complex readObject query that uses all available configuration options.

Example 6-42 A Named Read Query with Two Arguments

// Define two expressions that map to the first and last name of the employee.
ExpressionBuilder emp = new ExpressionBuilder();
Expression firstNameExpression = emp.get("firstName").equal(emp.getParameter("firstName"));
Expression lastNameExpression = emp.get("lastName").equal(emp.getParameter("lastName"));

//Initialize the query object by specifying the query type
ReadObjectQuery query = new ReadObjectQuery();
//Set the reference class for the query.
query.setReferenceClass(Employee.class);
/* Configure the query execution. Because this example uses an expression, it uses the setSelectionCriteria call */
query.setSelectionCriteria(firstNameExpression.and(lastNameExpression)); 
//Specify the required arguments for the query.
query.addArgument("firstName");
query.addArgument("lastName");

// Add the query to the session.
session.addQuery("getEmployeeWithName", query);

/* Execute the query by referencing its name and providing values for the specified arguments */
Employee employee = (Employee) session.executeQuery("getEmployeeWithName","Bob","Smith");

Specialized Query Object Options

In addition to the query object configuration options discussed in "Creating a Query Object", several more specialized options are available for customizing query objects

Ordering for Read All Queries

Ordering is a common option for query objects. To order the collection of objects returned from a ReadAllQuery, use the addOrdering(), addAscendingOrdering(), or addDescendingOrdering() methods. You can apply order based on attribute names or query keys and expressions.

Example 6-43 A Query with Simple Ordering

// Retrieves objects ordered by lastName then firstName in Ascending Order 
ReadAllQuery query = new ReadAllQuery();
query.setReferenceClass(Employee.class);
query.addAscendingOrdering ("lastName");
query.addAscendingOrdering ("firstName");
Vector employees = (Vector) session.executeQuery(query);

Example 6-44 A Query with Complex Ordering

/* Retrieves objects ordered by Street Address, descending case-insensitive order of Cities, and manager's Last Name */
ReadAllQuery query = new ReadAllQuery();
query.setReferenceClass(Employee.class);
ExpressionBuilder emp = new ExpressionBuilder();
query.addOrdering (emp.getAllowingNull("address").get("street"));
query.addOrdering (emp.getAllowingNull("address").get("city").toUpperCase().descending());
query.addOrdering(emp.getAllowingNull("manager").get("lastName"));
Vector employees = (Vector) session.executeQuery(query);

Note the use of getAllowingNull, which creates an outer join for the address and manager relationships. This ensures that employees without an address or manager still appear in the list.

For more information, see "Join Reading".

Parameterized SQL in Query Objects

To enable the parameterized SQL on individual queries, use the bindAllParameters() and cacheStatement() methods. This causes OracleAS TopLink to use a prepared statement, binding all SQL parameters and caching the prepared statement. When you re-execute this query, you avoid the SQL preparation, which improves performance.

For more information, see Chapter 10, "Tuning for Performance".


Note:

Do not use OracleAS TopLink internal statement caching with an external connection pool.

Example 6-45 A Simple Read Query Object with Parameterized SQL

ReadObjectQuery query = new ReadObjectQuery(Employee.class);
query.setShouldBindAllParameters(true);
query.setShouldCacheStatement(true);

Collection Classes

By default, a ReadAllQuery returns its result objects in a vector. You can configure the query to return the results in any collection class that implements the Collection or Map interface.

Example 6-46 Specifying the Collection Class for a Collection

ReadAllQuery query = new ReadAllQuery(Employee.class);
query.useCollectionClass(LinkedList.class);
LinkedList employees = (LinkedList) getSession().executeQuery(query);

Example 6-47 Specifying the Collection Class for a Map

ReadAllQuery query = new ReadAllQuery(Employee.class);
query.useMapClass(HashMap.class, "getFirstName");
HashMap employees = (HashMap) getSession().executeQuery(query);

For more information about interfaces, see "Working with Interfaces" in the Oracle Application Server TopLink Mapping Workbench User's Guide.

Using Cursoring for a ReadAllQuery

The ReadAllQuery class includes methods for cursored stream and scrollable cursor support. If you expect the result set to be large, streams and cursors enable you to handle the result sets more efficiently.

For more information, see "Cursors and Streams".

Query Optimization

OracleAS TopLink supports both joins and batch reads to optimize database reads. When your query reads many objects, these techniques dramatically decrease the number of times you must access the database during a read operation. Use the addJoinedAttribute() and addBatchReadAttribute() methods to configure query optimization.

For more information, see "Query Object Performance Options", and Chapter 10, "Tuning for Performance".

Other options to optimize queries include the setMaxRows() method and partial object reading.

Maximum Rows Returned

You can limit a query to a specified maximum number of rows. Use this feature to avoid queries that can return an excessive number of objects.

To specify a maximum number of rows, use the setMaxRows method, and pass an integer that represents the maximum number of rows for the query.

Example 6-48 Setting the Maximum Returned Object Size

ReadAllQuery query = new ReadAllQuery();
query.setReferenceClass(Employee.class);
query.setMaxRows(5);
Vector employees = (Vector) session.executeQuery(query);

The setMaxRows method limits the number of rows the query returns, but does not enable you to acquire more records after the initial result set. If you want to browse the result set in fixed increments, use either cursors or cursored streams.

For more information, see "Java Streams" .

Partial Object Reading

OracleAS TopLink enables you to query for partial objects. For example, you can create a read query that returns a subset of an object's attributes, rather than the entire object. This option improves read performance when the full object is not required. For example, use partial object reading to create a list of objects from which the client chooses the required object.

When you use partial object reading, be aware that:

  • You cannot cache or edit partial objects.

  • OracleAS TopLink does not automatically include primary key information in a partially populated object. If you need primary key information (for example, if you want to re-query or edit the object), specify it as one of the required attributes.

Use the addPartialAttribute() method to configure partial object reading.

For more information, see "Query Object Performance Options", and Chapter 10, "Tuning for Performance".

Query Timeout

You can implement a timeout for query objects. This enables you to automatically abort a hung or lengthy query after the specified time elapses. OracleAS TopLink throws a DatabaseException after the timeout.

To specify a timeout, implement the setQueryTimeout() call, and pass the timeout interval as an integer representing the number of seconds before timeout occurs.

Example 6-49 Timeout on Query Objects

// Create the appropriate query and set timeout limits
ReadAllQuery query = new ReadAllQuery();
query.setReferenceClass(Employee.class);
query.setQueryTimeout(2);
try{
    Vector employees = (Vector)session.executeQuery(query);
} catch (DatabaseException ex) {
    // timeout occurs
}

Predefined Queries

Predefined queries enable you to create efficient, reusable queries. OracleAS TopLink creates predefined queries and registers them with a session or descriptor when the application starts. You can then retrieve the queries by name and execute them.

The most common way to create a predefined query is to register the query to a descriptor by specifying an amendment method with OracleAS TopLink Mapping Workbench for an after load event.

Predefined queries improve the performance of frequently called queries because when you create a query, it is saved and reused as required. Each time you use a query, you create three or more objects that OracleAS TopLink uses to build the SQL statement. If you use predefined queries, OracleAS TopLink creates these objects only once, at binding time. OracleAS TopLink stores the queries as SQL statements in the descriptor and makes them available for the duration of the session.

In addition to performance improvements, predefined queries add structure to a querying framework and give you more options for reading query structure from alternative sources, such as XML.

Named Queries

Named queries improve application performance, because they reduce the resources required to run a query.

The readAllObjects(Class c, Expression e) creates a ReadAllQuery, which builds the other objects it needs to perform its task. After you execute the readEmployeesMatchingLastName method, the query, expression, expressionBuilder, and any other related objects become garbage. Each time you call this method, OracleAS TopLink creates these related objects again, uses them once, and then discards them.

The use of named queries eliminates this behavior. To configure named queries, use a descriptor amendment method. This creates named queries when you open a database session.

Example 6-50 Named Query in the Descriptor File

public class MyTopLinkManager {
// some code that manages sessions, login, etc…
…
// This method is called by front end when needing to query on last names
   public Vector readEmployeesMatchingLastName(String theName) {
      ExpressionBuilder eBuilder = new ExpressionBuilder();
         Expression exp = eBuilder.get("lastName").like(theName+"%");
         return session.readAllObjects(Employee.class, exp);
     }
}

Use and Reuse

OracleAS TopLink stores named queries by name on a per descriptor basis. When the application needs a query, it calls the named query and passes the required arguments. Because OracleAS TopLink builds the query when it opens the database session, the query is immediately available. In addition, the query is named and bound to a descriptor, so it is reusable.

The first time you execute a named query, OracleAS TopLink calculates the core SQL based on your database platform and schema. OracleAS TopLink caches this information and reuses it if you reuse the query.

Centralized Query Management

OracleAS TopLink creates and registers named queries in a centralized location, usually your descriptor amendment method. Storing all queries in one location facilitates the reuse of queries and simplifies query maintenance.

When Not To Use Named Queries

Rarely used queries may be more efficient when built on an as-needed basis. If you seldom use a given query, it may not be worthwhile to build and store that query when you invoke a session.

Named Finders

A named finder is an OracleAS TopLink query registered with an EJB container under a specific name. When using named finders, the find method on the Home interface must correspond to the name of an OracleAS TopLink query registered with the container. To implement and register the query with the container, use an OracleAS TopLink descriptor amendment method or session amendment class.

Example 6-51 A Named Finder

/* The named finder in this example uses an OracleAS TopLink query named findCustomersInCity */
public Enumeration findCustomersInCity(String City)throws FinderException, RemoteException;

Before you build and implement the findCustomersInCity finder shown in Example 6-51, define the corresponding named query, and register it with the project descriptor. To build the named query, employ:

OracleAS TopLink Mapping Workbench Using EJB QL, SQL, or Expressions

Use EJB QL, SQL, or the OracleAS TopLink expression framework in OracleAS TopLink Mapping Workbench to:

  • Define the query in OracleAS TopLink Mapping Workbench. Specify the query in the Queries tab of the bean descriptor.

  • Add the query to the descriptor in a user-defined method.

Java Code Using the OracleAS TopLink Expression Framework

Use the OracleAS TopLink expression framework to add the query employing a user defined method. Define these methods in one of the following ways:

  • Use OracleAS TopLink Mapping Workbench to specify a descriptor amendment method on the bean descriptor (see Example 6-52).

  • Add a preLogin method to a session event listener class. Specify the session event listener classes using the event-listener-class element in the toplink-ejb-jar.xml descriptor (see Example 6-53).

Example 6-52 Define an Amendment Method

/* This example defines the findCustomersInCity query in the amendment method of the descriptor */
public static void amendment(Descriptor descriptor) {
// create a query...

descriptor.getQueryManager().addQuery("findCustomersInCity", query);

Example 6-53 Define a Pre-Login Event

/* This example defines the findCustomersInCity query in the preLogin method of a session event listener class and specifies the session event listener class in the toplink-ejb-jar.xml deployment descriptor */
public void preLogin(SessionEvent event) {
// create a query...
event.getSession().getDescriptor(Customer.class).getQueryManager().addQuery("findCustomersInCity", query);
}

OracleAS TopLink Expression Framework

To use the OracleAS TopLink expression framework, define the finder in OracleAS TopLink Mapping Workbench to specify the finder as a query object. Set the reference class to the name of the bean against which you run the query.

For more information, see "Query Objects".

If you build your finder in code, use the builder.getParameter() call to retrieve the arguments defined in the query. Use the arguments for comparison, combining them with various predicates and operators, such as equal(), like(), and anyOf().

Example 6-54 Using the OracleAS TopLink Expression Framework and Java Code

public static void addCustomerFinders(Descriptor descriptor) {
/* This code supports the query, Enumeration findCustomersInCity(String aCity)
Since this finder returns an Enumeration, it requires a ReadAllQuery. The finder is a "NAMED" finder that is registered with the QueryManager */
//1 Define the query.
ReadAllQuery query = new ReadAllQuery();
query.setName("findCustomersInCity");
query.addArgument("aCity");
query.setReferenceClass(CustomerBean.class);
//2 Use an expression
ExpressionBuilder builder = new ExpressionBuilder();
query.setSelectionCriteria
builder.get("city").like(builder.getParameter("aCity"));
/*3 You can set options on the query, such as  query.refreshIdentityMapResult(); */
//4 Register the query with the querymanager.
descriptor.getQueryManager().addQuery("findCustomersInCity",query);
}

Generic Named Finder

You can use a named query without the need to provide the matching implementation on the Home interface. To do this, use the Generic Named finder provided by OracleAS TopLink. This finder takes the name of the named query and a vector of arguments as parameters.

Example 6-55 The Generic Named Finder

public Enumeration findAllByNamedQuery(String queryName, Vector arguments) throws RemoteException, FinderException;

For more information about finders, see "EJB Finders".

Redirect Queries

To perform complex operations, you can combine query redirectors with the OracleAS TopLink query framework. To create a redirector, implement the oracle.toplink.queryframework.QueryRedirector interface. The query mechanism executes the Object invokeQuery(DatabaseQuery query, DatabaseRow arguments, Session session) method and waits for the results.

OracleAS TopLink provides one pre-implemented redirector, the MethodBasedQueryRedirector method. To use this redirector, create a static invoke method on a class, and use the setMethodName(String) call to specify the method to invoke.

Example 6-56 Redirect Query

ReadObjectQuery query = new ReadObjectQuery(Employee.class);
query.setName("findEmployeeByAnEmployee");
query.addArgument("employee");

MethodBaseQueryRedirector redirector = new MethodBaseQueryRedirector(QueryRedirectorTest.class, "findEmployeeByAnEmployee");
query.setRedirector(redirector);
Descriptor descriptor = getSession().getDescriptor(query.getReferenceClass());
descriptor.getQueryManager().addQuery(query.getName(), query);

Vector arguments = new Vector();
arguments.addElement(employee);
objectFromDatabase = getSession().executeQuery(query,arguments);

public class QueryRedirectorTest{
public static Object findEmployeeByAnEmployee(DatabaseQuery query,    oracle.toplink.publicinterface.DatabaseRow arguments, oracle.toplink.sessions.Session session) {
    ((ReadObjectQuery) query).setSelectionObject(arguments.get("employee"));
    return session.executeQuery(query);
}
}

EJBs and Redirect Finders

Redirect finders enable you to specify a finder in which the implementation is defined as a static method on an arbitrary helper class. When you invoke the finder, it redirects the call to the specified static method.

The finder can have any arbitrary parameters. If the finder includes parameters, then OracleAS TopLink packages them into a vector and passes them to the redirect method.

Advantages

Because you define the redirect finder implementation independently from the bean that invokes it, you can build the redirect finder to accept any type and number of parameters. This enables you to create a generic redirect finder that accepts several different parameters and return types, depending on input parameters.

A common strategy for using redirect finders is to create a generic finder that:

  • Includes logic to perform several tasks

  • Reads the first passed parameter to identify the type of finder requested and select the appropriate logic

The redirect method contains the logic required to extract the relevant data from the parameters and uses it to construct an OracleAS TopLink query.

Disadvantages

Redirect finders are complex and can be 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 file, and leave 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.

    For more information, see "Customizing OracleAS TopLink Descriptors with Amendment Methods".

  4. Start OracleAS TopLink Mapping Workbench.

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

  6. Specify the class and name of the static method to enable the amendment method for the descriptor.

The amendment method then adds a query to the descriptor query manager, as follows:

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

The redirect method must return either a single entity bean (object) or a vector. Here are the possible method signatures:

public static Object redirectedQuery2(oracle.toplink.sessions.Sessions, Vector args)

and

public static Vector redirectedQuery4(oracle.toplink.sessions.Sessions, Vector args)

When you implement the query method, ensure that the method returns the correct type. For methods that return more than one bean, set the return type to java.util.Vector. OracleAS TopLink converts this result to java.util.Enumeration (or Collection) if required.


Note:

The redirect method also interprets an OracleAS TopLink session as a parameter. For more information about an OracleAS TopLink session, see Chapter 4, "Sessions".

At runtime, the client invokes the finder from the entity bean home and packages the arguments into the args vector in order of appearance from the finder method signature. The client passes the vector to the redirect finder, which uses it to execute an OracleAS TopLink expression.

Example 6-57 A Simple Redirect Query Implementation

public class RedirectorTest {
    private Session session;
    private Project project;
    public static void main(String args[]) {

       RedirectorTest test = new RedirectorTest();

       test.login();

            try {
            // Create the arguments to be used in the query
                Vector arguments = new Vector(1);
                arguments.add("Smith");

            // Run the query
                Object o = test.getSession()
                .executeQuery(test.redirectorExample(), arguments);
                o.toString();
            }
            catch (Exception e) {
                System.out.println("Exception caught -> " + e);
                e.printStackTrace();
            }
    }

    public ReadAllQuery redirectorExample() {

       // Create a redirector
       MethodBasedQueryRedirector redirector = new MethodBasedQueryRedirector();

       // Set the class containgin the public static method
       redirector.setMethodClass(RedirectorTest.class);

       // Set the name of the method to be run
       redirector.setMethodName("findEmployeeByLastName");

       // Create a query and add the redirector created above
       ReadAllQuery readAllQuery = new ReadAllQuery(Employee.class);
       readAllQuery.setRedirector(redirector);
       readAllQuery.addArgument("lastName");

       return readAllQuery;
    }
    //Call the static method
    public static Object findEmployeeByLastName(oracle.toplink.sessions
      .Session
    session, Vector arguments) {

       // Create a query
       ReadAllQuery raq = new ReadAllQuery();
       raq.setReferenceClass(Employee.class);
       raq.addArgument("lastName");

       // Create the selection criteria
       ExpressionBuilder employee = new ExpressionBuilder();
       Expression whereClause =
       employee.get("lastName").equal(arguments.firstElement());

       // Set the selection criteria
       raq.setSelectionCriteria(whereClause);

       return (Vector)session.executeQuery(raq, arguments);
    }
[...]
}

Queries Defined with OracleAS TopLink Mapping Workbench

You can define several types of queries with OracleAS TopLink Mapping Workbench, including custom SQL queries and named queries (which you can build using OracleAS TopLink expressions, EJB QL, or SQL).

For more information about the features and options available to create queries with OracleAS TopLink Mapping Workbench, see "Understanding Descriptors," in the Oracle Application Server TopLink Mapping Workbench User's Guide.

Query Managers

A query manager is a descriptor-owned object that controls descriptor access to the database. The query manager generates its own SQL to access the database in a transparent manner.

You can modify the query manager to do the following:

Customize the Default Query Methods

Query managers generate SQL for five database actions:

  • Insert

  • Update

  • Delete

  • Read

  • Read all

The OracleAS TopLink session class provides default query objects to perform these database functions. However, you can also use the query manager to provide custom query objects or SQL strings to perform these functions.

For example, to replace the OracleAS TopLink readObject function with a stored procedure call, specify the replacement code in OracleAS TopLink Mapping Workbench. If you use a Sybase database, the stored procedure call to read an object looks like this:

EXEC PROC Read_Employee(@EMP_ID = 4653)

To implement this replacement code, add the following string to read the object:

EXEC PROC Read_Employee(@EMP_ID = #EMP_ID)

In the deployed project, the query manager substitutes the code you specified for the readObject call in any queries that include this call.

For more information about customizing default query methods in OracleAS TopLink Mapping Workbench, see "Custom SQL Queries" in the Oracle Application Server TopLink Mapping Workbench User's Guide.

Customize the Default Query Methods in Java Code

To customize the query manager database access methods in Java code, use the getQueryManager() method to invoke the query manager. To change the default database access queries, use an amendment method listed in Table 6-5.

Table 6-5 Query Manager Methods for Database Access

To Change the Default Use This Query Manager Method
Delete call using a query setDeleteQuery (DeleteObjectQuery query)
Delete call using SQL setDeleteSQLString (String sqlString)
Insert call using a query setInsertQuery (InsertObjectQuery query)
Insert call using SQL setInsertSQLString (String sqlString)
ReadAll call using a query setReadAllQuery (ReadAllQuery query)
ReadAll call using SQL setReadAllSQLString (String sqlString)
ReadObject call using a query setReadObjectQuery (ReadObjectQuery query)
ReadObject call using SQL setReadObjectSQLString (String sqlString)
Update call using a query setUpdateQuery (UpdateObjectQuery query)
Update call using SQL setUpdateSQLString (String sqlString)


Note:

When you customize the update function for an application that uses optimistic locking, the custom update string must not write the object if the row version field has changed since the initial object was read. In addition, it must increment the version field if it writes the object successfully.

For example:

update Employee set F_NAME = #F_NAME, VERSION = VERSION + 1 where (EMP_ID = #EMP_ID) AND (VERSION = #VERSION)

The update string must also maintain the row count of the database.


Define Additional Join Expressions

You can set the query manager to automatically append an expression to every query it performs on a class. For example, you can add an expression that filters the database for the valid instances of a given class.

Use this to:

  • Filter logically deleted objects.

  • Enable two independent classes to share a single table without inheritance.

  • Filter historical versions of objects.

The query manager provides the setAdditionalJoinExpression() and the setMultipleTableJoinExpression() methods for this purpose.

Example 6-58 Registering a Query that Includes a Join Expression

/* The join expression in this example filters invalid instances of employee from the query */
public static void addToDescriptor(Descriptor descriptor)
{
    ExpressionBuilder builder = new ExpressionBuilder();
    descriptor.getQueryManager().setAdditionalJoinExpression((builder.getField("EMP.STATUS
      ").notEqual("DELETED")).and(builder.getField("EMP.STATUS").notEqual("HISTORICAL")));

}

Customize the Existence Check

When OracleAS TopLink writes an object to the database, OracleAS TopLink runs an existence check to determine whether to perform an insert or an update.

The query manager enables you to substitute custom logic for the existence check.

For more information on how to implement a custom existence check, see "Specifying Identity Mapping" in the Oracle Application Server TopLink Mapping Workbench User's Guide.

Use the following DescriptorQueryManager methods to modify the default existence checking:

checkCacheForDoesExist()
assumeExistenceForDoesExist()
assumeNonExistenceForDoesExist()
checkDatabaseForDoesExist()
setDoesExistQuery(DoesExistQuery)
setDoesExistSQLString(String)