Each persistence manager maintains a cache of persistent application data objects. Each persistent object may or may not hold, that is to say cache, some or all of its persistent state. In addition, the state manager, which is a JDO implementation object associated with the persistence manager, may hold cached state for some persistent fields of the application data object. From the application's point of view, both the cached persistent field values in the application data object and any cached persistent field values in the state manager are one cache. The issue, then, of controlling the persistence manager's cache is really two issues: controlling the cache of persistent objects and controlling the cache of persistent state for each persistent object.
The persistence manager has three operations to control the cache of persistent state. The retrieve operation loads the persistent state. The evict operation discards the persistent state, and the refresh operation ensures that the cached persistent state is up-to-date with the current state in the datastore. These three operations control the caching of persistent state for any application data objects that the application selects. In addition, as described in Chapter 4, the transactional properties RetainValues and RestoreValues control the automatic eviction of persistent state that can occur at the end of JDO transactions.
Although JDO provides direct control over the cache of persistent state, the cache of persistent objects is managed indirectly as a side-effect of application and JDO design. Persistent objects are inserted into the cache when JDO constructs them to hold persistent state, or when the application makes transient objects persistent. The uniqueness requirement limits the number of copies of persistent application data objects to no more than one per persistence manager per unique persistent state in the datastore. Normal JVM garbage collection determines when the persistent application objects are removed from the cache. JDO does not hold strong references to application data objects that are hollow or persistent-nontransactional. As long as the application does not hold strong references to them, they may be garbage collected.
The PersistenceManager interface provides five methods to load persistent values into any persistent fields that have not been loaded already.
public void retrieve(Object adObject) public void retrieveAll(Collection adObjects) public void retrieveAll(Collection adObjects, boolean DFGOnly) public void retrieveAll(Object adObjects) public void retrieveAll(Object adObjects, boolean DFGOnly)
There are two reasons to call these methods. First, applications will usually want to call one of these methods before making an object transient. By doing so, the application ensures that all of the object's persistent fields are loaded with persistent values before the object becomes unmanaged.
Implementations may provide a second reason for using these methods. They may use the retrieve methods to provide the best performance in loading persistent state. To this end, the JDO implementation may retrieve related data objects according to a policy that is implementation dependent.
JDO tracks which persistent fields of a persistent object have been loaded. If the field is not loaded, then the value in the field is the Java default value for the field's type. If the field is loaded, the value may be any legal value for the field's type. In some cases, even though the field is not loaded, JDO has obtained and stored someplace else in memory the information that it needs in order to load the field. This happens most commonly for reference types, when JDO may store else-where in memory the identity value that it needs to find the persistent state of the object referred to.
When the DFGOnly flag is true, JDO may load only the fields in the default fetch group. Setting DFGOnly to true is a hint to the implementation that it may disregard. On the other hand, when DFGOnly is false, which is the implied setting when this parameter is not in the signature, then JDO must load all of the persistent fields. When a persistent field that contains a reference is loaded, JDO must find or construct the object referred to. This second object may be hollow, but it must be present in memory. Since reference fields are usually not in the default fetch group, using the hint may in some cases increase performance.
The default fetch group is a possibly empty group of persistent fields that the JDO implementation prefers to load together into the persistent object for performance reasons. Every application data class has a default fetch group, and its membership can be configured in the JDO metadata. Chapter 5 covers the JDO metadata.
For the retrieve methods to have effect, either a transaction must be active or the transaction's NontransactionalRead property, described in Chapter 4, must be true. Although the specification is not clear, if the transaction is not active and the NontransactionalRead property is false, then calling the retrieve method will likely yield a JDOUserException.
Application data objects in any of the following five states do not refer to preexisting state in the datastore:
As a result, the retrieve methods cannot load persistent state for them. The retrieve methods perform no operation on objects in these states.
Passing a persistent-deleted object to the retrieve methods does not change its management state or throw an exception. In this case, it is reasonable to expect that no persistent fields are loaded, but the specification is not clear on this point.
Application data objects in any of the following four states refer to preexisting state in the datastore:
For objects in these management states, the retrieve methods load the fields (either all or those in the default fetch group as the case may be) that are not already loaded.
The retrieve operation does not imply the refresh operation. Retrieving a persistent-dirty object leaves it in the persistent-dirty state, and its modified persistent fields are not altered. Likewise, passing a persistent-clean object to the retrieve methods does not modify its management state, but any fields that were not loaded prior to the call are loaded by the call.
The retrieve methods change objects in the hollow state either to persistent-clean in the case of an active datastore transaction or to persistent-nontransactional otherwise. The retrieve methods change objects in the persistent-nontransactional state to persistent-clean in the case of an active datastore transaction. The object's cached persistent state is discarded and new state is loaded from the datastore. Outside of an active transaction or in the case of an active optimistic transaction, the retrieve methods leave persistent-nontransactional objects in the persistent-nontransactional state. In these cases, no cached state is discarded.
The PersistenceManager interface provides four methods to evict the persistent state of persistent objects.
public void evict(Object adObject) public void evictAll() public void evictAll(Collection adObjects) public void evictAll(Object adObjects)
When successful, the evict methods immediately discard the persistent state of the object. As a result, the persistent fields acquire their Java default values, and the object's management state changes to hollow. If the object uses application identity, the values of its persistent key fields are not discarded. The evict operation operates only on the persistent state, but you may find implementations that incorrectly evict transactional state along with the persistent state.
As a general rule, the application does not need to explicitly evict application data objects. JDO automatically evicts the persistent state of application data objects under commonly encountered conditions. As Chapter 4 explains, the transaction's RetainValues property controls automatic eviction. Excessive use of the evict methods may indicate that the RetainValues property should be set to false.
The application calls the evict methods to accomplish one of two purposes. Occasionally, the application may know that a persistent-nontransactional object contains persistent state that no longer reflects the state in the datastore. By calling the evict method, the application can discard the persistent state of the object when a transaction is not active. As a result of eviction, the object will be effectively refreshed when the application next uses the object.
The second reason to call the evict methods arises when the application anticipates or knows that it is iterating a large extent of persistent objects or a large results collection from a query. In this case, eviction is a way to ensure that the persistent-clean objects can be garbage collected while the transaction is active. In fact, the evictAll() method is custom made for the job as it evicts all persistent-clean objects and nothing else.
Objects in the JDO-transient state do not have persistent state to discard. Passing a JDO-transient object to the evict(Object adObject) method causes a JDOUserException to be thrown. In the case of the evictAll methods, contrary to the general rule for xxxAll methods, any JDO-transient objects contained in the passed Collection or array are silently ignored.
Objects in any of the following four management states also do not contain persistent state to discard:
The evict methods perform no operation on objects in these states.
Evicting objects in any of the following three states is also a no-op for the object:
The evict methods, when passed a persistent-nontransactional or persistent-clean object, discard the object's persistent state and change its management state to hollow. In the case of a datastore transaction, JDO may not release until the transaction commits the locks that it holds in the datastore on the persistent state of any persistent-clean objects that it evicts.
The PersistenceManager interface provides four methods to refresh the persistent state of persistent objects.
public void refresh(Object adObject) public void refreshAll() public void refreshAll(Collection adObjects) public void refreshAll(Object adObjects)
The refresh methods ensure that the cached persistent state of the passed objects contains the current values available from the datastore. The persistent values may be simply reloaded, or the JDO implementation may rely on a concurrency value or database locks to determine whether the persistent state needs to be reloaded. The values of transactional fields (those that are managed but not persistent) are not affected by refresh.
Values cannot be loaded from the datastore unless the transaction is active or the NontransactionalRead property is true. If these conditions are not met, calling any refresh method is a no-op.
Calling the refresh operation for application data objects that do not refer to preexisting state in the datastore is a no-op. For that reason, passing application data objects in any of the following five management states to the refresh operation is a no-op:
Refreshing a persistent-deleted object is also a no-op. This behavior exists because the decision to delete the object was made on the basis of its cached persistent state.
Refreshing an object in the hollow state is usually a no-op because the object does not have any cached persistent state that could be out of date. On the other hand, the JDO implementation has the option to load some or all of the persistent fields and change the management state to either persistent-clean in the case of a datastore transaction or persistent-nontransactional in the case of optimistic transactions.
Refreshing an object in the persistent-clean or persistent-nontransactional state verifies that its persistent state is up-to-date with the datastore. For objects in the persistent-clean state in a datastore transaction, JDO may rely on the database locks that it acquired to know that the object's state is up-to-date. In an optimistic transaction, JDO may use the concurrency value to verify that the object's state is up-to-date. Either way, if the object's persistent state is out-of-date, the refresh methods discard it and reload new values from the datastore.
Refreshing an object in the persistent-dirty state discards the modifications made to the object within the transaction. The methods reload current values from the datastore. In the case of a datastore transaction, the application data object becomes persistent-clean, but in the case of an optimistic transaction, the application data object becomes persistent-nontransactional. In both cases, any state saved for rollback is also discarded.
The refreshAll() method refreshes all persistent-clean and persistent-dirty objects.