Skip Headers
Oracle TopLink Developer's Guide
10g Release 3 (10.1.3)
B13593-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
 

Using Conforming Queries and Descriptors

Because queries are executed on the database, querying though a unit of work will not, by default, include new, uncommitted objects in a unit of work. The unit of work will not spend time executing your query against new, uncommitted objects in the unit of work unless you explicitly tell it to. If you have uncommitted changes, this can pose a problem in a unit of work. Uncommitted changes not yet written to the database cannot influence which result set gets returned.

Conforming is a query feature that lets you include new, changed, or deleted objects in queries within a unit of work prior to committing. This lets you to query against your relative logical or transaction view of the database.

Before you use conforming, be aware of its limitations (see "Guidelines for Using Conforming") and make sure that conforming is actually necessary. For example, consider the alternative described in "Conforming Query Alternatives".

This section explains the following:


Note:

By default, TopLink suppresses exceptions thrown during the memory search stage of conforming. For more information on handling exceptions during conforming, see "Exceptions During Conforming".

Guidelines for Using Conforming

When using conforming, follow the guidelines that this section describes to ensure that conforming queries return results:

Use Only In-Memory Queries that Support Conforming

Conforming is supported by the following in-memory queries:

  • ReadObjectQuery

  • ReadAllQuery

  • Query by example (ReadAllQuery only)

  • Query by selection object or primary key (only new or deleted objects apply)

Conforming is not supported by any other type of in-memory query.

For more information, see "Using In-Memory Queries".

Consider How Conforming Affects Database Results

Table 102-1 summarizes conforming behavior for in-memory queries.

Table 102-1 Conforming Support for In-Memory Queries

Query Type Conforming's Effect on Database Result

In-memory query without joining

Considers both explicitly and implicitly created (new), changed, or deleted objects in the unit of work:

  • Removes explicitly or implicitly deleted objects

  • Adds explicitly or implicitly created objects

  • Adds explicitly or implicitly changed objects

In-memory query with joining

Considers only explicitly created, changed, or deleted objects in the unit of work:

  • Removes explicitly deleted objects

  • Adds explicitly created objects

  • Adds explicitly changed objects

Unsupported in-memory queries

Removes explicitly deleted objects



Note:

If a ReadObjectQuery returns a single result from the database and if that object is deleted in the unit of work, nothing is returned.

If You Want an Object in the Conformed Results, Register it in the Unit of Work

Consider the example shown in Figure 102-1. In this example, you have Employee objects with an address attribute configured for indirection (see "Indirection") mapped by a one-to-one mapping to an Address object.

Figure 102-1 Conforming Example

Description of Figure 102-1  follows
Description of "Figure 102-1 Conforming Example"

You want to read all employees who live in Ottawa but first, you need to modify some of the Address objects to change city from Toronto to Ottawa. Jane Doe is one such employee.

