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
 

OracleAS TopLink Reading Optimization Features

You can optimize certain read and write operations in an OracleAS TopLink application. To optimize reading, you can tune:

OracleAS TopLink provides the read optimization features listed in Table 10-2.

Table 10-2 Read Optimization Features

Feature Function Performance Technique
Unit of Work Tracks object changes within the Unit of Work. To minimize the amount of tracking required, register only those objects that will change.
Object indirection Uses valueholders as a stand-in for domain objects. Valueholders can provide a major performance benefit, because they minimize database reads.
Soft cache weak identity map Offers client-side caching for objects read from database, and drops objects from the cache when memory becomes low. Reduces database calls and improves memory performance.
Weak identity map Offers client-side caching for objects. Reduces database access and maintains a cache of all referenced objects.
Full identity map Offers client side caching for objects. Avoids database calls for objects that have already been read.

Limit the cache size. A large cache can impact system performance.

Cache identity map Offers a fixed size client side cache. Leverages a moderate caching strategy, and controls the impact on memory.
No identity map Disables cache lookup. Useful if you prefer database access over cached objects.
Batch reading and joining Reduces database access by batching many queries into a single query that reads more data. Dramatically reduces the number of database accesses required to perform a READ query.
Partial object reading Allows reading of a subset of a result set of the object attributes. Reduces the amount of data read from the database at any one time. Reducing connection time for each read improves performance.
Report query Similar to partial object reading, but returns only the data instead of the objects. Supports complex reporting functions such as aggregation and group-by functions. Also enables you to compute complex results on the database, instead of reading the objects into the application and computing the results locally.

Reading Case 1: Displaying Names in a List

An application may ask the user to choose an element from a list. Because the list displays only a subset of the information contained in the objects, it is not necessary to query for all information for objects from the database.

Partial object reading and report query are two OracleAS TopLink features that optimize these types of operations. They enable you to query only the information required to display the list. The user can then select an object from the list.

Partial Object Reading

Partial object reading is a query designed to extract only the required information from a selected record in a database, rather than all the information the record contains. Because partial object reading does not fully populate objects, you can neither cache nor edit partially-read objects. Also note that the primary key is required to re-query the object (so it can be edited, for example). OracleAS TopLink does not automatically include the primary key information in a partially populated object. If you want to edit the object, specify the primary key as a required partial attribute.

In Example 10-5, the query builds complete employee objects, even though the list displays only employee last names. With no optimization, the query reads employee data.

Example 10-5 No Optimization

/* Read all the employees from the database, ask the user to choose one and return it. This must read in all the information for all the employees.*/
List list;

// Fetch data from database and add to list box.
Vector employees = (Vector) session.readAllObjects(Employee.class);
list.addAll(employees);

// Display list box.
....

// Get selected employee from list.
Employee selectedEmployee = (Employee) list.getSelectedItem();

return selectedEmployee;

Example 10-6 demonstrates the use of partial object reading. It reads only the last name and primary key for the employees. This reduces the amount of data read from the database.

Example 10-6 Optimization Through Partial Object Reading

/* Read all the employees from the database, ask the user to choose one and return it. This uses partial object reading to read just the last name of the employees. Note that OracleAS TopLink does not automatically include the primary key of the object. If this is needed to select the object for a query, it must be specified as a partial attribute so that it can be included. In this way, the object can easily be read for editing. */
List list;
// Fetch data from database and add to list box.
ReadAllQuery query = new ReadAllQuery(Employee.class);
query.addPartialAttribute("lastName");
/* OracleAS TopLink does not automatically include the primary key of the object. If this is needed to select the object for a query, it must be specified as a partial attribute so that it can be included.*/
query.addPartialAttribute("id");
// The next line avoids a query exception
query.dontMaintainCache();
Vector employees = (Vector) session.executeQuery(query);
list.addAll(employees);

// Display list box.
....
// Get selected employee from list.
Employee selectedEmployee = (Employee)session.readObject(list.getSelectedItem());
return selectedEmployee;

ReportQuery

Report query enables you to retrieve data from a set of objects and their related objects. Report query supports database reporting functions and features.

For more information, see "ReportQuery".

