JDO has a total of 10 lifecycle states. The following 7 states are required:
There are also three optional states:
If a JDO implementation does not support the transaction-related optional features that allow transient transactional and persistent-nontransactional instances, these three optional states are not reachable. This chapter focuses on the required states. Chapter 13 and Chapter 14 discuss these optional features and associated lifecycle states.
When you call a constructor to create an instance of a class, the instance is placed in the transient state. Each instance created by the application starts its life as a transient instance. Transient instances do not have a JDO identity, because identity is only a characteristic of persistent instances. A transient instance should behave exactly as an instance of the class would if the class were not persistent. No JDO exceptions are thrown for a transient instance.
Many developers wonder how much overhead is involved when transient instances of an enhanced class are manipulated. Fields of transient instances have slightly slower access and modification than they would if the class were not persistent and enhanced. No mediation of access or modification of fields is performed on instances in the transient state. In particular, a transient instance never makes a call to a method of the JDO implementation, specifically those defined in the StateManager interface. To understand the exact overhead involved, read the sidebar Overhead of Accessing a Field of a Transient Instance.
Overhead of Accessing a Field of a Transient Instance
The enhancer replaces the getfield and putfield instructions that access a field at the byte-code level with a call to a generated static method. The code generated for these static methods has different logic, depending on whether the specific field is in the default fetch group. Chapter 12 discusses field fetch groups and the default fetch group.
For a field in the default fetch group, the first line of the generated static method checks the jdoFlags field (generated by the enhancer) for equality with the PersistenceCapable constant READ_WRITE_OK. If they are equal, the field is accessed and the method returns. A transient instance has its jdoFlags field set to READ_WRITE_OK, so this one equality comparison with jdoFlags is the only additional software executed for fields in the default fetch group.
For a field that is not in the default fetch group, the first line of the generated static method checks to see whether the jdoStateManager field is null; if so, the field access or modification is performed and the method returns. Transient instances have their jdoStateManager field set to null, so this one equality comparison with the jdoStateManager field is the only additional software executed for a field that is not in the default fetch group.
JDO does not support the demarcation of transaction boundaries for instances in the transient lifecycle state. Indeed, transient instances have no transactional behavior, unless they are referenced by persistent instances at commit time. In that case, they transition to the persistent-new state. Transient-transactional instances are instances that are transient and have transactional behavior. Chapter 13 covers transient-transactional instances.
Instances that have been made persistent in the current transaction are placed in the persistent-new state. This occurs if the application makes an instance persistent explicitly by passing it as a parameter to makePersistent( ), or implicitly through persistence-by-reachability. Thus, instances that become provisionally persistent via the reachability algorithm also transition to the persistent-new state. Only transient instances (which include transient, transient-clean, and transient-dirty instances) can transition to the persistent-new state, and this only occurs as a result of making them persistent.
During the transition from transient to persistent-new, the following actions are performed:
The associated PersistenceManager becomes responsible for implementing state interrogation and all further state transitions. This is implemented by setting the jdoStateManager field in the instance to reference the associated StateManager.
If the RestoreValues flag is true, the values of persistent and transactional nonpersistent fields are saved in a before image to be used during transaction rollback.
The implementation assigns an identity to the instance. This identity uniquely identifies the instance inside the PersistenceManager and might uniquely identify the instance in the datastore. The instance must have a unique identity at transaction commit for classes with a durable identity.
The JDO implementation instantiates every object accessed from the datastore in memory. The implementation constructs a hollow instance by calling the no-arg constructor. An instance in memory is in the hollow state if it represents a specific object in the datastore whose values have not yet been loaded from the datastore into the instance. Instances transition to the hollow state at transaction commit when RetainValues is false.
An instance can be in the hollow state if it is:
Committed from a previous transaction
Acquired by getObjectById( )
Returned by iterating an Extent
Returned in the result of a query
Accessed by navigating a persistent field reference
However, with these operations an implementation may choose to return the instances in a different state that is reachable from hollow. An implementation can transition an instance from the hollow state to another state at any time, just as if a field were read. Therefore, the hollow state might not be visible to the application.
Primary-key fields are always available in an instance, regardless of its state. So, the primary-key fields of a hollow instance are initialized. Read access of primary-key fields is never mediated. The JDO implementation is not required to load values into any other field until the application attempts to read or modify the field.
Once the JDO implementation has initialized a reference or collection of references to persistent instances in the cache, these references need to refer to actual Java instances in memory. So, the JDO implementation needs to instantiate instances to refer to; it instantiates instances and places them in the hollow state. It is important for you to know that these hollow instances exist and that they consume memory resources in the JVM. If your application never accesses them, their state may never be initialized from the datastore.
A hollow instance maintains its identity and association with its PersistenceManager instance. A PersistenceManager must not hold a strong (nonweak) reference to a hollow instance. Thus, if your application does not hold a strong reference to a hollow instance, it might be garbage-collected during or between transactions.
Furthermore, instances transition to hollow at transaction commit. If your application still has a strong reference to a hollow instance after transaction commit, the JVM garbage collector will not free up its associated memory resources. If the instances your application refers to have their own references that refer to additional instances in the cache, those instances cannot be freed either. So, it is very important that your application does not refer to such instances after transaction commit, unless you intend to continue using them after commit, between transactions, or in a subsequent transaction. Chapter 14 covers the access and use of persistent instances after commit.
An instance in the persistent-clean lifecycle state represents a specific instance in the datastore whose values have not been changed in the current transaction. If any persistent field other than a primary-key field of a hollow instance is read, the instance transitions to persistent-clean. The field values of a persistent-clean instance in memory are identical to their values in the datastore.
When a field is modified, an instance may become inconsistent with the state it had in the datastore at the beginning of the transaction. This includes instances that have been modified or deleted. These instances are referred to as dirty.
If the value of a managed field is modified, the instance is marked as dirty and placed in the persistent-dirty state. If your application does not modify any managed field of an instance, the instance is not marked as dirty. In one special circumstance, the application modifies a managed field, but the new value is equal to the old value. If the field is of an array type, the implementation marks the field as modified and makes the instance dirty. Otherwise, the implementation decides whether to consider the instance dirty.
During the commit of a transaction in which a dirty instance's values have changed (including a new persistent instance), the underlying datastore is changed to have the transactionally consistent values from the instance and the instance transitions to hollow.
A JDO implementation might store the state of persistent instances in the datastore at any time; this process is called flushing. This does not affect the dirty state of the instances. This flushing behavior is not visible to the application and does not impact the rollback of a transaction.
A persistent instance that has been deleted in the current transaction by a call to deletePersistent( ) is in the persistent-deleted state. You can read the primary-key fields of a deleted instance, because the primary-key fields always have their values populated. But accessing any other persistent field throws a JDOUserException.
An instance that has been made newly persistent and also deleted in the current transaction is placed in the persistent-new-deleted state. You can read its primary-key fields, but any other persistent field access throws a JDOUserException.