13.4 Making a Persistent Instance Transient

Suppose you have a persistent instance that you want to make accessible to a client application via Remote Method Invocation (RMI). Suppose your code is executing in a Common Object Request Broker Architecture (CORBA) or application-server environment, where the transaction context will no longer exist once your servlet or session bean returns from a client invocation. When RMI serializes your instance, the transaction is no longer active. You do not want the PersistenceManager to mediate access to a persistent instance outside of a transaction context. So, to pass the persistent instance to a remote client, you must convert it into a transient instance. This is necessary to disassociate the instance with the PersistenceManager, so field access is not mediated.

You do this by making the persistent instance transient. You can use the following PersistenceManager methods to make persistent instances transient:

void makeTransient(Object obj);
void makeTransientAll(Object[] objs);
void makeTransientAll(Collection objs);

When the instances transition to transient, they lose their identity and association with the PersistenceManager. They are no longer associated with their representation in the datastore, so their in-memory state does not affect the persistent state in the datastore. Even though the instance in memory is transient, the instance still exists in the datastore. Making a persistent instance transient is not equivalent to calling deletePersistent( ). The effect of these methods is immediate and permanent; if a transaction rollback occurs, the instances remain transient. If a parameter is already transient, these methods have no effect.

A persistent-dirty instance has changes to field values that are not committed to the datastore until transaction commit. You do not want to lose these changes, which occurs when an instance is disassociated with its PersistenceManager. Therefore, if you pass a persistent-dirty instance to these methods, a JDOUserException is thrown.

Before calling makeTransient( ), you should call retrieve( ) or retrieveAll( ) to fetch all the field's values from the datastore. Otherwise, some of the fields may not be fetched. The makeTransient( ) methods do not change the values of the fields in the parameter instances.

Another use for makeTransient( ) is to copy an instance from one transaction to another that is running in the same JVM. The following code copies a persistent instance from one PersistenceManager instance (pm1) to another (pm2):

RentalCodeKey key = new RentalCodeKey("High Demand");
RentalCode code = (RentalCode) pm1.getObjectById(key, true);
pm1.retrieve(code);
pm1.makeTransient(code);
pm2.makePersistent(code);

The PersistenceManager referenced by pm2 might be from the same JDO implementation as pm1 but a different datastore. Or, pm1 and pm2 could be from different JDO implementations and datastores.

If you want the instances to remain transient at transaction commit, you must make sure that all references to them from other persistent instances in memory are changed; you should also make the referring persistent instances transient. Otherwise, the persistence-by-reachability algorithm will cause the instances to become persistent again at commit. Since the original persistent instance still exists in the datastore, if the instance becomes persistent again as a result of persistence-by-reachability, there might be two copies of the instance in the datastore. If the class uses datastore identity, the new transient instance is assigned a new identity value. However, if the class uses application identity and you did not change the value of the primary key, you get an exception indicating that you have a duplicate primary-key value.