14.5 Restoring Values at Transaction Rollback

We have seen how an application can retain persistent field values in cached instances across transactions by using the RetainValues property. But this property is effective only at commit. If you want to preserve cached values even if a transaction rolls back, you need to use the RestoreValues property. Unlike RetainValues, RestoreValues is not an optional feature, and the property setting affects the treatment of new instances as well as persistent-clean and persistent-dirty instances.

With RestoreValues set to false, persistent transactional instances have their values cleared at transaction rollback, and the instances transition to hollow. This is shown in Figure 14-3. Subsequent reads of fields in these instances require access to the datastore. In order to allow accesses of the values in the instances without accessing the datastore, the application sets the RestoreValues flag to true.

Figure 14-3. Rollback with RestoreValues true

Similar to RetainValues, there are several ways to set the RestoreValues property:

  • Your application can include the javax.jdo.option.RestoreValues property with a value of true or false in the Properties instance used to construct the PersistenceManagerFactory.

  • Your application can set the property using setRestoreValues( ) in PersistenceManagerFactory.

  • Your application can set the property using setRestoreValues( ) in Transaction.

Since this flag affects the way persistent fields are managed during a transaction, the property must be changed only between transactions. If an attempt is made to execute setRestoreValues( ) during an active transaction, a JDOUserException is thrown.

14.5.1 Before Image

With RestoreValues set to true, the JDO implementation must make a before image of instances that are made persistent and persistent instances that are changed or deleted during the transaction. The before images contain the state of persistent and transactional fields as of the first access of the fields in the transaction, and they supply the field values restored during rollback. The before image contains a shallow copy of all the fields in the instance as of the call to makePersistent( ), deletePersistent( ) , or a method that changes a managed field.

A shallow copy means that the field values are copied exactly as they are stored in the instance; values of primitive fields are copied, and references are copied. There is no copy made of the contents of reference types.

Making a before image can adversely affect performance, as there is extra work for the JDO implementation to do when the instance is made persistent, deleted, or made dirty. Therefore, applications should carefully consider the use of this flag.

With RestoreValues set to false, the JDO implementation does not need to remember the state of fields of transient instances that are made persistent. If the transaction is rolled back, the instances revert to transient, and the state of the fields is unchanged. Normally, your application will discard these instances and allow them to be garbage-collected. Similarly, there is no requirement to remember the state of instances that are changed or deleted. At transaction rollback, the instances transition to hollow, and the field contents are cleared.

14.5.2 Restoring Persistent Instances

At rollback, with RestoreValues set to true, persistent-clean, persistent-dirty, and persistent-deleted instances transition to persistent-nontransactional. Persistent-clean instances retain their values as of the end of the transaction. Persistent-dirty and persistent-deleted instances are restored as follows:

  • Fields of primitive types (int, float, etc.), wrapper types (Integer, Float, etc.), immutable types (Locale, etc.), and PersistenceCapable types are restored to their values as of the beginning of the transaction.

  • Fields of mutable types (Date, Collection, etc.) are marked by the JDO implementation as not loaded. Subsequent accesses of these fields will cause the JDO implementation to read the values from the datastore.

14.5.3 Restoring Persistent-New Instances

At rollback, with RestoreValues set to true, persistent-new and persistent-new-deleted instances transition to transient and all fields are restored to their values in the before image.

The before image allows the JDO implementation to restore the instance to the state it had at the time the instance was made persistent. But consider that the state of reference type fields is also part of the state of the instance and cannot necessarily be restored to its state as of the time the referring instance was made persistent.

For example, consider the following code, which makes an instance of Movie persistent and rolls back the transaction:

Calendar calendar = Calendar.newInstance(  );
calendar.set(Calendar.YEAR, 1965);
Date released = calendar.getTime(  );
Movie movie = new Movie("Sound of Music", released, 174, "G", "musical, biography");
tx.begin(  );
pm.makePersistent(movie);     [1]
calendar.set(Calendar.YEAR, 1987);
released.setTime(calendar.getTimeInMillis(  )); // AVOID     [2]
calendar.set(Calendar.YEAR, 1999);
released = calendar.getTime(  );     [3]
tx.rollback(  ); // movie.released now is 1987; released is 1999
  • [1] During makePersistent( ), a shallow copy of movie is made and the copy becomes the before image. The releaseDate field in the persistent movie instance is replaced with a new instance of a JDO implementation-defined subclass of Date, containing the same millisecond value of the original released instance. There are now two instances of Date; both represent the year 1965.

    Any change to the Date instance referred to by released after makePersistent( ) does not affect the persistent instance, but it changes the instance in the before image.

  • [2] In the preceding example, the instance referred to by the before image is changed to represent the year 1987. Similarly, any change to the value of the field in the persistent instance does not affect the value of released or the before image.

  • [3] When a new Date is created and assigned to released, there is now a third instance of Date, which contains a value representing the year 1999.

At rollback, the value of the field releaseDate in instance movie is restored to its original value of released, but because the released object was modified to represent the year 1987, these modifications remain. Thus, even though the fields in the movie instance itself were restored, the releaseDate field contains changes made subsequent to makePersistent( ).

After rollback, the original instance of released becomes the restored value of releaseDate in the movie instance; the JDO implementation-defined subclass of Date, representing 1965, is not referenced and can be garbage-collected; and the third instance, representing 1999, is now the value of the released variable.

To avoid this situation, you should never modify instances referred by fields of persistent instances once they are made persistent; instead you should replace the fields or use accessor/mutator methods defined in the persistent class. Replacing the fields leaves the instance in the before image as it was, and using mutator methods in the persistent class modifies the copy of the original instance.