JDO s Transaction Interface


JDO's Transaction Interface

Each persistence manager has one and only one Transaction object associated with it. The transaction object gives the application control of one transaction at a time. As a result, the objects that a persistence manager controls can be associated with no more than one transaction at a time. The Transaction object associated with a persistence manager does not change while the persistence manager is open. For simplicity, a JDO implementation may have the same class implement both interfaces. Although the two interfaces Transaction and PersistenceManager are closely related, by having two interfaces, JDO separates the control of transactions from the other properties and operations of the persistence manager. Chapter 3 describes the PersistenceManager interface.

The class diagram in Figure 4-1 shows the three operations and eight properties of the Transaction interface. The following sections describe the methods that define these operations and properties.

click to expand
Figure 4-1: The class diagram of the Transaction interface

Controlling Transactional Boundaries

The Transaction interface provides three operations and one property to control the transaction's boundaries.

 public void begin() public void commit() public void rollback() public boolean isActive() 

When the application controls the transaction, the begin method starts a transaction. The commit method ensures that JDO saves in the datastore all (or in the event of failure, none) of the changes made within the transaction. The changes may include new values in the persistent fields of persistent objects and the addition or deletion of persistent objects. The rollback method ends the transaction without storing any of the changes in the datastore. Depending on the setting of the RestoreValues property, rollback may restore the values of dirty managed fields to the values that were present in memory when the object became dirty.

Note

A transactional field becomes dirty when its value changes within the transaction. A persistent field becomes dirty when its value changes within the transaction or when its object has been made persistent or deleted within the current transaction. A persistent or a transactional field becomes dirty when it refers to a mutable embedded object (such as a supported system class like Date or HashSet) and that object changes. An application data object becomes dirty when one or more of its managed fields become dirty.

From the time that a transaction begins until it ends with a call to either commit or rollback, the transaction's Active property is true. Outside of the transactional boundaries, the Active property is false.

If an operation in the Transaction fails, various JDO exceptions may be thrown. Chapter 7 details JDO's exception hierarchy. In the case of commit, if the exception is a JDOFatalDataStoreException, then JDO has rolled back the transaction. If the exception is some other type of JDOFatalException, then automatic rollback may have occurred. In any case, the transaction should be abandoned since there is no recovery possible. The other types of JDO exceptions can be retried if the application knows how to recover. When these exceptions are thrown, the transaction remains active. The simplest and safest way to deal with the various possibilities is to check the transaction's Active property in the catch block and perform the rollback, if needed, when the application either cannot or does not know how to recover.

The application cannot invoke the boundary operations (begin, commit, and rollback) when a container or a JTA TransactionManager is controlling the transaction. Any attempt to do so causes a JDOUserException. Incidentally, a similar restriction applies to the Connection interface in JDBC; it is not a limitation unique to JDO.

On the other hand, stand-alone, client-server, and Web applications that use JDO usually control a local transaction with JDO's Transaction interface. EJBs that are deployed for bean-managed transactions (BMT) can also control the transaction through JDO's Transaction interface. Chapters 6 and 9 through 11 describe how to use JDO in the various deployment environments.

The Read-Only PersistenceManager Property

The Transaction interface provides a getter method for the PersistenceManager property.

 public PersistenceManager getPersistenceManager() 

This method returns a reference to the persistence manager that is associated with the transaction. Each PersistenceManager object is in a one-to-one relationship with a Transaction object. This relationship is immutable as long as the persistence manager is open. As a result of the relationship between the PersistenceManager object and its Transaction object, the following expression is always true when the persistence manager pm is open:

 pm == pm.currentTransaction().getPersistenceManager() 

The Five Transactional Properties

The five Boolean properties Optimistic, NontransactionalRead, NontransactionalWrite, RetainValues, and RestoreValues are collectively called the transactional properties. Turning on any of these properties invokes an optional feature of the JDO implementation. When a particular transactional property is not supported, its value is always set to false. Attempts to set it to true, or to use a feature of JDO that would require it, yield a JDOUnsupportedOptionException.

