Oracle TopLink Developer's Guide
10g Release 3 (10.1.3) B13593-01 |
|
![]() Previous |
![]() Next |
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". |
When using conforming, follow the guidelines that this section describes to ensure that conforming queries return results:
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".
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:
|
In-memory query with joining |
Considers only explicitly created, changed, or deleted objects in the unit of work:
|
Unsupported in-memory queries |
Removes explicitly deleted objects |
Note: If aReadObjectQuery returns a single result from the database and if that object is deleted in the unit of work, nothing is returned.
|
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.
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:
Using the UnitOfWork
, read in all employees with addresses outside of Ottawa.
All these Employee
objects are now registered with the UnitOfWork
Using the same UnitOfWork
, read in all addresses outside of Ottawa.
All these Address
objects are registered with the UnitOfWork
.
Modify the addresses, changing some of the addresses to be in Ottawa.
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.
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:
Using the UnitOfWork
, read in all addresses outside of Ottawa.
Modify the addresses, changing some of the addresses to be in Ottawa
Commit the transaction.
Using the UnitOfWork
, read in all employees inside Ottawa.
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]
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".
This section describes alternatives to conforming that may meet your needs without the performance penalty imposed by conforming. This section describes the following:
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"
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.