Example 10-7 demonstrates the use of report query to read only the last name of the employees. This reduces the amount of data read from the database compared to the code in Example 10-5, and avoids instantiating employee instances.

Example 10-7 Optimization Through Report Query

/* Read all the employees from the database, ask the user to choose one and return it. This uses the report query to read just the last name of the employees. It then uses the primary key stored in the report query result to read the real object.*/
List list;
// Fetch data from database and add to list box.
ExpressionBuilder builder = new ExpressionBuilder();
ReportQuery query = new ReportQuery (Employee.class, builder);
query.addAttribute("lastName");
query.retrievePrimaryKeys();
Vector reportRows = (Vector) session.executeQuery(query);
list.addAll(reportRows);

// Display list box.
....

// Get selected employee from list.
ReportQueryResult result = (ReportQueryResult) list.getSelectedItem();
Employee selectedEmployee = (Employee)    result.readobject(Employee.Class,session);

Although the differences between the unoptimized example (Example 10-5) and the report query optimization in Example 10-7 appear to be minor, report queries offer a substantial performance improvement.

Reading Case 2: Batch Reading Objects

The way your application reads data from the database affects performance. For example, reading a collection of rows from the database is significantly faster than reading each row individually.

A common performance challenge is to read a collection of objects that have a one-to-one reference to another object. This normally requires one read operation to read in the source rows, and one call for each target row in the one-to-one relationship.

To reduce the number of reads required, use join and batch reading. Example 10-8 illustrates the unoptimized code required to retrieve a collection of objects with a one-to-one reference to another object. Example 10-9 and Example 10-10 illustrate the use of joins and batch reading to improve efficiency.

Example 10-8 No Optimization

/* Read all the employees, and collect their address' cities. This takes N + 1   
   queries if not optimized.
*/

// Read all the employees from the database. This requires 1 SQL call.
Vector employees = session.readAllObjects(Employee.class,new    ExpressionBuilder().get("lastName").equal("Smith"));

//SQL: Select * from Employee where l_name = 'Smith'

// Iterate over employees and get their addresses.
// This requires N SQL calls.
Enumeration enum = employees.elements();
Vector cities = new Vector();
while(enum.hasMoreElements()) Employee employee = (Employee) enum.nextElement();
   cities.addElement(employee.getAddress().getCity());

//SQL: Select * from Address where address_id = 123, etc }

Example 10-9 Optimization Through Joining

/* Read all the employees, and collect their address' cities. Although the code
   is almost identical because joining optimization is used it only takes 1 
   query. 
*/

// Read all the employees from the database, using joining. 
// This requires 1 SQL call.
ReadAllQuery query = new ReadAllQuery();
query.setReferenceClass(Employee.class);
query.setSelectionCriteria(new    ExpressionBuilder().get("lastName").equal("Smith"));
query.addJoinedAttribute("address");
Vector employees = session.executeQuery(query);