Note

The supportedOptions method in the PersistenceManagerFactory interface, which Chapter 6 discusses, provides a list of the options that the implementation supports.

Some of these properties can be set when a transaction is active or inactive, and some can only be set when a transaction is inactive. None of these properties can be set during the time that the transaction is completing. In other words, these properties cannot be set within the callback methods of the Synchronization object, and they should not be set within the jdoPreClear or jdoPreStore methods of the InstanceCallbacks interface. If an attempt is made to set the transactional properties while the transaction is completing, the operation throws a JDOUserException. A later section of this chapter describes the transaction's Synchronization property, and Chapter 7 covers JDO's InstanceCallbacks interface.

These five transactional properties are also properties of the PersistenceManagerFactory interface. The Transaction object inherits the values of its transactional properties from the persistence manager factory that produced its persistence manager.

JDO allows the implementation to support none, any, or all of the transactional properties. As the 1.0.1 version makes clear, JDO prohibits dependencies between the transaction's properties. Changing the setting of one of these properties has no effect on the current settings, or possible settings, of the other properties.

The transactional properties maintain their values across transactional boundaries.

The Optimistic Property

The Transaction interface provides getter-setter methods for its Boolean Optimistic property.

 public boolean getOptimistic() public void setOptimistic(boolean optFlag) 

The application configures a datastore transaction by setting the transaction's Optimistic property to false. It configures an optimistic transaction by setting the property to true. The Optimistic property must be set before calling the begin method. Attempts to set the Optimistic property after the transaction has started results in a JDOUserException.

JDO requires support for datastore transactions. Optimistic transactions, on the other hand, are an optional feature of JDO.

When the application code runs inside a component whose container manages the transaction, the transaction has started by the time the application code gains control. In this case, the application must obtain a persistence manager factory that has the Optimistic property set to the desired value. In this way, the application obtains a persistence manager whose transaction is configured for the desired transaction type.

For transactions that include user think time, optimistic transactions are recommended for scalability. Because a JDO optimistic transaction may close the database transaction and release the database connection back to a connection pool between reads, the application may scale better due to JDO's more frugal use of datastore resources. It may also scale better due to better use of the caching built into the implementation. (See later sections of this chapter, starting with "JDO's Cache of Persistent Objects and Their State," for more discussion of caching.)

On the other hand, datastore transactions are also recommended for some situations. Not all JDO implementations offer optimistic transactions, so the only choice may be a datastore transaction. For JDO applications that must share their database schema with applications that do not use JDO, the JDO optimistic transaction may not detect changes made by the non-JDO applications. Whether this is the case depends on the JDO implementation's algorithm for calculating the concurrency value. When transactional collisions are expected to be frequent, a datastore transaction may be a better choice since the isolation level and locking strategy used tends to serialize the transactions rather than fail one of them. Datastore transactions, however, may also encounter deadlock when two datastore transactions attempt to change the same set of objects in a different sequential order. Finally, in the exceptional situation where inconsistent reads are a pervasive issue for the application, if the JDO implementation can be configured to use the serializable isolation level in the database transaction, then the datastore transaction is a good choice. In short, use optimistic transactions when possible and datastore transactions when necessary.

The NontransactionalRead Property

The Transaction interface provides getter-setter methods for its Boolean NontransactionalRead property.

 public boolean getNontransactionalRead() public void setNontransactionalRead(boolean ntrFlag) 

The NontransactionalRead property affects the behavior of JDO only when a transaction is not active. It has no effect on the behavior of JDO when a transaction is active. When the implementation supports the option of turning on this property, it may be set to true or false at any time.

The NontransactionalRead property determines whether JDO allows read access to the persistent fields of persistent objects outside of transactions. It also determines whether JDO allows access to the datastore when a transaction is not active. When NontransactionalRead is true, the application can execute queries outside of transactions. Likewise, it can iterate extents and the results collections returned from queries, and it can access the persistent fields of persistent objects. If NontransactionalRead is false, these operations produce a JDOUserException.