First, using the UnitOfWork, you read all Address objects and change some city attributes (including Jane's) from Toronto to Ottawa. Then you run a conforming query to get all employees who live in Ottawa. However, Jane is not included in the results, even though she now lives in Ottawa:

  • Jane is not returned from the database because the transaction has not yet been committed and in the database, her address still says Toronto.

  • Jane cannot be added to the conformed result in memory because she is not registered in the UnitOfWork cache.

Conforming only recognizes explicit changes. In this example, Jane Doe's Employee object was only implicitly changed. In order to be considered explicitly changed, an Employee must:

  • be registered in the UnitOfWork

  • have its address attribute changed (in this example, indirection must be triggered for the address attribute)

The correct way to handle this example would be as follows:

  1. Using the UnitOfWork, read in all employees with addresses outside of Ottawa.

    All these Employee objects are now registered with the UnitOfWork

  2. Using the same UnitOfWork, read in all addresses outside of Ottawa.

    All these Address objects are registered with the UnitOfWork.

  3. Modify the addresses, changing some of the addresses to be in Ottawa.

  4. Run the conforming query on employees with addresses inside Ottawa.

    The conforming query triggers indirection.

    All employees with addresses in Ottawa are returned, including both employees that were in Ottawa originally and employees whose addresses were changed in this transaction.

  5. Commit the transaction.

Note that even if the conforming query triggers indirection, in this example, there may be no extra database reads because of the one-to-one mapping and the fact that the Address objects are in the cache. If you do not register all employees whose address may be changed, the conforming query will not include Jane.

An alternate approach is to use short transactions: the safest conforming query is one made immediately after a commit. For example:

  1. Using the UnitOfWork, read in all addresses outside of Ottawa.

  2. Modify the addresses, changing some of the addresses to be in Ottawa

  3. Commit the transaction.

  4. Using the UnitOfWork, read in all employees inside Ottawa.

Using Conforming Queries

Assume that a single Pet of type Cat already exists on the database. Examine the code shown in Example 102-6.

Example 102-6 Using Conforming Queries

UnitOfWork uow = session.acquireUnitOfWork();
    Pet pet2 = new Pet();
    Pet petClone = (Pet)uow.registerObject(pet2);
    petClone.setId(200);
    petClone.setType("Cat");
    petClone.setName("Mouser");

    ReadAllQuery readAllCats = new ReadAllQuery();
    readAllCats.setReferenceClass(Pet.class);
    ExpressionBuilder builder = new ExpressionBuilder();
    Expression catExp = builder.get("type").equal("Cat");
    readAllCats.setSelectionCriteria(catExp);

    Vector allCats = (Vector)uow.executeQuery(readAllCats);

    System.out.println("All 'Cats' read through UOW are: " + allCats); 
uow.commit();

This produces the following output:

All 'Cats' read through UOW are: [Pet type Cat named Fluffy id:100]

If you tell the query readAllCats to include new objects:

readAllCats.conformResultsInUnitOfWork();

The output would be:

All 'Cats' read through UOW are: [Pet type Cat named Fluffy id:100, Pet type Cat named Mouser id:200]

Using Conforming Descriptors

TopLink's support for conforming queries in the unit of work can be specified at the descriptor level.

You can define a descriptor directly to always conform results in the unit of work so that all queries performed on this descriptor conform its results in the unit of work by default. You can specify this either within code or from the TopLink.

You can configure a descriptor to always conform in the unit of work using the TopLink or Java code.

To configure a descriptor to always conform in the unit of work in Java code, use Descriptor method setShouldAlwaysConformResultsInUnitOfWork, passing in an argument of true.

To configure a descriptor to always conform in the unit of work using TopLink, see "Configuring Unit of Work Conforming at the Descriptor Level".

Conforming Query Alternatives

This section describes alternatives to conforming that may meet your needs without the performance penalty imposed by conforming. This section describes the following:

Using Unit of Work Method writeChanges Instead of Conforming

Using UnitOfWork method writeChanges, you can write uncommitted changes to the data source: you can execute report queries, noncaching queries, and data read and modify queries against these changes (see Example 102-7).

Example 102-7 Using Unit of Work Method writeChanges

UnitOfWork uow = session.acquireUnitOfWork();
    Pet pet = new Pet();
    Pet petClone = (Pet)uow.registerObject(pet);
    petClone.setId(100);
    petClone.setName("Fluffy");
    petClone.setType("Cat");

uow.writeChanges();

// Use uow to perform report, noncaching, and data read and modify queries
// against the changes made so far

uow.commit();

However, you can call writeChanges only once; any attempt to register objects or to execute object-level queries will throw an exception.

For more information, see "Writing Changes Before Commit Time"

Using Unit of Work Properties Instead of Conforming

Sometimes, you need to provide other code modules with access to new objects created in a unit of work. Conforming can be used to provide this access. However, the following alternative is significantly more efficient.

Somewhere a unit of work is acquired from a session and is passed to multiple modules for portions of the requisite processing:

UnitOfWork uow = session.acquireUnitOfWork();

In the module that creates the new employee, note the following:

Pet newPet = new Pet();
Pet newPetClone = (Pet)uow.registerObject(newPet);
uow.setProperty("NEW PET", newPet);

In other modules where newPet needs to be accessed for further modification, it can simply be extracted from the unit of work's properties:

Pet newPet = (Pet) uow.getProperty("NEW PET");
newPet.setType("Dog");

Conforming queries are ideal if you are not sure if an object has been created yet or the criteria is dynamic.

However, for situations where the quantity of objects is finite and well known, using unit of work properties is a simple and more efficient solution.