14.1 Nontransactional Features

As you have seen earlier, the JDO runtime contains an instance cache managed by the PersistenceManager, and in the transaction modes we have presented thus far, instances in the cache have always been transactional. We now introduce the behavior of the cache and the instances contained in the cache in light of nontransactional behavior. There are five independent flags that govern this behavior.

NontransactionalRead

This flag enables your application to iterate extents, perform queries, access persistent values of persistent instances, and navigate the entire graph of persistent instances, without having a transaction active.

NontransactionalWrite

This flag enables your application to make changes to the cache that will never be committed to the datastore. Most applications expect that changes made to persistent instances will be stored in the datastore at some point. NontransactionalWrite caters to applications that manage a cache of persistent instances where the changes to the datastore are made by a different application.

Optimistic

This flag enables your application to execute transactions that improve the concurrency of datastore access, by deferring locking of data until commit. We discuss optimistic transactions in detail in Chapter 15; we introduce it here because instances used in an optimistic transaction are read nontransactionally, so they share common characteristics of data that is read with NontransactionalRead.

RetainValues

This flag enables your application to retain the field values of instances in the cache at the end of committed transactions, to improve performance. Subsequent nontransactional accesses to cached values do not need to access the datastore.

RestoreValues

This flag enables your application to retain the field values of instances in the cache at the end of rolled-back transactions, to improve performance. Subsequent nontransactional accesses to cached values do not need to access the datastore.

The JDO implementation governs the availability of these features. Except for RestoreValues, the features are optional, and an implementation might support any or all of them, although if an implementation supports any of Optimistic, RetainValues, or NontransactionalWrite, it will logically support NontransactionalRead as well.

Attempts to use an unsupported feature result in the JDO implementation throwing an exception. For example, if an implementation does not support NontransactionalRead, attempting to set the NontransactionalRead option to true throws a JDOUnsupportedOptionException.

The runtime behavior of the PersistenceManager depends on the current settings of these flags, which are accessed via the Transaction instance associated with the PersistenceManager. You can read the current settings by using the property access method for the flag of interest. This example shows an application-specific method that returns the current setting for a given PersistenceManager instance:

boolean retrieveNontransactionalReadSetting(PersistenceManager pm) {
    Transaction tx = pm.currentTransaction(  );
    return tx.getNontransactionalRead(  );
}

You can set the property values using the property access methods. Once set, they remain unchanged until they are set to a different value. This example shows an application-specific method that changes the NontransactionalRead setting for the given PersistenceManager:

void setNontransactionalReadSetting(PersistenceManager pm, boolean value) {
    Transaction tx = pm.currentTransaction(  );
    tx.setNontransactionalRead(value);
}

The settings for the flags are initialized from the PersistenceManagerFactory that created the PersistenceManager. You can read the default settings from the PersistenceManagerFactory. This example shows an application-specific method that returns the default setting for a given PersistenceManagerFactory instance:

boolean retrieveNontransactionalReadSetting(PersistenceManagerFactory pmf) {
    return pmf.getNontransactionalRead(  );
}

The default values for these PersistenceManagerFactory flags are JDO implementation-specific. You can configure the PersistenceManagerFactory to have specific default values by using the property access methods with an existing PersistenceManagerFactory, or by including the appropriate values in the Properties instance used to configure the PersistenceManagerFactory.

For example, to guarantee that the PersistenceManagerFactory used by your application has the NontransactionalRead property set to true, you can use one of the following techniques:

PersistenceManagerFactory createPMF(  ) {
    PersistenceManagerFactory pmf;
    pmf = new com.sun.jdori.fostore.FOStorePMF(  );
    // set other required properties
    // the following might throw JDOUnsupportedOptionException
    pmf.setNontransactionalRead(true);
    return pmf;
}

Note that this code refers to a JDO implementation-specific class that is not part of the JDO specification. The advantage of the following technique is that you can compile this code without reference to any JDO implementation-specific class:

PersistenceManagerFactory createPMF(Properties props) {
    // other required properties are already in the props instance
    PersistenceManagerFactory pmf;
    props.put("javax.jdo.option.NontransactionalRead", "true");
    // the following might throw an Exception
    pmf = JDOHelper.getPersistenceManagerFactory(props);
    return pmf;
}

If your application depends on any of the optional features, you should make sure that the JDO implementation that you are using supports them, either by constructing the PersistenceManagerFactory with the property set to true, or by dynamically querying the optional features of the PersistenceManagerFactory during initialization using supportedOptions( ). This will avoid exceptions in your application logic that might be awkward to handle.

You might execute your application in an environment where a different component constructs the PersistenceManagerFactory and you must use it. For example, the PersistenceManagerFactory might be constructed and registered as a named entry in a Java Naming and Directory Interface (JNDI) context. Your application looks up the entry and verifies that it supports the required feature.

The required feature can be verified by a simple contains( ) check:

PersistenceManagerFactory pmf;
pmf = (PersistenceManagerFactory)ctx.lookup("MoviePMF");
Collection supportedOptions = pmf.supportedOptions(  );
if (!supportedOptions.contains("javax.jdo.option.NontransactionalRead")) {
    throw new ApplicationCannotExecuteException
        ("NontransactionalRead is not supported");
}