When the application accesses a persistent field of a persistent object outside of a transaction while the NontransactionalRead property is true, the managed state of the application object either is or becomes persistent-nontransactional. As a result of nontransactional access, the persistent fields of the data object reflect the persistent state in the datastore as of some indeterminate point in the past. The state may have been loaded as a result of the current access, or it may have been loaded at any point since the persistence manager was obtained. If the object was transactional in a previously committed transaction, then its persistent state is no older than that transaction.

Note

A persistent application data object becomes transactional when it is modified, deleted, made persistent, or made transactional within a transaction. It also becomes transactional when its persistent fields are accessed in a datastore transaction. It does not become transactional when its persistent fields are accessed in an optimistic transaction.

The JDO implementation does not hold any database locks for a persistent-nontransactional object.

The NontransactionalWrite Property

The Transaction interface provides getter-setter methods for its Boolean NontransactionalWrite property.

 public boolean getNontransactionalWrite() public void setNontransactionalWrite(boolean ntwFlag) 

The NontransactionalWrite property affects the behavior of JDO when a transaction is not active. It has no effect on the behavior of JDO when a transaction is active. When the implementation supports the option of turning on this property, it may be set to true or false at any time.

The NontransactionalWrite property permits writes to the persistent fields of persistent objects outside of transactional boundaries. When the write occurs, the application data object either is or becomes persistent-nontransactional. The changes that occur nontransactionally cannot be later stored in the datastore. JDO expressly prohibits storing nontransactional changes made to persistent-nontransactional objects. In essence, the JDO implementation must reload the persistent state of modified persistent-nontransactional objects if they later become transactional.

The practical uses of setting the NontransactionalWrite property to true are likely to be very specialized.

The RetainValues Property

The Transaction interface provides getter-setter methods for its Boolean RetainValues property.

 public boolean getRetainValues() public void setRetainValues(rtvFlag) 

The RetainValues property affects the behavior of JDO when a transaction commits. It has no effect otherwise. It controls whether JDO discards or retains the persistent state of application data objects that are persistent-clean, persistent-new, persistent-dirty, persistent-deleted, or persistent-new-deleted when the transaction commits. These are the five management states that are both transactional and persistent.

When the transaction commits and RetainValues is false, JDO clears the persistent fields of the objects that are both transactional and persistent after it updates the datastore. The persistent-deleted and persistent-new-deleted objects change to JDO-transient, while the persistent-clean, persistent-new, and persistent-dirty objects change to hollow. In effect, the persistent-clean, persistent-new, and persistent-dirty objects are automatically evicted on transaction commit.

Note

JDO clears the persistent fields by setting each persistent field to its Java default value. Boolean fields are set to false, numeric fields are set to zero, and reference fields are set to null. JDO discards the persistent state by clearing the persistent fields and also by discarding any values held for the object elsewhere in memory.

When the transaction commits and RetainValues is true, JDO retains the values in the persistent fields. The persistent-deleted and persistent-new-deleted objects change to JDO-transient, while the persistent-clean, persistent-new, and persistent-dirty objects change to the persistent-nontransactional state.

When the JDO implementation supports the option of turning on the RetainValues property, the application may set the property to true or false before or after the transaction is active.

Deciding Whether to Set RetainValues to True or False

When the application uses persistent objects within datastore transactions, it makes little difference whether the RetainValues property is true or false. The transactional semantics of the datastore transaction requires that the persistent value of an object's persistent field be reloaded from the datastore after the transaction starts and before the application accesses or modifies the field's value. Whether RetainValues is true or false, JDO discards the persistent state acquired in one transaction before the object is reused in a later datastore transaction.

