text:
This chapter explains the advanced unit of work API calls and techniques that you are most likely to use later in the development cycle.
This chapter includes the following sections:
Using Optimistic Read Locking with the forceUpdateToVersionField Method
Integrating the Unit of Work with an External Transaction Service
For more information about the available methods for the
UnitOfWork, see Oracle Fusion Middleware Java API Reference for Oracle TopLink.
The unit of work provides a number of object registration options.
This section describes the following:
How to Create and Register a New Object in One Step Using UnitOfWork Method newInstance
Example 115-1 shows how to use the unit of work
newInstance method to create a new
Pet object, register it with the unit of work, and return a clone, all in one step. If you are using a factory design pattern to create your objects (and have specified this in the query builder), the
newInstance method will use the appropriate factory.
registerAllObjects method takes a
Collection of objects as an argument and returns a
Collection of clones. This lets you register many objects at once as shown in Example 115-2.
Note:You cannot use
UnitOfWork methods
registerNewObject, or
registerExistingObject with an aggregate object (see Section 184.108.40.206, "Creating Relational Aggregate Descriptors"). Doing so will raise a ValidationException or other errors at commit time. For more information, see Section 115.1.4, "How to Work with Aggregates".
Example 115-2 Using registerAllObjects
UnitOfWork uow = session.acquireUnitOfWork(); Collection toRegister = new ArrayList(2); VetVisit vv1 = new VetVisit(); vv1.setId(70); vv1.setNotes("May have flu"); vv1.setSymptoms("High temperature"); toRegister.add(vv1); VetVisit vv2 = new VetVisit(); vv2.setId(71); vv2.setNotes("May have flu"); vv2.setSymptoms("Sick to stomach"); toRegister.add(vv2); uow.registerAllObjects(toRegister); uow.commit();
When you register an object with the unit of work, TopLink runs an existence check to determine whether or not the object exists. TopLink uses this information at commit time to determine whether to perform an insert or an update operation. You can specify the default existence checking policy for a project as a whole (see Section 117.7, "Configuring Existence Checking at the Project Level") or on a per-descriptor basis (Section 119.17, "Configuring Cache Existence Checking at the Descriptor Level"). By default, TopLink uses the check cache existence checking policy. If you use any existence checking policy other than check cache, then you can use the way you register your objects to your advantage to reduce the time it takes TopLink to register an object.
This section explains how to use one of the following existence checking policies to accelerate object registration:
If you configure a class's descriptor with an existence checking policy of check database, TopLink will check the database for existence for all instances of that class registered in a unit of work. However, if you know that an object is new or existing, rather than use the basic
registerObject method, you can use
registerNewObject or
registerExistingObject to bypass the existence check. TopLink will not check the database for existence on objects that you have registered with these methods. It will automatically perform an insert operation if
registerNewObject is called, or an update operation if
registerExistingObject is called.
If you configure a class's descriptor with an existence checking policy of assume existence, TopLink will assume that all instances of that class registered with a unit of work exist and TopLink will always perform an update operation to the database on all such registered objects; even new objects that you registered with
registerObject method. However, if you use the
registerNewObject method on the new object, TopLink knows to perform an insert operation in the database even though the existence checking policy says assume existence.
If you configure a class's descriptor with an existence checking policy of assume nonexistence then TopLink assumes that all instances of that class registered with a unit of work do not exist and will always perform an insert operation on the database, even on objects read from the database. However, if you use the
registerExistingObject method on existing objects, TopLink knows to perform an update operation on the database.
Aggregate mapped objects should never be registered in a TopLink unit of work–doing so will generate an exception. Aggregate cloning and registration is automatic based on the owner of the aggregate object. In other words, if you register the owner of an aggregate, the aggregate is automatically cloned. When you get a working copy of an aggregate owner, its aggregate is also a working copy.
When working with aggregates, you should always use an aggregate within the context of its owner:
If you get an aggregate from a working clone owner, then the aggregate is a working clone.
If you get an aggregate from a cache version owner, then the aggregate is the cache version.
For more information about aggregate objects, see Section 184.108.40.206, "Creating Relational Aggregate Descriptors".
The unit of work
unregisterObject method lets you unregister a previously registered object from a unit of work. An unregistered object will be ignored in the unit of work, and any uncommitted changes made to the object up to that point will be discarded.
In general, this method is rarely used. It can be useful if you create a new object, but then decide to delete it in the same unit of work (which is not recommended).
Table 115-1 summarizes the
UnitOfWork object registration methods.
Table 115-1 UnitOfWork Object Registration API
|UnitOfWork Method
Create new object without children
New object is reference to cache object after commit.
Create new object without children
New object is reference to cache object after commit.
Performance enhancement: no clone created.
Create new child object for existing parent: unidirectional relationship
Child: no registration necessary
TopLink cascades registration to all new child objects reachable from the registered object: no need to register new child objects.
Create new child object for existing parent: unidirectional relationship
New object is reference to cache object after commit.
Create new child object for existing parent: bidirectional relationship
Child: no registration necessary
TopLink cascades registration to all new child objects reachable from the registered object: no need to register new child objects.
Create new child object for existing parent: bidirectional relationship
New object can be queried prior to commit (without using conforming query).
Modify existing object known to exist in the database.
Performance enhancement: no call to
Modify a
Convenience method: equivalent to calling
If a new object is reachable from a clone, you do not need to register it.
When working with new objects, remember the following:
Only reachable or registered objects will be persisted.
Reachable new objects or objects that have been registered with
registerNewObject are considered to be working copies in the unit of work.
If you call
registerObject with a new object, the clone the method returns, and the argument you pass in, are considered the cache version.
registerNewObject method registers a new object as if it was a clone. At commit time, the unit of work creates another instance of the object to be the cache version of that object. Use the
registerNewObject method in situations where the following applies:
You do not need a handle to the cache version of the object after the commit transaction and you do not want to work with clones of new objects.
You must pass a clone into the constructor of a new object, then register the new object.
Note that if you call
registerNewObject on an object, TopLink does not cascade registration to new children of that object. Children will be persisted but you cannot query them before commit (unless you use conforming queries).
To make children visible to queries before commit, you must do one of the following:
register the parent using
register each child using
registerNewObject if you register the parent using
For more information, see the following:
You can declare a class as read-only within the context of a unit of work. Clones are neither created nor merged for such classes, thus improving performance. Such classes are ineligible for changes in the unit of work.
When a unit of work registers an object, it traverses and registers the entire object tree. If the unit of work encounters a read-only class, it does not traverse that branch of the tree, and does not register objects referenced by the read-only class, so those classes are ineligible for changes in the unit of work. The read-only classes are cached and must not be changed by the user.
This section describes the following:
How to Configure Read-Only Classes for a Single Unit of Work
Alternatively, you can configure an object-level ready query as a read-only query. For more information, see Section 126.96.36.199, "Read-Only Query".
For example, suppose class A owns a class B, and class C extends class B. You acquire a unit of work in which you know only instances of class A will change: you know that no class Bs will change. Before registering an instance of class B, use the following:
You can then proceed with your transaction: registering class A objects, modifying their working copies, and committing the unit of work.
At commit time, the unit of work will not have to compare backup clones with the working clones for instances of class B (even if instances were registered explicitly or implicitly). This can improve unit of work performance if the object tree is very large.
Note that if you register an instance of class C, the unit of work does not create or merge clones for this object; any changes made to class C are not be persisted because class C extends class B and class B was identified as read-only.
To identify multiple classes as read-only, add them to a
Vector and use the following code:
Note that a nested unit of work inherits the set of read-only classes from the parent unit of work. For more information on using a nested unit of work, see Section 115.8, "Using a Nested or Parallel Unit of Work".
To establish a default set of read-only classes for all units of work, use the project method
setDefaultReadOnlyClasses(Vector). After you call this method, all new units of work include the
Vector of read-only classes.
When you declare a class as read-only, the read-only declaration extends to its descriptors. You can declare a descriptor as read-only at development time, using either Java code, Oracle JDeveloper, or TopLink Workbench. This option improves performance by excluding the read-only descriptors from unit of work registration and editing.
To flag descriptors as read-only in Java code, call the
setReadOnly method on the descriptor as follows:
To declare a descriptor as read-only in TopLink Workbench, select the Read Only check box for the specific descriptor.
For more information, see Section 119.3, "Configuring Read-Only Descriptors".
By default, when you call the unit of work
commit method, TopLink writes your changes to the data source and commits your changes.
Alternatively, you can perform a two-stage or partial commit transaction by using the unit of work
writeChanges method prior to calling
commit (either directly or by way of an external transaction service).
When you call the unit of work
writeChanges method, the unit of work commit process begins, and all changes are written out to the data source. However, the data source transaction is not committed, nor will changes be merged into the shared session cache. To finalize your changes, the unit of work
commit method must still be called (either directly or by way of an external transaction service).
After you call the unit of work
writeChanges method, any attempt to register objects or execute object-level queries will throw an exception. You may execute report queries, noncaching queries, and data read and modify queries.
If any exception is thrown, the transaction will be rolled back (or marked rollback only) and you cannot recover the unit of work.
You can call this method only once. You cannot use this method to write out changes in an incremental fashion.
You can use the unit of work
writeChanges method to address a variety of transaction issues, including the following:
As an alternative to conforming (see Section 126.96.36.199, "Using Unit of Work Method writeChanges Instead of Conforming")
To handle external transaction issues (see Section 115.13.4, "How to Use the Unit of Work to Handle External Transaction Timeouts and Exceptions")
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 Section 115.4.1, "How to Use Conforming") and make sure that conforming is actually necessary. For example, consider the alternative described in Section 115.4.4, "What You May Need to Know About Conforming Query Alternatives".
This section explains the following:
What You May Need to Know About Conforming Query Alternatives
Note:By default, TopLink suppresses exceptions thrown during the memory search stage of conforming. For more information on handling exceptions during conforming, see Section 18.104.22.168, "Handling Exceptions During Conforming".
When using conforming, follow the guidelines that this section describes to ensure that conforming queries return the correct results:
Conforming is supported by the following queries:
Query by example
Query by selection object or primary key (only new or deleted objects apply)
Conforming is not supported by the following queries:
DataReadQuery (deleted objects can be conformed)
StoredProcedureCall (deleted objects can be conformed)
EISCall (deleted objects can be conformed)
Expression or EJB QL queries that use database-specific functions, or subselects
Parallel expressions.
When conforming is used on a
ReadAllQuery, the database result is first queried. If the unit of work has not yet committed any changes to the database, this result will not reflect the unit of work changes. The database results are then conformed in memory using the following criteria:
Registered new objects that conform to the query are added.
Modified existing objects that no longer conform are removed.
Modified existing objects that conform are added.
Deleted objects are removed.
Note:If new objects are not explicitly registered, they are not conformed. Also, if removed object are not explicitly deleted, they are not conformed.
If the query uses ordering, ordering of conformed results is not maintained and conformed instances are added to the front of the result. To apply ordering, store the result in memory using
Collections method
sort, or a
TreeSet result collection class. When using conforming on a
ReadObjectQuery, first query the unit of work for a conforming object: if the conforming object is found, it is returned and the database access is avoided; if the conforming object is not found, the database is queried. If the unit of work has not yet committed any changes to the database, this result does not reflect the unit of work changes. The database results are then conformed in memory using the following criteria:
If the database result no longer conforms, null is returned.
If the database result has been deleted, null is returned.
Note:If the database result returns multiple results, only the first result is considered, because it is an instance of the
ReadAllQuery and only a single result is expected. If the first result no longer conforms, null is returned, even if there were potential valid conforming results.
If you expect the query to return multiple results, use a
If a new object is only related to an existing object, and not explicitly registered, queries for this object are not able to conform it. If you remove, but do not explicitly delete a privately owned object, this object cannot be conformed.
If a query traverses relationships (uses joins) and the related objects are changed, the query can only conform these objects if both of the following conditions are met:
The source objects have been registered in the unit of work.
The source objects' relationship has been instantiated.
TopLink provides a conforming option that forces an instantiation of indirection (lazy loading). However, you use this option with caution as it can cause an increased database access.
Consider Example 115-2. In this example, you have
Employee objects with an
address attribute configured for indirection (see Section 17.2.4, "Indirection (Lazy Loading)") mapped by a one-to-one mapping to an
Address object.
Figure 115-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, for the following reasons 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 meet the following criteria:
Be registered in the
Have its
address attribute changed: in this example, indirection (lazy loading) 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.
All these
Employee objects are now registered with the
Using the same
UnitOfWork, access the employees' addresses, instantiating the indirect relationships.
Modify the employees' addresses, changing some of the addresses to be in Ottawa.
Run the conforming query on employees with addresses inside Ottawa.
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.
If you do not register all employees whose address may be changed, and instantiate their address relationship, 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 115-3.
Example 115-3 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); List allCats = (List)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:
The output would be as follows:
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, from Oracle JDeveloper, or TopLink Workbench.
You can configure a descriptor to always conform in the unit of work using Oracle JDeveloper, TopLink Workbench, 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
To configure a descriptor to always conform in the unit of work using TopLink, see Section 119.4, "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 Unit of Work Method writeChanges Instead of Conforming
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 115-4).
Example 115-4 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 Section 115.3, "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.
In a three-tier application, the client and server exchange objects using a serialization mechanism such as RMI or CORBA. When the client changes an object and returns it to the server, you cannot register this serialized object into a unit of work directly. On the server, you must merge the serialized object with the original object in the session cache.
Using the unit of work methods listed in Table 115-2, you can merge a deserialized object into your session cache. Each method takes the serialized object as an argument and returns the original object.
Before doing so, you must ensure that the source object is in your session cache. Attempting to merge a deserialized object into a session cache that does not yet contain the object will result in a descriptor exception. To avoid this, Oracle recommends that you first read the object instance that the deserialized object represents. If you are using a coordinated cache or your application is running in a cluster, the session you merge into may not yet contain your original object. By performing a read operation first, you guarantee that the object will be in the cache before you merge.
Table 115-2 Unit of Work Merge Methods
|Used When
Merges the serialized object and all its privately owned parts (excluding non-private references from it to independent objects) into the working copy clone.
The client edits the object but not its relationships, or marks its independent relationships as transient.
Merges the serialized object and all references into the working copy clone.
The client edits the object and the targets of its relationships and has not marked any attributes as transient.
Merges only serialized object changes to attributes mapped with direct mappings into the working copy clone.
The client edits only the object's direct attributes or has marked all of the object's relationships as transient.
Merges the serialized object and everything connected to it (the entire object tree where the serialized object is the root) into the working copy clone.
The client traverses all relationships of the objects and makes changes.
Note: Use
Note that if your three-tier client is sufficiently complex, consider using the TopLink remote session (see Section 87.9, "Remote Sessions"). It automatically handles merging and lets you use a unit of work on the client.
You can merge clones with both existing and new objects. Because clones do not appear in the cache and may not have a primary key, you can merge new objects only once within a unit of work. If you need to merge a new object more than once, call the unit of work
setShouldNewObjectsBeCached method, and ensure that the object has a valid primary key; you can then register the object.
Example 115-5 shows one way to update the original object with the changes contained in the corresponding serialized object (
rmiClone) received from a client.
Example 115-5 Merging a Serialized Object
update(Object original, Object rmiClone) { original = uow.registerObject(original); uow.mergeCloneWithRefereneces(rmiClone); uow.commit(); }
For more information, see Section 188.8.131.52, "Indirection, Serialization, and Detachment".
At commit time, a unit of work and its contents expire: you must not use the unit of work nor its clones even if the transaction failed and rolled back.
However, TopLink offers the following API that lets you continue working with a unit of work and its clones:
commitAndResume: Commits the unit of work, but does not invalidate it or its clones.
commitAndResumeOnFailure: Commits the unit of work. If the commit transaction succeeds, the unit of work expires. However, if the commit transaction fails, this method does not invalidate the unit of work or its clones. This method lets you modify the registered objects in a failed unit of work and retry the commit transaction.
You should resume a unit of work only in an application that makes repeated changes to the same, small dataset. Reusing the same unit of work while accessing different datasets may result in poor performance.
Example 115-6 shows how to use the
commitAndResume method.
Example 115-6 Using the commitAndResume Method
UnitOfWork uow = session.acquireUnitOfWork(); PetOwner petOwnerClone = (PetOwner)uow.readObject(PetOwner.class); petOwnerClone.setName("Mrs. Newowner"); uow.commitAndResume(); petOwnerClone.setPhoneNumber("KL5-7721"); uow.commit();
commitAndResume call produces the following SQL:
UPDATE PETOWNER SET NAME = 'Mrs. Newowner' WHERE (ID = 400)
Then, the
commit call produces the following SQL:
Under certain circumstances, you may want to abandon some or all changes to clones in a unit of work, but not abandon the unit itself. The following options exist for reverting all or part of the unit of work:
revertObject: Abandons changes to a specific working copy clone in the unit of work
revertAndResume: Uses the backup copy clones to restore all clones to their original states, deregister any new objects, and reinstate any deleted objects.
You can use a unit of work within another unit of work (nesting), or you can use two or more units of work with the same objects in parallel.
This section describes the following:
To start multiple units of work that operate in parallel, call the
acquireUnitOfWork method multiple times on the session. The units of work operate independently of one another and maintain their own cache.
To nest units of work, call the
acquireUnitOfWork method on the parent unit of work. This creates a child unit of work with its own cache. If a child unit of work commits, it updates the parent unit of work rather than the database. If the parent does not commit, the changes made to the child are not written to the database.
TopLink does not update the database or the cache until the outermost unit of work is committed. You must commit or release the child unit of work before you can commit its parent.
Working copy clones from one unit of work are not valid in another units of work: not even between an inner and outer unit of work. You must register objects at all levels of a unit of work where they are used.
Example 115-7 shows how to use nested units of work.
Example 115-7 Using Nested Units of Work
UnitOfWork outerUOW = session.acquireUnitOfWork(); Pet outerPetClone = (Pet)outerUOW.readObject(Pet.class); UnitOfWork innerUOWa = outerUOW.acquireUnitOfWork(); Pet innerPetCloneA = (Pet)innerUOWa.registerObject(outerPetClone); innerPetCloneA.setName("Muffy"); innerUOWa.commit(); UnitOfWork innerUOWb = outerUOW.acquireUnitOfWork(); Pet innerPetCloneB = (Pet)innerUOWb.registerObject(outerPetClone); innerPetCloneB.setName("Duffy"); innerUOWb.commit(); outerUOW.commit();
You can execute native SQL or invoke a stored procedure within a unit of work by using unit of work method
executeNonSelectingCall, or by executing a
DataModifyQuery. This makes the unit of work begin its database transaction early and execute the call to the data immediately.
If you release the unit of work, it will roll back the database changes. If you commit the unit of work and the commit succeeds, the unit of work will commit the changes to the database.
You can execute a
DataModifyQuery only in a unit of work or a database session. You cannot execute a
DataModifyQuery in a client or server session directly.
You can execute a
DataReadQuery or use session method
executeSelectingCall in any session type because these do not modify data.
Example 115-8 illustrates using
SQLCall with the unit of work method
Example 115-8 Using the executeNonSelectingCall Method
uow.executeNonSelectingCall(new SQLCall(mySqlString));
Allowing an unverified SQL string to be passed into methods makes your application vulnerable to SQL injection attacks.
Section 114.7, "Deleting Objects" explained that TopLink always properly arranges (orders) the SQL based on the mappings and foreign keys in your object model and schema. You can control the order of delete operations if you know how to do the following:
How to Use the setShouldPerformDeletesFirst Method of the Unit of Work
How to Use the addConstraintDependencies Method of the Descriptor
By default, TopLink does insert and update operations first, before delete operations, to ensure that referential integrity is maintained. This is the preferred approach.
If you are forced to replace an object with unique constraints by deleting it and inserting a replacement, you may cause a constraint violation if the insert operation occurs before the delete operation. In this case, call
setShouldPerformDeletesFirst to perform the delete operation before the insert operation.
The constraints used by TopLink to determine delete order are inferred from one-to-one and one-to-many mappings. If you do not have such mappings, you can add constraint knowledge to TopLink using the descriptor
addConstraintDependencies(Class) method.
For example, suppose you have a composition of objects: A contains B (one-to-many, privately owned) and B has a one-to-one, nonprivate relationship with C. You want to delete A (and in doing so the included Bs) but before deleting the Bs, for some of them (not all) you want to delete the associated object C.
There are the following possible solutions:
You can use the
deleteAllObjects method without the
addConstraintDependencies method (see Section 115.10.3, "How to Use the deleteAllObjects Method Without the addConstraintDependencies Method")
You can use the
deleteAllObjects method with the
addConstraintDependencies method (see Section 115.10.4, "How to Use the deleteAllObjects Method with the addConstraintDependencies Method")
In the first option, you do not identify the one-to-many (A to B) relationship as privately owned. When deleting an A object, you must delete all of its B objects, as well as any C objects, as the following example shows:
// delete one of the Cs
uow.deleteObject(((B) existingA.getBs().get(1)).getC());
This option produces the following SQL:
In the second option, keep the one-to-many (A to B) relationship privately owned and add a constraint dependency from A to C, as the following example shows:
Now the delete code would be as follows:
// delete one of the Cs
uow.deleteObject(((B) existingA.getBs().get(1)).getC());
This option produces the following SQL:
In both cases, the B object is deleted before A and C. The main difference is that the second option generates fewer SQL statements, as it knows that it is deleting the entire set of Bs related from A.
If your descriptors are configured to use an optimistic version locking policy (see Section 16.4.1, "Optimistic Version Locking Policies") or field locking policy (see Section 16.4.4, "Optimistic Field Locking Policies"), use the unit of work method
forceUpdateToVersionField to solve either or both of the following problems:
You want an
OptimisticLockingException thrown at commit time if an object you read in a transaction has changed since it was registered even though you have not changed the object in your transaction (see Section 115.11.1, "How to Force a Check of the Optimistic Read Lock").
You modify an object in a transaction in such a way that its version field is not updated but you want to alert other threads of the change by way of the version field (see Section 115.11.2, "How to Force a Version Field Update").
For example, you change a privately owned object that has its own database table so the parent object's version field is not, by default, updated. In this case, you can use
forceUpdateToVersionField to update the parent's version field.
As an alternative to this approach, consider Section 16.4.2, "Optimistic Version Locking Policies and Cascading".
To remove
forceUpdateToVersionField configuration from an object before a commit operation, use the unit of work method
removeForceUpdateToVersionField (see Section 115.11.3, "How to Disable the forceUpdateToVersionField Configuration").
When you read an object with the unit of work, optimistic lock checking is not applied to that object at commit time unless you change the object. However, there are times when you want your transaction to fail if the state of an object has changed since it was read, even though you have not modified the object.
Example 115-9 shows a transaction that updates a mortgage rate by multiplying the central bank prime rate by 1.25. The transaction forces an optimistic read lock on the central prime rate at commit time to ensure that the prime rate has not changed since the transaction began. Note that in this example, the transaction does not increment the version of the unchanged object (the central prime rate).
Example 115-9 Optimistic Read Lock with No Version Increment
try { UnitOfWork uow = session.acquireUnitOfWork(); MortgageRate cloneMortgageRate = (MortgageRate) uow.registerObject(mortgageRate); CentralPrimeRate cloneCentralPrimeRate = (CentralPrimeRate) uow.registerObject(CentralPrimeRate); // change the Mortgage Rate cloneMortgageRate.setRate(cloneCentralPrimeRate.getRate() * 1.25); // optimistic read lock check on Central prime rate with no version update uow.forceUpdateToVersionField(cloneCentralPrimeRate, false); uow.commit(); } catch(OptimisticLockException exception) { // refresh the out-of-date object session.refreshObject(exception.getObject()); // Retry }
For another example that forces both optimistic locking and a version field update, see Example 115-10 in Section 115.11.2, "How to Force a Version Field Update".
The unit of work considers an object changed when you modify its direct-to-field or aggregate object mapping attribute. Adding, removing, or modifying objects related to the source object does not render the source object changed for the purposes of the unit of work. In other words, when a relationship is changed in a one-to-many or one-to-one target foreign key mapping, by default, the version field (if any) of the affected object is not changed.
If you configure a descriptor to refresh the cache only if the database version is newer than the cache version (using descriptor method
onlyRefreshCacheIfNewerVersion), and such a relationship changes, you will not be able to refresh the object at all. Because the version has not changed, the unit of work method
refreshObject and even a query with
refreshIdentityMapResults option set to
true cannot refresh the object.
Using the unit of work method
forceUpdateToVersionField passing in both the unit of work copy clone and
true value will ensure that the object's version field is updated when such a change is made. It will also ensure that changes to the object before it is refreshed will result in optimistic locking exceptions, preventing the writing of stale data (see Section 115.11.1, "How to Force a Check of the Optimistic Read Lock").
Example 115-10 and Example 115-11 show transactions executing in separate threads that access the same customer object concurrently. The unit of work method forceUpdateToVersionField is used to ensure that changes to the customer object in one thread are detected by the other threads.
Example 115-10 shows a transaction in which an invoice thread calculates an invoice for a customer. Example 115-11 shows a transaction in which another thread, the service thread, adds a service to the same customer or modifies the current service. In either case, the service thread must inform the invoice thread, which adds the changes to the invoice.
Example 115-10 Optimistic Read Lock with Version Increment: Service Thread
/* The following code represents the service thread. Notice that the thread forces a version update */ try { UnitOfWork uow = session.acquireUnitOfWork(); Customer cloneCustomer = (Customer uow.registerObject(customer); Service cloneService = (Service uow.registerObject(service); // add a service to customer cloneService.setCustomer(cloneCustomer); cloneCustomer.getServices().add(cloneService); /* Modify the customer version to inform other application that the customer has changed */ uow.forceUpdateToVersionField(cloneCustomer, true); uow.commit(); } catch (OptimisticLockException exception) { // refresh out-of-date object session.refreshObject(exception.getObject()); // retry }
Example 115-11 Optimistic Read Lock with Version Increment: Invoice Thread
/* The following code represents the invoice thread, and calculates a bill for the customer. Notice that it does not force an update to the version */ try { UnitOfWork uow = session.acquireUnitOfWork(); Customer cloneCustomer = (Customer) uow.registerObject(customer); Invoice cloneInvoice = (Invoice) uow.registerObject(new Invoice()); cloneInvoice.setCustomer(cloneCustomer); // calculate service charge int total = 0; for(Enumeration enum = cloneCustomer.getServices().elements(); enum.hasMoreElements()) { total += ((Service) enum.nextElement()).getCost(); } cloneInvoice.setTotal(total); /* Force optimistic lock checking on the customer to guarantee a valid calculation */ uow.forceUpdateToVersionField(cloneCustomer, false); uow.commit(); } catch(OptimisticLockException exception) { // refresh the customer and its privately owned parts session.refreshObject(cloneCustomer); /* If the customer's services are not privately owned then use a ReadObjectQuery to refresh all parts */ ReadObjectQuery query = new ReadObjectQuery(customer); /* Refresh the cache with the query's result and cascade refreshing to all parts including customer's services */ query.refreshIdentityMapResult(); query.cascadeAllParts(); // refresh from the database query.dontCheckCache(); session.executeQuery(query); // retry }
forceUpdateToVersionField configuration that you apply to an object stays in effect for the lifetime of your unit of work. After you commit your transaction,
forceUpdateToVersionField configuration no longer applies.
To remove
forceUpdateToVersionField configuration from an object before commit time, use the unit of work method
removeForceUpdateToVersionField. TopLink will not apply optimistic read locking to the object unless you change it in this transaction (that is, unless you modify its direct-to-field or aggregate object mapping attribute).
Auditing data source changes is a common requirement: when a user commits a change to the data source, the application updates a field in the data source to record the user who made the change and the date.
For example, suppose each row in your database schema includes fields
lastUpdateBy (to record the user name of the user who commits a change) and
lastUpdateOn (to record the date of the change).
You can use
UnitOfWork method
setProperty to record the name of the user who acquires the
UnitOfWork and implement a descriptor event listener for
AboutToUpdateEvent descriptor events that extracts the property and updates the
lastUpdateBy and
lastUpdateOn fields.
For more information, see the following:
To support transactions managed by an application server's external transaction service, TopLink supports external connection pools and external transaction controller classes for supported servers. This lets you incorporate external transaction service support into your application, and use the unit of work with transactions managed externally by the server. For more information, see Section 113.1.2, "Unit of Work Transaction Demarcation".
To integrate a unit of work with an external transaction service, you must enable the use of the following:
an external transaction controller (see Section 89.9, "Configuring the Server Platform")
an external connection pool (see Section 97.4, "Configuring External Connection Pooling")
After you configure external connection pool and external transaction controller support, you use a unit of work in your TopLink application as you would typically, with few exceptions. This section describes these exceptions as follows:
How to Acquire a Unit of Work with an External Transaction Service
How to Use a Unit of Work when an External Transaction Exists
How to Use a Unit of Work when No External Transaction Exists
How to Use the Unit of Work to Handle External Transaction Timeouts and Exceptions
You use a unit of work to commit changes to a data source even when using an external transaction service. To ensure that only one unit of work is associated with a given transaction, use the
getActiveUnitOfWork method to acquire a unit of work, as shown in Example 115-12.
Note:Although there are other ways to commit changes to a data source using an external transaction service, Oracle recommends using the
getActiveUnitOfWork method.
getActiveUnitOfWork method searches for an existing external transaction in the following way:
If there is an active external transaction and a unit of work is already associated with it, return this unit of work.
If there is an active external transaction with no associated unit of work, then acquire a new unit of work, associate it with the transaction, and return it.
If there is no active external transaction in progress, return null.
If TopLink returns a unit of work that is not
null, use it exactly as you would typically: the only exception is that you do not call the
commit method (see Section 115.13.2, "How to Use a Unit of Work when an External Transaction Exists").
If TopLink returns a
null unit of work, start an external transaction explicitly through the
UserTransaction interface.
Example 115-12 Using a Unit of Work with an External Transaction Service
// Read in any pet
Pet pet = (Pet)clientSession.readObject(Pet.class);
UnitOfWork uow = clientSession.getActiveUnitOfWork();
if (uow == null) {
throw new RuntimeException("No transaction started");;
Pet petClone = (Pet)uow.registerObject(pet);
getActiveUnitOfWork returns a unit of work that is not
null, you are associated with an existing external transaction. Use the unit of work as usual.
As the external transaction was not started by the unit of work, issuing a
commit on it will not cause the external transaction to be committed. The unit of work will defer to the application or container that began the transaction. When the external transaction does get committed by the container, TopLink receives synchronization callbacks at key points during the commit transaction.
The unit of work sends the required SQL to the database when it receives the
beforeCompletion callback.
The unit of work uses the
Boolean argument received from the
afterCompletion callback to determine if the commit was successful (
true) or not (
If the commit transaction was successful, the unit of work merges changes to the session cache. If the commit transaction was unsuccessful, the unit of work discards the changes.
Figure 115-2 shows the life cycle of a unit of work when an external transaction exists.
Figure 115-2 Unit of Work when an External Transaction Exists
When the
getActiveUnitOfWork method returns a null unit of work, there is no existing external transaction. You must start a new external transaction.
Do this either by starting an external transaction explicitly using the
UserTransaction interface, or by acquiring a new unit of work using the
acquireUnitOfWork method on the server session.
Use the unit of work as usual.
Once the modifications to registered objects are complete, you must commit the transaction either explicitly through the
UserTransaction interface or by calling the unit of work
commit method.
The transaction synchronization callbacks are then invoked on, and the database updates and cache merge occur based upon those callbacks.
Figure 115-3 Unit of Work when No External Transaction Exists
This section describes the following two common problems with external transactions, and the ways to handle them:
When an external transaction is committed, the external transaction service expects each transaction owner to commit its portion of the overall transaction within a finite amount of time. If any individual transaction exceeds this timeout interval, the external transaction service will fail the specific transaction and roll it back (or mark it rollback only).
If your transaction is large and its commit transaction may exceed the external transaction service timeout interval, use
UnitOfWork method
writeChanges to write changes to the data source before committing the external transaction. This will reduce the time it takes for your part of the global transaction to commit.
For more information about the
UnitOfWork method
writeChanges, including restrictions and warnings, see Section 115.3, "Writing Changes Before Commit Time".
When you use the unit of work with an external transaction service, commit exceptions may not be thrown until long after your application thread calls its
UnitOfWork method
commit and returns. In this case, commit exceptions are thrown to the client of the container-managed transaction (CMT) call, forcing the client to handle this server-side failure.
You can use the
UnitOfWork method
writeChanges to write changes to the data source before the external transaction commits. This allows your application thread to catch and handle most exceptions that could be thrown at the time the external transaction service commits the global transaction.
For more information about the
UnitOfWork method
writeChanges, including restrictions and warnings, see Section 115.3, "Writing Changes Before Commit Time".
For more information on handling unit of work exceptions in general, see Section 115.16.4, "How to Handle Exceptions".
All modifications to persistent beans should be carried out in the context of a transaction.
Modifying entity beans without a transaction can lead to an inconsistent state, potentially corrupting the values in the TopLink cache. Because of this, TopLink does not support modifying a bean through its remote interface when no transaction is active. If you attempt to do so, TopLink simply does not write changes to the database.
Although TopLink does not let you modify an entity bean through its remote interface without a transaction, TopLink does let you invoke methods on its home interface that change the state in the underlying database without a transaction. For example, you may invoke remove and create methods on the home interface of an entity bean without a transaction.
To integrate TopLink transactions and the unit of work with container-managed persistence, you must consider the following:
CMP transaction attribute (see Section 115.14.1, "How to Use CMP Transaction Attribute")
Local transactions (see Section 115.14.2, "How to Use Local Transactions")
Nondeferred changes (see Section 115.14.3, "How to Use Nondeferred Changes")
To ensure that all modifications to persistent beans are carried out in the context of a transaction, transactional attributes must be properly specified in the bean deployment descriptors.
The transaction may be either client-controlled or container-controlled.
Client-controlled transactions are started explicitly by your application by way of the
javax.transaction.UserTransaction interface.
Container-controlled transactions are started implicitly by the container to satisfy the transaction attribute configuration when a bean method is invoked in the absence of a client-controlled transaction.
Table 115-3 shows what transaction (if any) an EJB method invocation uses depending on how its transaction attribute is configured and whether or not a client-controlled transaction exists at the time the method is invoked.
Oracle recommends that you do not make modifications to entity beans under conditions identified as "Use no transaction" in Table 115-3. Oracle also recommends that you avoid using the
Supports transaction attribute because it leads to a nontransactional state whenever the client does not explicitly provide a transaction.
Table 115-3 EJB Transaction State by Transaction Attribute
|Transaction Attribute
|ClientEControlled Transaction Exists
|ClientEControlled Transaction Does Not Exist
Use no transaction
Use no transaction
Use client-controlled transaction
Use no transaction
Use client-controlled transaction
Use container-controlled transaction
Use client-controlled transaction
Use container-controlled transaction
Use client-controlled transaction
Exception raised
Exception raised
Use no transaction
Depending on the EJB container you use, you may be able to write without a container-controlled transaction (see Section 115.13, "Integrating the Unit of Work with an External Transaction Service"). In this case, TopLink automatically uses a transaction of its own, referred to as a local transaction (see Section 115.14.2, "How to Use Local Transactions").
Some EJB containers, such as, for example, OC4J, support writing without an active JTA transaction.
If you execute a bean method outside a JTA transaction while the transaction attribute (see Section 115.14.1, "How to Use CMP Transaction Attribute") is set to
NotSupported, or
Never, TopLink performs the operation within a local unit of work and commits the unit of work at the end of the method. This unit of work is referred to as a local transaction.
The reason for this is because the update semantics in the EJB specification are left undefined for these scenarios, and a proper transactional model demands that a transaction be active before being able to modify data. TopLink also requires change operations to occur within a unit of work to ensure that the session cache remains consistent.
Some EJB containers, such as, for example, OC4J, support nondeferred changes: the ability to modify the data source immediately as you change the persistent fields of an entity bean.
Using nondeferred changes, you can achieve backwards compatibility with the native behavior of some EJB containers, such as, for example, OC4J, and you can accommodate advanced applications that rely on the database and entity changes being synchronized for such things as triggers or stored procedures based on transient state within the transaction, deletion and creation of rows with the same primary key, or other complex queries that depend on transient transaction state.
Nondeferred changes have the disadvantage of being the least efficient approach: they produce the greatest number of data source interactions.
By default, TopLink defers all changes until commit time. This is the most efficient approach that produces the least number of data source interactions.
For more information, see Section 18.104.22.168, "Nondeferred Changes".
Achieving a particular database transaction isolation level in a TopLink application is more involved than simply using the
DatabaseLogin method
In a typical TopLink application and in Java EE applications that require persistence in general, a variety of factors affect when database transaction isolation levels apply and to what extent a particular database transaction isolation can be achieved.
This section describes these factors and provides guidelines on configuring and using TopLink to achieve each database transaction isolation level to the extent possible given these factors.
This section includes the following:
What You May Need to Know About General Factors Affecting Transaction Isolation Level
This section describes some of the important factors and variables that may affect the degree to which your TopLink application can achieve a particular database transaction isolation level. These factors include the following:
In many cases, your TopLink application is not the only application that can update to the database. External, non-TopLink applications, can also update the database at any time.
In this case, your TopLink application must use the
ObjectLevelReadQuery method
refreshIdentityMapResult (see Section 108.16.5, "How to Refresh the Cache") or
Descriptor methods
alwaysRefreshCache and
disableCacheHits (see Section 119.9, "Configuring Cache Refreshing").
For more information, see Section 22.214.171.124, "Managing Cache Access".
If the external application can update a version field in the database, your TopLink application could use
alwaysRefreshCache in conjunction with
Descriptor method
onlyRefreshCacheIfNewerVersion to ensure that refresh operations are performed only when required. Another, recommended way to achieve this, is to use the descriptor isolated cache option (see Section 102.2.7, "Cache Isolation"), as well as cache invalidation (see Section 102.2.5, "Cache Invalidation").
Consider multiple TopLink applications (each running on its own application server instance) configured to use a distributed, coordinated cache (as described in Section 102.3, "Cache Coordination"). A TopLink application instance first commits changes to its own cache before the change is distributed to other caches. Because cache coordination is not instantaneous, there is a possibility that one TopLink application instance may read an older version of an object from its cache before a cache coordination message is received.
To provide your TopLink application with the most up-to-date version of an object use the descriptor isolated cache option (see Section 102.2.7, "Cache Isolation"), as well as cache invalidation (see Section 102.2.5, "Cache Invalidation").
You can also avoid stale data by using
Descriptor methods
alwaysRefreshCache and
disableCacheHits. For more information on the
disableCacheHits method, see Section 18.104.22.168, "Managing Cache Access".
Descriptor methods
alwaysRefreshCache and
disableCacheHits will result in frequent database hits. Use only when absolutely necessary.
Use the
DatabaseLogin method
setTransActionIsolation to configure the database transaction isolation level that TopLink applies to any database connection it obtains, for example:
This method sets the transaction isolation level used for both database read and write operations on the database connections obtained from either an internal or external connection pool (see Section 220.127.116.11, "Connection Pools"), for both internal transactions and external transactions as in the case of CMP.
However, with TopLink, by default read operations use a different database connection than write operations, typically obtained from an external connection pool, or may use the cache, bypassing the database entirely. Thus, with TopLink, by default, read operations are always performed outside the transaction or unit of work, even if you perform the read operation within a transaction or unit of work. Although database transaction isolation applies to both read and write connections, the read is not performed as part of the transaction. Therefore, the read operation overrides the transaction isolation set on the database.
Depending on the level of transaction isolation you are trying to achieve, you may require that the same transaction isolation be applied to both read and write operations. You must take special action to make TopLink use the same connection for both read and write operations. For more information, see Section 188.8.131.52, "Reading Through the Write Connection".
Recall that TopLink, by default, performs read operations with a different database connection than used for write operations (Section 184.108.40.206, "DatabaseLogin Method setTransactionIsolation"). However, from the perspective of database transaction isolation, there is a one-to-one relationship between transaction and database connection: that is, all database operations (including read operations) must use the same database connection in order to achieve a particular database transaction isolation level.
In general, when TopLink performs a read operation, if a write connection already exists, TopLink will use the write connection for the read operation. This is called "reading through the write connection." If a write connection does not yet exist, TopLink will acquire another connection and use that for the read operation.
You can configure TopLink to allocate a write connection early using any of the following:
Caution:Depending on the database transaction isolated level reading through the write connection may lock the object being read. This will affect performance and reduce concurrency. Oracle recommends that you do not use these advanced techniques unless strict database transaction isolation is absolutely necessary.
For more information, see Section 22.214.171.124, "CMP and External Transactions".
When you use pessimistic locking (
ObjectLevelReadQuery methods
acquireLocks or
acquireLocksWithoutWaiting or
Session method
refreshAndLockObject), TopLink does the following:
Allocates a write connection used for both read and write operations.
Always reads from the database.
Always updates the cache with the database version.
This method is advanced API. If you call
beginTransactionEarly on an instance of a unit of work, all read operations should be performed through that instance of the unit of work.
This method starts a database transaction immediately: any objects you read will lock data in the database before commit time, reducing concurrency.
Client sessions can access the data source using a connection pool or an exclusive connection. To use an exclusive connection, acquire your client session using a
ConnectionPolicy (see Section 90.4.2, "How to Acquire a Client Session that Uses Exclusive Connections").
If you are using isolated client sessions (see Section 87.5, "Isolated Client Sessions"), you can use exclusive connections for reading isolated data. In this case, you can configure TopLink to acquire an exclusive connection from the write connection pool and use it for both writing and reading isolated data. However, TopLink still acquires a shared connection from the read connection pool for reading nonisolated data.
For more information, see Exclusive Write Connections.
By default, TopLink uses the shared session cache as much as possible. Doing so increases concurrency and improves performance. However, to achieve a particular transaction isolation level, you may need to avoid the cache using some or all the following:
This method always goes to the database for the initial read operation of an object whose descriptor is configured as isolated. By avoiding the shared session cache, you do not need to use the more complicated descriptor and query APIs to disable cache hits or always refresh. For more information about isolated client sessions, see Section 87.5, "Isolated Client Sessions". This is particularly useful for achieving serializable transaction isolation (see Section 115.15.5, "What You May Need to Know About Serializable Read Levels").
This API goes to the database unless it is a primary key-based query, in which case it will go to the cache first. For information on how to avoid the cache entirely in this case, see Section 188.8.131.52.4, "Descriptor Method disableCacheHits".
This API always goes to the databases. For information on how to avoid the cache entirely in this case, see the description of the
Descriptor method
alwaysRefreshCache in Section 102.4.2, "Cache Refresh API".
This API allows for cache hits on primary key, read-object queries to be disabled. This can be used with the
Descriptor method
alwaysRefreshCache to ensure queries always go to the database.
This is a query-level means of preventing objects from being added to the shared session cache. Using an isolated client session (see Section 22.214.171.124.1, "Isolated Client Session Cache") is a simpler approach to achieving the same ends.
In general, the transaction isolation information in this section applies to both CMP and non-CMP applications, with the following exception:
For application servers other than OC4J, when using a TopLink application with CMP, Oracle recommends that you configure your container to use separate read and write connection pools, and to associate only the write connections with an external transaction. This means the read connections do not participate in the transaction.
Note:OC4J always uses the same connection pool for reading and writing: it uses JTA connections from that pool for writing, and non-JTA connections from the pool for reading.
However, because TopLink treats EJB finders as just another type of query, you can use your descriptor configuration to exploit the options described in Section 184.108.40.206, "Reading Through the Write Connection". For example, if you configure a descriptor to use pessimistic locking (see Section 119.26, "Configuring Locking Policy"), then when its finder is invoked it will allocate a write connection early and both read and write operations will use the same connection.
Refer to Section 96.1.1, "Externally Managed Transactional Data Sources" for more information on external transactions with transactional data sources.
Oracle does not recommend using this transaction isolation level.
In general, a read uncommitted operation is not necessary. Using TopLink, a transaction isolation of read committed gives you better performance than read uncommitted but with greatly improved data integrity.
Using the unit of work guarantees that you will read only committed data in the shared session cache or committed data in the database.
To achieve repeatable read operations, you must use a unit of work, you must register all objects in the unit of work (both objects you intend to modify and objects you intend only to read), and you must use
ObjectLevelReadQuery method
conformResultsInUnitOfWork or
Descriptor method
By doing so, each time you query a registered object, you will get the version of the object as it currently is in your unit of work.
To achieve serializable transaction isolation with TopLink, Oracle recommends that you use an isolated client session (see Section 87.5, "Isolated Client Sessions") as follows:
Configure the database transaction isolation as serializable.
Configure objects as isolated (see Section 117.11, "Configuring Cache Isolation at the Project Level" or Section 119.13, "Configuring Cache Isolation at the Descriptor Level").
Use the
UnitOfWork method
beginTransactionEarly (see Section 184.108.40.206.2, "Unit of Work Method beginTransactionEarly").
If you are only concerned about the write aspect of serializable, optimistic locking is sufficient.
To prevent phantom read transactions (that is, when a transaction detects that new records that have been added to the database after the transaction started), use the
ReadQuery method
This section examines common unit of work problems and debugging techniques, and describes the following:
How to Determine Whether or Not an Object Is the Cache Object
A common unit of work error is holding on to clones after commit time. Typically the clones are stored in a static variable and you incorrectly believe that this object is the cache copy. This leads to problems when another unit of work makes changes to the object and what you believe is the cache copy is not updated (because a unit of work updates only the cache copy, not old clones).
Consider the error in Example 115-13. In this example you get a handle to the cache copy of a
Pet and store it in the static
CACHE_PET. We get a handle to a working copy clone and store it in the static
CLONE_PET. In a future unit of work, the
Pet is changed.
If you incorrectly store global references to clones from units of work, you often expect them to be updated when the cache object is changed in a future unit of work. Only the cache copy is updated.
Example 115-13 Incorrect Use of Handle to Clone
// Read a Pet from the database, store in static CACHE_PET = (Pet)session.readObject(Pet.class); // Put a clone in a static. This is a bad idea and is a common error UnitOfWork uow = session.acquireUnitOfWork(); CLONE_PET = (Pet)uow.readObject(Pet.class); CLONE_PET.setName("Hairy"); uow.commit(); //Later, the pet is changed again UnitOfWork anotherUow = session.acquireUnitOfWork(); Pet petClone = (Pet)anotherUow.registerObject(CACHE_PET); petClone.setName("Fuzzy"); anotherUow.commit(); // If you incorrectly stored the clone in a static and thought it should be // updated when it is later changed, you would be wrong: only the cache copy is // updated; NOT OLD CLONES System.out.println("CACHE_PET is" + CACHE_PET); System.out.println("CLONE_PET is" + CLONE_PET);
The two
System.out calls produce the following output:
CACHE_PET isPet type Cat named Fuzzy id:100 CLONE_PET isPet type Cat named Hairy id:100
In Section 114.3, "Modifying an Object", it was noted that it is possible to read any particular instance of a class by executing:
There is also a
readObject method that takes an object as an argument: this method is equivalent to doing a
ReadObjectQuery on the primary key of the object passed in. For example, the following code is equivalent to the code in the subsequent example:
The following is equivalent to the preceding code:
ReadObjectQuery query = new ReadObjectQuery(); query.setReferenceClass(Pet.class); ExpressionBuilder builder = new ExpressionBuilder(); Expression exp = builder.get("id").equal(pet.getId()); query.setSelectionCriteria(exp); session.executeQuery(query);
Also note that primary key-based queries, by default, will return what is in the cache without going to the database. As a result, you can use very quick and simple method for accessing the cache copy of an object, as shown in Example 115-14.
Example 115-14 Testing If an Object Is the Cache Object
//Here is a test to see if an object is the cache copy
boolean cached = CACHE_PET == session.readObject(CACHE_PET);
boolean cloned = CLONE_PET == session.readObject(CLONE_PET);
System.out.println("Is CACHE_PET the Cache copy of the object: " + cached);
System.out.println("Is CLONE_PET the Cache copy of the object: " + cloned);
This code produces the following output:
Is CACHE_PET the Cache copy of the object: true Is CLONE_PET the Cache copy of the object: false
The unit of work has several debugging methods to help you analyze performance or track down problems with your code. The most useful is
printRegisteredObjects, which prints all the information about known objects in the unit of work. Use this method to see how many objects are registered and to make sure objects you are working on are registered.
To use this method, you must have log messages enabled for the session that the unit of work is from. Session log messages are disabled by default. To enable log messages, use the session
logMessages method. To disable log messages, use the session
dontLogMessages method, as shown in Example 115-15.
Example 115-15 Dumping the Contents of a Unit of Work
session.logMessages(); // Enable log messages UnitOfWork uow = session.acquireUnitOfWork(); Pet petClone = (Pet)uow.readObject(Pet.class); petClone.setName("Mop Top"); Pet pet2 = new Pet(); pet2.setId(200); pet2.setName("Sparky"); pet2.setType("Dog"); uow.registerObject(pet2); uow.printRegisteredObjects(); uow.commit(); session.dontLogMessages(); // Disable log messages
This example produces the following output:
UnitOfWork identity hashcode: 32373 Deleted Objects: All Registered Clones: Key: [100] Identity Hash Code:13901 Object: Pet type Cat named Mop Top id:100 Key: [200] Identity Hash Code:16010 Object: Pet type Dog named Sparky id:200 New Objects: Key: [200] Identity Hash Code:16010 Object: Pet type Dog named Sparky id:200
This section explains how to handle the following:
TopLink exceptions are instances of
RuntimeException, which means that methods that throw them do not have to be placed in a
catch block.
However, the unit of work
commit method is one that should be called within a
catch block to deal with problems that may arise.
Example 115-16 shows one way to handle unit of work exceptions:
Example 115-16 Handling Unit of Work Commit Exceptions
UnitOfWork uow = session.acquireUnitOfWork(); Pet petClone = (Pet)uow.registerObject(newPet); petClone.setName("Assume this name is too long for a database constraint"); // Assume that the name argument violates a length constraint on the database. // This will cause a DatabaseException on commit try { uow.commit(); } catch (TopLinkException tle) { System.out.println("There was an exception: " + tle); }
This code produces the following output:
There was an exception: EXCEPTION [ORACLEAS TOPLINK-6004]: oracle.toplink.exceptions.DatabaseException
If you use optimistic locking, you must catch exceptions at commit time because the exception raised is the indication that there was an optimistic locking problem. Optimistic locking allows all users to access a given object, even if it is currently in use in a transaction or unit of work. When the unit of work attempts to change the object, the database checks to ensure that the object has not changed since it was initially read by the unit of work. If the object has changed, the database raises an exception, and the unit of work rolls back the transaction. For more information, see Section 188.8.131.52, "Locking and the Unit of Work".
If you are using an external transaction service, exceptions may be thrown long after your
UnitOfWork code has returned. Using
UnitOfWork method
writeChanges, you can catch and handle most exceptions before the external transaction is committed. For more information, see Section 126.96.36.199, "Handling External Transaction Commit Exceptions".
You can conform query results in a unit of work across one-to-many relationships and a combination of both one-to-one and one-to-many relationships. Example 115-17 illustrates a query across two levels of relationships, one-to-many and one-to-one.
Example 115-17 Querying Across Two Levels of Relationship
Expression exp = bldr.anyOf("managedEmployees").get("address").get("city").equal("Perth");
By default, any exceptions thrown during conforming are suppressed. However, you can use the
UnitOfWork method
setShouldThrowConformExceptions to make the unit of work throw all conforming exceptions. This method takes one
int argument with the following values:
0–do not throw conform exceptions (default)
1–throw all conform exceptions
For more information on customizing exception handling when using conforming and in-memory queries, see Section 18.104.22.168, "Handling Exceptions Resulting from In-Memory Queries".
The unit of work validates object references at commit time. If an object registered in a unit of work references other unregistered objects, this violates object transaction isolation, and causes TopLink validation to raise an exception.
Although referencing unregistered objects from a registered object can corrupt the session cache, there are applications in which you want to disable validation. TopLink offers the following APIs to toggle validation:
dontPerformValidation: disables validation
performFullValidation: enables validation
If the unit of work detects an error when merging changes into the session cache, it throws a
QueryException. Although this exception specifies the invalid object and the reason it is invalid, it may still be difficult to determine the cause of the problem.
In this case, you can use the
validateObjectSpace method to test registered objects and provide the full stack trace of all traversed objects. This may help you more easily find the problem. You can call this method at any time on a unit of work.


text:
Vinyl Liner Repair
Save money by repairing your vinyl liner instead of replacing it. We offer a huge variety of vinyl liner repair supplies like Vinyl Patch Kits, Vinyl Liner repair, Flexible Vinyl Sealer that will help repair your pool liner as well as pool covers, inflatables, toys,and anything else made of vinyl.
Your pool's liner is a sheet of vinyl between the water and the pool structure and is meant to fit tightly into the shape of the pool. Vinyl liner not only extends your pool's life by protecting its structure, it also adds a unique touch to your pool Vinyl liners are expensive and are not easily replaceable so it's a good idea to keep some vinyl liner repair supplies on hand, and fix the minor nips, cuts, wear and tear.
One of the major issues pool owners face with vinyl liners is cuts and leaks. Vinyl liners tend to lose their resiliency with time and endless exposure to sun and harsh weather as a result of which they start to tear. No worries, this is a common problem and can be easily fixed with the high quality vinyl liner repair patches and sealants that Pool Center has to offer. Our vinyl liner repair kits are high quality repair kits that can be used both underwater and out of water, and can also be used to repair pool covers, inflatables, toys, and anything else made of vinyl. Inexpensive and easy to use, our vinyl liner repair kits will help repair all kinds of rips and tears, and will prevent leaking. We also carry vinyl liner sealers that can be used to seal small cracks in cracks in PVC, fiberglass, ceramics, plaster and gunite without having to use a repair patch.

subset: commoncrawl


text: After Escaping a Boko Haram Onslaught, These Farmers Begin a New Season With Hope By Richard Zack Taylor on March 21, 2017 1 minutes read time Once displaced by war, thousands of Nigerian farmers are cultivating again. Mary Utsewa gripped an ear of maize so large she could barely get her hand around it. Standing among bright green cornstalks swaying… more


text:
Call +917029599180 [ 10:00 AM To 10:00 PM ] Dismiss
Signup to get Rs 50 Discount Coupon & notified about sales and new products.
No products in the cart.
Showing all 4 results
Dahi Bora (2 pcs)
Eidli (4 pcs)
Masala Dosa (1 plate)
Username or email address *
Password *
Log in
Remember me
Lost your password?
Email address *
Password *
Your personal data will be used to support your experience throughout this website, to manage access to your account, and for other purposes described in our privacy policy.


text:
Double-concave (DCV) lenses have two inward, equally curved surfaces and a negative focal length. They are used for image reduction and to spread light.
Commercial Grade Uncoated Double-Concave (DCV) Lenses offer commercial quality, at an affordable price. Commercial grade optics have standard designs and are available on an ongoing basis. They are recommended for volume buyers or OEMs.


text:
Antony Hewish, (born May 11, 1924, Fowey, Cornwall, Eng.), British astrophysicist who won the Nobel Prize for Physics in 1974 for his discovery of pulsars (cosmic objects that emit extremely regular pulses of radio waves).
Hewish was educated at the University of Cambridge and in 1946 joined the radio astronomy group there led by Sir Martin Ryle. While directing a research project at the Mullard Radio Astronomy Observatory at Cambridge in 1967, Hewish recognized the significance of an observation made by a graduate assistant, Jocelyn Bell. He determined that the regularly patterned radio signals, or pulses, that Bell had detected were not caused by earthly interference or, as some speculated, by intelligent life forms trying to communicate with distant planets but rather were energy emissions from certain stars. For this work in identifying pulsars as a new class of stars, he was awarded jointly with Ryle the Nobel Prize for Physics in 1974, the first time the prize had been given for observational astronomy.
Hewish was professor of radio astronomy at the Cavendish Laboratory, Cambridge, from 1971 to 1989.


text:
Sunday, 11 July 2010
Rouge Bunny Rouge Powder and Liner Brush
After buying the blush brush I bought the powder and liner brush.
Picture below of the Powder and Blusher brush for size comparison
The powder brush 001 (£44) is the largest brush RBR do, its made of natural goat hair. Like the blusher brush it feels very soft, lightweight and delicate. I was surprised by just how flat this was compared to the usual full rounded shape typical of powder brushes.
Picture of side on view
I find the RBR one much more versatile, I can use the tip to apply powder to a more specific area, use it side ways for bronzer and highlighter and use it full on for powder. Where I truly notice a difference in performance was when I went to powder over a break out.
If you have dry skin and acne you will know the woes of trying to conceal it, you need it to last all day so powder is a must over the concealer, however putting powder over flaky inflamed skin is never a good thing. I don't know how but this brush allows me to lay powder down so that it's fine enough to cover but not to highlight the dry area, almost an invisible finish.
The eye liner brush 008 (£22) is not a shape I am use to, I either have slanted ones or the very thin long ones.
This is describes as a "conical" shape, although firm it feels very comfortable. Where I find this excels is with cake or gel eyeliners, it applies them a lot more cleanly compared to my other brushes and is great for getting into the little corners if you are tight lining. The brush head is nylon.
Rouge Bunny Rouge is stocked exclusively by Zuneta.com
7 comments:
Thank you for your post on these! I've got some brushes myself but not the ones that you've just reviewed. I'm the same, i want them all, they are so great quality! First I was concerned because my brushes lost a lot of 'hair' but those must've been excess ones. I'm washing mine with mild shampoo regularly and they just stay in shape, love'em :) xxxReplyDelete
The liner one is such an interesting shape! These are terrifyingly expensive though :PReplyDelete
The liner brush has got me excited. Having small, hooded eyes, lining is always tricky for me. I think I may have to try it, as I find flat brushes rather difficult to use.ReplyDelete
I am currently using an E. Arden one (yeah, odd choice, I know) that is similarly shaped and seems to do the trick, but the quality is far from the best...
Oh, and I'm trying to save for the Suqqu powder brush (and you know that purchase is going to hurt lol)
Have a great week!
lovely but so pricey :SReplyDelete
Hi Sam,ReplyDelete
I just bought the eyeshadow one as well, which ones do you have ? I'm glad you got the eyeshadows to work better for you :)
Hello Lillian,
Yes, I try not to think about the price, I do notice the difference in quality though.
Hello Nina,
Let me know if you get it, I'll be interested to hear your thoughts. I've never owned anything E.Arden, just doesn't appeal. How much is the Suqqu brush,dare I ask.... x
Hi Get Gawjus,
I'm sure the price puts a lot of people off, that said Zuneta have done the occasional 20% off promo so that would be a good time to get them :)
yes, the e/s work for me now, I'm so glad!! I've got the Face Contour 012 (love it!!), the Large Shader 003 (my fav e/s brush), the Crease 011 (could be a bit softer in my opinion), and the Eye Liner 008 (which I don't use often - I should've got a slightly thicker one for my purpose...) They are all very reliable and look their best even after I washed them for so many times! Want 'em all :)
Hi Sam,ReplyDelete
I really want the contour one too, although I don't actually contour my face but I really like the look of it! wonder if it could be used for highlighter/blush instead maybe ?


text:
How will you be able to tell when the Fifty Shades of Grey baby-boom finally hits? SkaHimself offered a plausible, if, um, graphic, answer:
You'll know its a greybaby when it's birthed with the umbilical cord wrapped around it's neck while it's pleasing itself at the same time.
"Don't cut the cord you fucker! I'm almost there!"
Submit nominations to the Comment of the Day tag page. Click on the comment's time stamp, and post the comment's URL to the forum. (Replying to a comment with "#cotd" does not work; you need to go to the forum.) For meetups, use the meetup forum!


text:
The Tiffany-ed Maria Sharapova has faced some close calls in the first three rounds of this year's French but managed to eke out wins so far (and now it'll get even easier with the Williams sisters out of the way).
It was a battle of fashion good vs. evil yesterday when Masha took on Bethanie Mattek in the second round. They played over two days, and Bethanie actually had a fairly respectable outfit from Bebe Sport for day one.
And the match (postponed due to darkness) resumed with Bethanie wearing her pink hooker dress which we first saw in March at Indian Wells.
God. Bless. America.
And her third-round opponent, Karin Knapp, gave a fight in the first set (even intimidating Masha with some second serve mindgames ala Azarenka) but sputtered 6-0 in the second. Oh well, at least she looked great in her pink and silver Fila separates. Fabric belts = trend du jour.
(Photos by Getty Images)


text:
Annual COLLOQUIUM ON AGING:
IOA Newsletter,
MIDUS newsletter: Cancer Survivors
in MIDUS
Over the past 100 years, we have witnessed enormous change in the size of the aging population in the U.S. - from around 1 out of every 25 persons in the early 1900s to around 1 out of every 4 or 5 persons in the 21st century. Because older adults represent the fastest growing segment of society, our universities must provide leadership, training, and scientific advances to meet their diverse needs.
At the UW-Madison Institute on Aging, we are jointly focused on addressing the problems of aging, which include diseases and impairments (e.g., osteoporosis, dementia, Parkinson's, glaucoma, mobility problems) and the challenges of later life (e.g., widowhood, retirement, caregiving, relocation) as well as the potential of aging, which refers to the notable strengths, resources, and vitality of those in their 70s and beyond. Our goal is to understand the many factors (biological, psychological, social) that promote resilience as people age and to translate this knowledge to innovative educational and community intervention programs. As such, we see the present as a time of unprecedented opportunity to change the fundamental meaning of "old age."
the News:


text:
Marguerites - Aramis Yellow x6 Plants Growing Chart
- Jan
- Feb
- Mar
- Apr
- May
- Jun
- Jul
- Aug
- Sep
- Oct
- Nov
- Dec
- Flowers
Our products are all grown with the utmost care and to high standards of quality. We regularly check our nursery stock to determine exactly when they will be at the optimum time for despatch.
We despatch our fresh, nursery-grown live plants (including vegetables, trees and flowers) when they are at a stage of growth ready for you, the customer, to plant on at home.
The delivery period shown on your order confirmation indicates when you can expect to receive your order and we will endeavour to despatch your plants in this period within 7-14 days
Customer Reviews
Looked awful when arrived looking a bit better now
Too small not what I was expecting
3 out of the ten did not survive planting on despite doing everything as instructed.
Marguerites - Aramis Yellow x6 Plants
£8.99 each
Marguerites 'Aramis Yellow' is a compact bushy plant, studded with brilliant yellow coloured flowers all summer.
These delightful daisies like plants just live to flower and no summer garden is complete without them! They have lovely colours and neat habit and are so easy to grow they seem to just go on and on, no matter how you treat them.
Perfect in pots and exceptional in the border, with a bit of dead heading they will keep on flowering until the first frost.
Plant into good quality, reliably moist compost in window boxes, containers and hanging baskets. Marguerites 'Aramis Lemon' is a tender perennial that can be overwintered.
See Full Description +
- Quantity 6x Young Plants
- Height 45cm/17.7in
This Item is Still Growing...
Despatched from Early May
Stock Status: Pre order


text:
Volunteer South Africa with
Make a real difference and join one of AVIVA's rewarding volunteer projects.
Immerse yourself in a vibrant new culture and contribute to the welfare of underprivileged children and endangered wildlife in South Africa.
Your application includes free fundraising assistance and an online trip planner while preparing for your trip.
Book now for 2018/19 and take advantage of the AVIVA Price Promise - You won't find better value, service or support.
Volunteer in Cape Town
Cape Town is the ideal volunteering location with an unrivaled variety of spectacular scenery, adventurous activities and attractions to keep you busy during your free time.View Projects
Volunteer with Children
Provide essential support for care workers at well run children's homes, help feed underprivileged children, or assist teachers in special needs classrooms.View Projects
Wildlife & Marine Conservation Work
Get your hands dirty helping to conserve endangered African Penguins and Great White Sharks, live on an Indian Ocean Island and contribute to Whale Shark conservation, and get involved in.View Projects


text:
I have always been on the Girl Team. I was raised on School House Rock, Cosmopolitan magazine, and the gospel according to Helen Reddy.
So when I saw the announcement that Defense Secretary Leon Panetta is removing the military's ban on women serving in combat and potentially opening nearly of a quarter of a million jobs to women, I was glad.
Because I want another Sal. And this time, I'd like that Sal to be a woman.
Can you tell I'm in the middle of reading Medal of Honor recipient Staff Sgt. Sal Giunta's biography Living With Honor? The guy impresses me. He ain't no John Wayne. No Army Ranger. He isn't the direct descendent of Stonewall Jackson or Audie Murphy. "I am not a hero," Giunta writes. "Just a soldier."
As a member of the Girl Team, that is exactly the kind of spirit we need in training women who will serve in combat. Whether they will continue in those jobs downrange they have worked for years, or take one of the newly opened combat jobs, we need these women to be soldiers and Marines. Not female soldiers or Marines. Just plain soldiers and Marines.
I know I am probably an idealist here. But if we Girl Teamers really want combat opened to women, then I think we need to repeat a lot of the steps outlined in Giunta's story.
First, we have to set aside the civilian idea that the job of a combatant is a job that is open to just anyone. As Giunta's story reveals, no job in the military is open to just anyone. You have to qualify.
If we Girl Teamers want women to be taken seriously in a physical job like the infantry, the physical standards for men and women have to be the same. Yeah, it will be harder to find women who can reach the same physical qualifications as men. Infantry standards should just be infantry standards, not standards adjusted for gender. We need to be good with that.
Next, we have to make sure that our combatant roles attract women with the right kind of personality. Giunta writes:
"See, the thing about the infantry is that it attracts fighters. There are a lot of people in the U.S. Army, but not a lot who are guaranteed to see combat duty…I wanted to join for one reason: to learn how to shoot my weapon more proficiently, and with greater accuracy, than the person I was shooting at, so that I could kill him and then move on and kill some of his friends, because they were all enemies of the United States. If that sounds barbaric, well, it was exactly what the infantry wanted: people who were eager to fight."Just like there are differences between men who elect to serve in different roles in the military, there will also be differences in what jobs women will be drawn to in the military.
We may find that particular personality-driven eagerness to fight and kill the enemy may not be as common to females as it is to males. So be it. When we recruit women who are fighters at heart, let them be trained to do the job.
Finally, we media types can't let women in combat roles be one of those numbers we tend to so carefully with our political correctness stick. As a member of the Girl Team, I will have to remind myself that because of the way women are socialized in this country and because of some very real physical differences (like, say, height) not as many women will serve in these jobs.
If we insist on some theoretical, numerical equality — and get it -- then we don't end up with the right kind of soldiers. In our ever changing world, we need combatants who are aggressive and confident and can be trained to be the kind of skilled fighters who face the enemy. We need more Sal Giuntas. More Josh Brennans. More Hugo Mendozas. More good soldiers like the ones who fought and died in the Korengal Valley. And we need both males and females trained and able to do the job.