Oracle® Application Server TopLink Application Developer's Guide
10g Release 2 (10.1.2) Part No. B15901-01 |
|
Previous |
Next |
OracleAS TopLink provides several options to execute queries, including:
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".
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.
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"));
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.
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()
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.
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);
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.
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.
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.
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
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:
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:
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
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.
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");
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 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".
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. |
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.
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".
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.
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" .
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".
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 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 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); } }
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.
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.
/* 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:
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.
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); }
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); }
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".
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); } }
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.
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.
Redirect finders are complex and can be difficult to configure. They also require an extra helper method to define the query.
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.
Create an amendment method.
For more information, see "Customizing OracleAS TopLink Descriptors with Amendment Methods".
Start OracleAS TopLink Mapping Workbench.
Choose Advanced Properties > After Load from the menu for the bean.
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); } [...] }
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.
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:
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.
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. |
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"))); }
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)