When the application uses optimistic transactions, it makes sense to set the RetainValues property to true. There are two reasons to do this. First, newly loaded persistent objects are persistent-nontransactional in an optimistic transaction until they are modified, deleted, or explicitly made transactional. Since the persistent-nontransactional objects are not transactional, JDO does not automatically evict their persistent state when the transaction commits and RetainValues is false. In most applications that use optimistic transactions, the nontransactional objects are the bulk of the objects loaded. Second, when the persistent-nontransactional objects become transactional, they are verified to be up-to-date when the transaction commits. Since a common cache management strategy is to discard the least recently used cached state, it would generally be counterproductive to evict the most recently used cached state. As a result, it usually does not make sense to set RetainValues to false when using optimistic transactions.

When the application uses both nontransactional reads and transactions, setting RetainValues to true ensures that the persistent state acquired in the transaction remains available outside the transaction. As a result, the application may gain a performance benefit due to caching. Later sections of this chapter, starting with "JDO's Cache of Persistent Objects and Their State," examine JDO's caching and the circumstances that may yield performance benefits.

The RestoreValues Property

The Transaction interface provides getter-setter methods for its Boolean RestoreValues property.

 public boolean getRestoreValues() public void setRestoreValues(rsvFlag) 

The RestoreValues property controls whether the persistent fields of transactional objects are restored, when the transaction rolls back, to their state at the beginning of the transaction. If the RestoreValues property is false, JDO may leave the persistent fields of transactional objects as they were when the transaction rolled back, or it may clear them to their Java default values.

The application must set the RestoreValues property to true before the transaction begins. Any attempt to set the property's value after the transaction starts yields a JDOUserException.

There is only one RestoreValues property, but JDO defines both weak and strong support for this property. The implementation must offer weak support for the RestoreValues property, but strong support for the property is an implementation option. The implementation must offer strong support for the RestoreValues property when it supports any of the other four transactional properties. The supportedOptions method in JDOHelper does not return an option string to indicate strong support for the RestoreValues property.

When RestoreValues is false, JDO behaves the same regardless of whether strong or weak RestoreValues is supported. When RestoreValues is true, JDO does more when it supports a strong RestoreValues property than it does when it supports a weak RestoreValues property.

In the event of rollback, the persistent state in the datastore is never changed, and the setting of the RestoreValues flag has no influence in this regard. The RestoreValues property, whether weak or strong, affects only what happens to the state of transactional objects in memory.

Weak Support for the RestoreValues Property

Weak support for the RestoreValues property affects JDO's actions on objects that become persistent-new. It also affects JDO's actions during rollback on objects that are persistent-new or persistent-new-deleted. It has no effect otherwise.

When weak RestoreValues is true, JDO makes a before-image when the object becomes persistent-new. Upon rollback, JDO restores the managed fields of persistent-new and persistent-new-deleted objects from the before-image. When weak RestoreValues is false, JDO does not make a before-image when objects become persistent-new, and upon rollback, it leaves the managed fields of persistent-new and persistent-new-deleted objects as they were when rollback commenced. As a result of rollback, these objects become JDO-transient.

When weak RestoreValues is set to true, it has no effect on objects that are persistent-deleted, persistent-dirty, or persistent-clean. JDO clears the persistent fields in these objects upon rollback regardless of the setting of weak RestoreValues.

Note

In JDO, the before-image is a copy of the values of all the managed fields of the object. The managed fields are the fields that the JDO metadata identifies as either persistent or transactional. Chapter 5 describes the JDO metadata.

Strong Support for the RestoreValues Property

The strong RestoreValues property affects the behavior of JDO when an application data object becomes dirty and when a transaction rolls back. It has no effect otherwise. It controls three related behaviors, whether the object's before-image is saved when the object becomes dirty within a transaction, whether the object's managed state is restored from the before-image in the event of transaction rollback, and whether the object's persistent state is cleared after rollback.

When rollback occurs and strong RestoreValues is true, persistent-clean objects do not have a before-image. As a result, they retain the values of their persistent fields and become persistent-nontransactional.