/* SQL: Select E.*, A.* from Employee E, Address A where E.l_name = 'Smith' and 
   E.address_id = A.address_id Iterate over employees and get their addresses. 
   The previous SQL already read all the addresses so no SQL is required. 
*/
Enumeration enum = employees.elements();
Vector cities = new Vector();
while (enum.hasMoreElements()) {
Employee employee = (Employee) enum.nextElement();
    cities.addElement(employee.getAddress().getCity());

Example 10-10 Optimization Through Batch Reading

/* Read all the employees, and collect their address' cities. Although the code 
   is almost identical because batch reading optimization is used it only takes 
   2 queries. 
*/

// Read all the employees from the database, using batch reading. 
// This requires 1 SQL call, note that only the employees are read. 
ReadAllQuery query = new ReadAllQuery();
query.setReferenceClass(Employee.class);
query.setSelectionCriteria(new    ExpressionBuilder().get("lastName").equal("Smith"));
query.addBatchReadAttribute("address");
Vector employees = (Vector)session.executeQuery(query);

// SQL: Select * from Employee where l_name = 'Smith'

// Iterate over employees and get their addresses.
// The first address accessed will cause all the addresses to be read in a single SQL call.
Enumeration enum = employees.elements();
Vector cities = new Vector();
while (enum.hasMoreElements()) {
    Employee employee = (Employee) enum.nextElement();
    cities.addElement(employee.getAddress().getCity());
    // SQL: Select distinct A.* from Employee E, Address A 
      where E.l_name = 'Smith' and E.address_id = A.address_i
}

Because the two-phase approach to the query (Example 10-9 and Example 10-10) accesses the database only twice, it is significantly faster than the approach illustrated in Example 10-8.

Joins offer a significant performance increase under most circumstances. Batch reading offers further performance advantage because it allows for delayed loading through valueholders, and has much better performance where the target objects are shared.

For example, if employees in Example 10-8, Example 10-9, and Example 10-10 live at the same address, batch reading reads much less data than joining, because batch reading uses a SQL DISTINCT call to filter duplicate data. Batch reading is also available for one-to-many relationships, but joining is available only for one-to-one relationships.

Reading Case 3: Using Complex Custom SQL Queries

OracleAS TopLink provides a high-level query mechanism. However, if your application requires a complex query, a direct SQL call may be the best solution.

For more information about executing SQL calls, see "Custom SQL".

Reading Case 4: Using View Objects

Some application operations require information from several objects rather than from just one. This can be both difficult to implement, and resource intensive. Example 10-11 illustrates unoptimized code that reads information from several objects.

Example 10-11 No Optimization

/* Gather the information to report on an employee and return the summary of the 
   information. In this situation a hashtable is used to hold the report 
   information. Notice that this reads a lot of objects from the database, but 
   uses very little of the information contained in the objects. This may take 5 
   queries and read in a large number of objects.
*/

public Hashtable reportOnEmployee(String employeeName)
    {
    Vector projects, associations;
    Hashtable report = new Hashtable();
    // Retrieve employee from database.
    Employee employee = session.readObject(Employee.class, new
       ExpressionBuilder.get("lastName").equal(employeeName)); 
    // Get all the projects affiliated with the employee.
    projects = session.readAllObjects(Project.class, "SELECT P.* FROM PROJECT P, 
      EMPLOYEE E WHERE P.MEMBER_ID = E.EMP_ID AND E.L_NAME = " + employeeName);
    // Get all the associations affiliated with the employee.
    associations = session.readAllObjects(Association.class, "SELECT A.*
      FROM ASSOC A, EMPLOYEE E WHERE A.MEMBER_ID = E.EMP_ID AND E.L_NAME = 
      " + employeeName);
}
    report.put("firstName", employee.getFirstName());
    report.put("lastName", employee.getLastName());
    report.put("manager", employee.getManager());
    report.put("city", employee.getAddress().getCity());
    report.put("projects", projects);
    report.put("associations", associations);
    return report;
}

To improve application performance in these situations, define a new read-only object to encapsulate this information, and map it to a view on the database. To set the object to be read-only, use the addDefaultReadOnlyClass() API in the oracle.toplink.sessions.Project class.

Example 10-12 Optimization Through View Object

CREATE VIEW NAMED EMPLOYEE_VIEW AS (SELECT F_NAME = E.F_NAME, L_NAME = E.L_NAME,EMP_ID = E.EMP_ID, MANAGER_NAME = E.NAME, CITY = A.CITY, NAME = E.NAME 
FROM EMPLOYEE E, EMPLOYEE M, ADDRESS A 
WHERE E.MANAGER_ID = M.EMP_ID
AND E.ADDRESS_ID = A.ADDRESS_ID)

Define a descriptor for the EmployeeReport class:

  • Define the descriptor normally, but specify tableName as EMPLOYEE_VIEW.

  • Map only the attributes required for the report. In the case of numberOfProjects and associations, use a transformation mapping to retrieve the required data.

You can now query the report from the database as with any other OracleAS TopLink-enabled object.

Example 10-13 View the Report from Example 10-12

/* Return the report for the employee.*/
public EmployeeReport reportOnEmployee(String employeeName) 
{
    EmployeeReport report;
    report = (EmployeeReport) session.readObject(EmployeeReport.class, 
      new ExpressionBuilder.get("lastName").equal(employeeName));
    return report;}