[ Team LiB ] |
14.5 Restoring Values at Transaction RollbackWe 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 trueSimilar to RetainValues, there are several ways to set the RestoreValues property:
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 ImageWith 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 InstancesAt 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:
14.5.3 Restoring Persistent-New InstancesAt 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.setRestoreValues(true); 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
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. |
[ Team LiB ] |