14.3 Persistent-Nontransactional State

The use of instances outside a transaction introduces another instance lifecycle state: persistent-nontransactional. From the application program perspective, this state is indistinguishable from the hollow state. That is, the results of executing the interrogatives in JDOHelper (isNew( ), isDirty( ), etc.) are the same for instances in both states. Your application generally should not be aware of the difference between instances in the hollow and persistent-nontransactional states.

From a performance perspective, your application might run faster, because accessing field values of instances in the persistent-nontransactional state might be done without a datastore access. Your application can retrieve field values cached in the instance and navigate the object graph to other instances, relying only on the cached values. The only time the datastore must be accessed is when a field that has not yet been loaded from the datastore is read.

With datastore transactions, existing persistent instances begin their lifecycle in the cache as persistent-clean or persistent-dirty. With the first access to persistent instances outside a transaction, they begin their lifecycle in the cache in the persistent-nontransactional state. This can be the result of an Extent iteration, a query execution, or navigation from another persistent-nontransactional instance.

With NontransactionalRead set to true, outside a transaction:

  • Your application can read field values, navigate the object graph, execute queries, and iterate extents. The JDO implementation decides whether the instances returned to your application are in the hollow or persistent-nontransactional state. Key fields are instantiated regardless of the instances' states.

  • The first time your application accesses a managed, nonkey field of a hollow instance, the instance transitions to persistent-nontransactional. This state transition is shown in Figure 14-1.

  • Persistent-nontransactional instances remain in this state until they are accessed in a subsequent transaction.

Figure 14-1. State transitions outside a transaction
figs/jdo_1401.gif

With NontransactionalRead set to false, outside a transaction:

  • If your application attempts to read field values, navigate the object graph, execute queries, or iterate extents, the JDO implementation throws a JDOUserException.

  • Persistent instances remain in the hollow state until accessed in a transaction.

We will now discuss a more complete example, based on the Media Mania application. MediaManiaApp declares an abstract method, execute( ), which is implemented by a derived class. In the derived classes, we have seen examples of main( ), which calls executeTransaction( ). This method then begins a transaction, calls execute( ), and commits the transaction.

For this example, we will implement main( ) to call execute( ) instead of executeTransaction( ), which will make the program run without a transaction. The program is PrintMovies in the com.mediamania.nontx package:

package com.mediamania.nontx;
import com.mediamania.MediaManiaApp;
import com.mediamania.content.Movie;
public class PrintMovies {

We don't define a constructor, so the compiler generates a no-arg constructor that calls the superclass to construct the PersistenceManagerFactory. The superclass constructor calls getPropertyOverrides( ), which is implemented in this class to specify the required NontransactionalRead property:

    protected static Map getPropertyOverrides(  ) {
        Map overrides = new HashMap(  );
        overrides.put("javax.jdo.option.NontransactionalRead", "true");
        return overrides;
    }

In this class, main( ) constructs a new instance of PrintMovies and calls execute( ):

    public static void main(String[] args) {
        PrintMovies printMovies = new PrintMovies(  );
        printMovies.execute(  );
    }

The superclass defines the pmf and pm fields and initializes them in the constructor. The execute( ) method gets an Extent of Movie and iterates it, calling Utilities.printMovie( ) to display the contents on System.out:

    public void execute(  ) {
        Extent extent = pm.getExtent(Movie.class, true);
        Iterator iter = extent.iterator(  );
        while (iter.hasNext(  )){
            Movie movie = (Movie) iter.next(  );
            Utilities.printMovie(movie, System.out);
        }
    }
}

As an alternative to using getPropertyOverrides( ), execute( ) could be slightly different, setting the NontransactionalRead property of the Transaction instance to true.

    public void execute(  ) {
        pm.currentTransaction(  ).setNontransactionalRead(true);
        Extent extent = pm.getExtent(Movie.class, true);
        Iterator iter = extent.iterator(  );
        while (iter.hasNext(  )){
            Movie movie = (Movie) iter.next(  );
            Utilities.printMovie(movie, System.out);
        }
    }