When rollback occurs and strong RestoreValues is true, the persistent fields of objects in the following states are restored to the values in the object's before-image:

  • Persistent-dirty

  • Persistent-deleted

  • Persistent-new

  • Persistent-new-deleted

As a result, objects that are persistent-dirty or persistent-deleted become either hollow or persistent-nontransactional depending on whether they were hollow before they became dirty or deleted. As happens when weak RestoreValues is true, the persistent-new and persistent-new-deleted objects are restored from the before-image, and they become JDO-transient.

When rollback occurs and strong RestoreValues is false, the values in the managed fields of persistent-new and persistent-new-deleted objects are left as they were at the start of rollback. As a result of rollback, these objects become JDO-transient. The persistent fields of persistent-clean, persistent-dirty, and persistent-deleted objects are cleared to their Java default values. In effect, these objects are automatically evicted on transaction rollback. As a result of rollback and automatic eviction, the persistent-clean, persistent-dirty, and persistent-deleted objects become hollow.

Note

Eviction, whether it occurs automatically after commit or roll-back or results from a call to the persistence manager's evict method, should never evict objects that are not persistent at the start of the operation or the state of any object's transactional but nonpersistent fields. As mentioned in Chapter 3, you may find implementations that incorrectly evict the transactional fields along with the persistent fields.

When strong RestoreValues is true and the attempt to commit throws a JDOOptimisticVerificationException, the application has the chore of evicting the failed instances if it continues to use the persistence manager in which the failure occurred. Since automatic eviction does not occur on persistent-clean, persistent-dirty, and persistent-deleted application data objects unless RestoreValues is either weak or false, the failed objects are still associated with the old concurrency value that caused the failure.

Saving and Discarding the Before-Image

When the RestoreValues property is true, JDO may save a before-image. When RestoreValues is true and weak, JDO saves the before-image when the application data object becomes new. When RestoreValues is true and strong, JDO saves the before-image when the application data object becomes new or dirty. Objects may become new or dirty only when a transaction is active.

Application data objects become new when they are directly or indirectly affected by the persistence manager's makePersistent method. An object that becomes new is always dirty, but application data objects can become dirty in other ways as well. The application may modify a value in a managed field. The application may delete the object by calling the persistence manager's deletePersistent method. The application may call the makeDirty method in the JDOHelper class. Chapter 7 discusses the JDOHelper class.

When the time comes for JDO to save the object's before-image, JDO saves it only when the object does not already have one. For example, within the same transaction, a transient-clean object may become transient-dirty, then persistent-new, then persistent-new-deleted. JDO saves the before-image when the object becomes transient-dirty, and for that reason, it does not save a before-image when the object becomes persistent-new. On the other hand, if a JDO-transient object becomes persistent-new and then persistent-new-deleted within the same transaction, JDO saves the before-image when the object becomes persistent-new.

When the persistence manager's transaction ends, whether by commit or rollback, JDO discards the before-image of all transactional objects managed by the transaction's persistence manager. When the application calls the persistence manager's makeNontransactional, evict, or refresh methods, JDO discards the before-image of the affected objects.

Optimizations Available to the JDO Implementation

The implementation has several opportunities to optimize the work in saving and restoring the before-image. For example, if the object is hollow when it becomes dirty, the implementation may be able to represent the before-image in a more efficient manner than by making a copy of its hollow state. It might use a flag as the way to indicate that the before-image is the hollow state. Likewise, after a persistent object is deleted, JDO prevents any changes to its persistent state. Therefore, there is no need to make a before-image to restore. Nonetheless, since dirty objects can be deleted, the object when deleted may already have a before-image, which JDO would keep and use to restore from.

As Chapter 3 describes, when JDO makes an object persistent, it replaces the references in persistent fields to objects of the supported mutable system classes, such as Date and HashSet, with references to objects that are instances of the classes supplied by the JDO implementation, which subclass the system classes. When RestoreValues is true, the before-image has the reference to the original instance of the system class, and in the event of rollback, the reference to the original system class object is restored in the persistent field.

