Core Java Data Objects

JDO defines optional states and behavior that may be implemented by a JDO vendor. These states help the developer to do things like use objects, although they may not contain transactional consistent data outside of active transactions. The states can also be used for transient objects that should act like persistent objects in an active transaction. Before any such optional features are used by an application, the JDO implementation's PersistenceManagerFactory.supportedOptions() method should be checked.

4.5.1 Transient-transactional instances

Transient instances of persistence-capable classes can become transactional, but not persistent, in the datastore by a call to PersistenceManager.makeTransactional . Those objects are called transient-transactional instances. Their data is somehow saved when the application calls makeTransactional and is restored when the transaction rolls back, but no data is stored in the backend. This feature is helpful when a complex operation changes many objects and the operation may be rolled back. The transient in-memory objects are also transactionally connected with other persistent objects of the same PersistenceManager instance.

Transient-transactional is an optional JDO feature; nevertheless, most of the vendors have implemented it at the time of this writing. To support lazy copy-on-write , a fresh transient-transactional instance enters the state transient-clean by a call to makeTransactional . Although its data is new or changed, this is only a marker for the PersistenceManager that any modification must be tracked. When the transient instance is modified, it becomes transient-dirty and a copy of its persistent fields is saved for later restore. There are two cases when makeTransactional can be invoked: before an active transaction is begun and afterward. In the first case, the data of the object it had at the time the transaction started will be saved. In the other case, the field data at the time makeTransactional was called will be saved.

Transient-dirty instances become transient-clean at either commit or rollback. During rollback, the previously saved values of managed fields are restored. Transient-clean instances can also be reset to non-transactional by invoking makeNontransactional . Both transient-clean and transient-dirty instances can be made persistent-new by a call to makePersistent .

The diagram in Figure 4-4 shows the state transitions for transient-transactional objects.

Figure 4-4. Lifecycle: Transient transactional instances.

4.5.2 Usages for transient-transactional instances

The basic idea for transient-transactional instances is transparency for the application. It is irrelevant whether or not an object is persistent; the state or data can be restored at rollback. An example is a GUI application that uses dialog boxes to display and edit configuration data. A dialog box may contain both persistent and local, transient configuration information. By using the transient-transactional feature, the implementation of the dialog box can simply roll back all data when "cancel" is clicked by the end- user .

4.5.3 Non-transactional instances

The idea behind non-transactional instances is to support slow changing, inconsistent data and optimistic transactions. In some scenarios, an application wishes to read data explicitly into objects, but doesn't care for modifications by other clients of the same datastore. This is the lowest isolation level with dirty read semantics. Non-transactional instances can also be the result of terminated transactions.

JDO defines four different properties that change the behavior of the JDO object lifecycle:

  • NontransactionalRead

  • NontransactionalWrite

  • RetainValues

  • Optimistic

The application has to set one or some of these properties to enable the specific options before the first use of a PersistenceManager. The reason for the options is safety. An application might modify an object by accident in a non-transactional context, but the modification gets lost or wrong data was read. Here is an example in which modification can get lost:

Keiron gets Book A.

Michael gets Book A.

Keiron updates Book A.

Michael updates Book A.

Michael commits transaction.

Keiron commits transaction (overwriting Michael's updates).

A similar problem can happen if data is updated between two consecutive reads:

Keiron counts all borrowed books.

Michael borrows a book.

Michael commits transaction.

Keiron counts all books in the library (with one book missing).

4.5.3.1 Non-transactional read

This option allows an application to read fields of hollow or non-transactional instances outside of transactions. It also enables navigation and queries outside of active transactions. The state to support non-transactional, inconsistent field data outside of active transactions is called persistent-non-transactional. An application can force a persistent-clean object to become persistent-non-transactional by invoking PersistenceManager.makeNontransactional . Consider the side effects and potential problems with non-transactional reads.

4.5.3.2 Non-transactional write

With this option set to true, modification of non-transactional instances is allowed outside a transaction. But changes to such objects are never written back to a datastore. JDO does not define how or when instances transition to persistent-non-transactional if they are modified outside a transaction with nontransactionalWrite set to true.

4.5.3.3 Usages for non-transactional instances

From an architectural standpoint, it is not really recommended to use non-transactional instances because of consistency problems. The design guidelines for transaction handling are discussed in Chapter 11. On the other hand, there are special scenarios in which non-transactional data has advantages:

  • An application needs to display statistical data from time to time, and it is irrelevant whether the data is accurate at a specific point in time. Another design goal can be that the displaying part must not disturb the rest of the data processing. Under some circumstances, it might even be necessary to cache the displayed data and reread only changes.

  • A GUI application uses JDO for configuration data, which is loaded at a defined point in time by another part of the system. While editing the configuration data, transactional behavior is not needed. Only the change from one configuration set to another (new) one must be atomic, consistent, isolated, and durable.

4.5.3.4 RetainValues

JDO defines that all persistent instances are cleared when they become hollow to remove unwanted references and clean up memory. With the RetainValues option set to true, persistent-clean, persistent-new, or persistent-dirty objects become persistent-non-transactional instead of hollow at commit. The field data is kept for later use, and it is allowed to read fields of such objects after the transaction terminated. Compared to non-transactional read, it is impossible to navigate to hollow objects and retrieve their data after transaction completion.

4.5.3.5 RestoreValues

Without the RestoreValues option set to true, the above case is valid only if the transaction commits. At rollback, instances still become hollow and they are unusable for the application. By setting the RestoreValues option to true, the JDO runtime must reread the old state of persistent-dirty objects at rollback, or by other means restore the values of modified fields. All persistent-clean and persistent-dirty objects become persistent-non-transactional at rollback, making them readable by the application.

4.5.4 Optimistic transactions

JDO also has the ability to do optimistic transaction management in the same way that a source code version control system allows multiple people to work on code concurrently rather than locking users out of a particular source document. It is interesting to note that optimistic transaction management changes the state transitions of persistent instances. When a transaction starts optimistically, the application believes that locking objects ahead of their access delays operations or locks other users out of the system. Instead, conflicting operations are dealt with when transactions are being committed. JDO accomplishes this technique by allowing parts of the program to modify persistent objects that contain transactional data. When the application finally instructs JDO to commit the persistent object, it checks the datastore to see if the data has been modified since the data was loaded (probably by using a version identification mechanism internal to JDO), and it rolls back, letting you know that there has been a versioning problem. This allows more than a single client to use a persistent object while ensuring that no data is lost. The drawback is that you have to recommit your data (with the new data merged). For this reason, the state management has the option of letting the application refresh data of some hollow or non-transactional instance before it is used inside the transaction.

When the application selects to use optimistic transactions, hollow instances transition to persistent-non-transactional, instead of persistent-clean, when a field is read (Figure 4-5). Upon updates of persistent fields, the instance transitions back to persistent-dirty like in pessimistic transactions. But the application has to explicitly invoke a refresh before any update is made to retrieve fresh values from the datastore. The pros and cons of optimistic transaction are explained in Chapter 11.

Figure 4-5. Lifecycle: Access outside transactions.

Категории