13.1 Explicit Management of Instances in the Cache

Normally, a persistent instance is managed in the cache automatically and this management is completely transparent to the application. When you query instances, navigate to instances, or modify instances, the instances are instantiated and their field values are fetched from the datastore. The implementation determines when to fetch a field's value from the datastore, which can occur at any time prior to the application's access of the field.

Instances that are no longer referenced in memory are garbage-collected without requiring your application to perform any explicit action. When you commit a transaction in which persistent instances were created, deleted, or modified, the transaction-completion mechanisms automatically handle the eviction of instances from the cache. So, you usually do not need to evict instances explicitly. By eviction, we mean that the PersistenceManager no longer holds a strong reference to the instances, allowing them to be garbage-collected. The JVM is still responsible for reclaiming the memory held by the instances.

13.1.1 Refreshing Instances

JDO provides a means to refresh instances in the cache with their current values in the datastore. This can be useful outside of a transaction (Chapter 14 covers nontransactional access). It is also useful when you use optimistic transactions (covered in Chapter 15). Refreshing an instance can also be used with datastore transactions. If you use a transaction-isolation level of read-committed, the values in the datastore might change between reads. (If you do not want this behavior, then the JDO implementation should use a repeatable-read isolation level). If you really want to guarantee that you have the current state of the object, you can refresh the instance. However, be aware that right after you refresh the instance, it can be changed in the datastore by another transaction.

You can use the following PersistenceManager methods to refresh the state of instances in memory with their current state in the datastore:

void refresh(Object obj);
void refreshAll(  );
void refreshAll(Object[] objs);
void refreshAll(Collection objs);

These methods perform the following actions on each instance:

  • Load the state of the instance in the datastore into the instance

  • Call the jdoPostLoad( ) method if the class implements InstanceCallbacks and the default fetch group fields have not been loaded yet

  • Transition persistent-dirty instances to persistent-clean in a datastore transaction or persistent-nontransactional in an optimistic transaction (Chapter 14 covers the persistent-nontransactional lifecycle state and Chapter 15 covers optimistic transactions)

Since these methods refresh an instance with its current state in the datastore, any changes you may have made to an instance will be lost. This is different from retrieve( ), which does not overwrite fields that have been modified.

The jdoPostLoad( ) method is only called after the default fetch group has been loaded. So, if the default fetch group had already been loaded prior to invoking refresh( ) or refreshAll( ), jdoPostLoad( ) is not executed again.

13.1.2 Evicting Instances

Your application may run in a memory-constrained environment. Or, it may access a large number of instances and need to access them only once in the transaction. In these situations, it could be useful to evict from the cache instances that you no longer need. Eviction allows the instances to be subsequently garbage-collected, freeing memory resources.

You can call the following PersistenceManager methods to evict instances from the cache:

void evict(Object obj);
void evictAll(  );
void evictAll(Object[] objs);
void evictAll(Collection objs);

If you call evictAll( ) with no parameters, all of the persistent-clean instances in the cache will be evicted. Calling these methods is only a hint to the PersistenceManager that your application no longer needs the instances in the cache. The implementation is not required to evict the instances.

The PersistenceManager performs the following actions for each evicted instance:

  • Calls the jdoPreClear( ) method if the class implements InstanceCallbacks and the instance is not in the hollow state

  • Clears the persistent fields by setting them to their Java default value

  • Sets the instance's lifecycle state to hollow

An implementation may evict a persistent-dirty instance, but it needs to flush the state to the datastore. The PersistenceManager needs to keep only a weak reference to the persistent-dirty instances that have been evicted; it does not need to maintain a reference to any evicted persistent-clean instances. Once instances have been evicted, they can be garbage-collected.

The values of evicted instances are not retained after transaction completion, regardless of the setting of the RetainValues and RestoreValues flags. If you want to evict all the transactional instances at transaction commits, set the RetainValues flag to false (Chapter 14 covers the RetainValues flag). If you want them to be evicted on rollback, set the RestoreValues flag to false (Chapter 7 covers the RestoreValues flag). In these cases, you do not need to call the evict( ) and evictAll( ) methods.