On the other hand, when the object is persistent-clean or persistent-nontransactional, the loaded persistent fields that refer to objects of supported mutable system classes already refer to objects of the implementation's subclasses. In some cases, it is not feasible or desirable to make a copy of these objects. For this reason, JDO specifies that the implementation does not include these fields in the before-image, and upon rollback, the value of these fields is set to null. Doing this is transparent to the application, because JDO reloads the values from the datastore when the application makes the next access to these fields. Although the specification is not clear, it is reasonable to expect that any transactional but not persistent field in a persistent object and any managed field in a transient-dirty object, even when the field is a supported mutable system type, is fully backed up in the before-image for use in the event of rollback.

Although the optimization strategies available to the implementation make the description of saving and restoring a before-image more precise, they are implementation details that you can ignore as an application programmer.

Synchronizing with Transaction Completion

The Synchronization interface is defined in the Java Transaction API. The Synchronization interface notifies the application when the transaction ends. As Figure 4-2 shows, the interface has two methods. JDO calls the beforeCompletion method when the transaction is starting the commit step, but never calls the beforeCompletion method when the transaction is starting a rollback step. JDO calls the afterCompletion method when the transaction has finished the commit or rollback step. JDO passes one of two values in the status parameter of the afterCompletion call.


Figure 4-2: The class diagram of the Synchronization interface

 javax.transaction.Status.STATUS_COMMITTED javax.transaction.Status.STATUS_ROLLEDBACK. 

The Transaction interface provides getter-setter methods for its Synchronization property.

 public javax.transaction.Synchronization getSynchronization() public void setSynchronization(javax.transaction.Synchronization syncObject) 

If the application wants to be notified when the transaction completes, it calls the setSynchronization method to register a Synchronization object. JDO calls this object's methods when the boundary events occur in the transaction. Since JDO itself is either controlling the transaction's boundaries or must be aware of the same boundary events, the Synchronization object that the application selects always receives notifications. The Synchronization callbacks occur whether the application controls the transactional boundaries through JDO's Transaction interface or whether a container controls the transactional boundaries.

The transaction holds a reference to only one Synchronization object. Setting the property to a new Synchronization object replaces the previous Synchronization object, if any, with the new one. Setting the property to null prevents callbacks to a Synchronization object. Calling the setSynchronization method within a Synchronization callback or during the time that a transaction is completing yields a JDOUserException.

Although many applications may not find the need to register a Synchronization object with the transaction, there are several scenarios that lead to using this interface. First, there may be a nontransactional step that needs to be taken when a transaction commits or rolls back. For example, a message might be sent or logged to a nontransactional message or logging service. Second, there may be unmanaged objects that need cleanup after a transaction commit. The application can use the afterCompletion callback to handle both of these scenarios. Third, there may be situations where the application wants to make information consistent before the commit. The application can use the beforeCompletion method to handle this need.

In some cases, the application may want to prevent the transaction's commit from succeeding. If the application code lives within an EJB whose transactions are managed by the container, the application can use the beforeCompletion callback in the Synchronization class to call the setRollbackOnly method in its bean's javax.ejb.EJBContext object. This action forces the container to roll back the transaction.

When the application itself controls the transactional boundaries through JDO's Transaction interface, the application should not attempt to reverse a commit in beforeCompletion. There is no setRollbackOnly method in JDO's Transaction interface. Instead, the application makes the decision whether to call commit or rollback based on all the information that it needs to consider. Thereafter, there is no reason to reconsider. Neither JTA nor JDO defines the expected behavior when an unchecked exception is thrown in the Synchronization callbacks.




Using and Understanding Java Data Objects
Using and Understanding Java Data Objects
ISBN: 1590590430
EAN: 2147483647
Year: 2005
Pages: 156
Authors: David Ezzio

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net