Arguably the second most important function of DataContext (first is performing queries) is keeping track of changes made to the registered DataObjects. "Registered" is a keyword here - registering an object with DataContext is what gives this object its persistent qualities.
Behind the scenes "registering an object" results in storing this object in a map using its ObjectId as a key, setting "dataContext" property of a DataObject to the current DataContext, and taking a snapshot of all persistent properties to be able to track later modifications. Objects can become "registered" in two ways:
- automatically when they are fetched via query API
- explicitly for the newly created objects
Whenever a selecting query is executed by a DataContext, all fetched objects are automatically registered with this DataContext. On the other hand, newly created objects must be registered explicitly:
This code can be simplified - object creation and registrations steps can be combined in one method call:
This method relies on the presence of a no-argument constructor in the DataObject class.
State transitions of DataObjects from persistence point of view are discussed in the "Design" chapter. State of each individual object is described by an integer constant obtained via a call to DataObject.getPeristenceState(). Allowed states are defined as static variables in PersistenceState class.
When a new object is inserted to the DataContext as described above, it becomes "NEW":
When a DataContext is committed, such object becomes "COMMITTED":
When any of the attributes or relationships of the fetched or committed object are changed, such an object becomes MODIFIED:
When a fetched or committed object is explicitly deleted from the DataContext, object becomes DELETED:
DataContext is said to have changes if it has one or more registered objects in a state PersistenceState.MODIFIED, PersistenceState.NEW or PersistenceState.DELETED. DataContext provides the following method to check if it has any changed objects:
- public boolean hasChanges()
There is also a way to obtain a list of changed objects in each one of the above states:
- public java.util.Collection newObjects()
- public java.util.Collection deletedObjects()
- public java.util.Collection modifiedObjects()
All of the uncommitted objects ("uncommitted" means "new", "modified" or "deleted") are saved ("committed") to the database with a single method call on the DataContext:
- public void commitChanges()
Method commitChanges takes care of building correct SQL statements, generating primary keys and transactional behaviour. It roughly follows this scenario:
- Checks if there are any changed objects (also detecting "phantom" modifications, e.g. if an object property was "updated" with the equivalent value).
- Validates "dirty" objects (for more information on validation see this page).
- Generates primary keys for any NEW objects that require autogenerated key.
- Builds any needed INSERT, UPDATE, DELETE queries.
- Starts the database transaction.
- Runs the queries.
- Commits transaction.
- Changes all committed objects state to PersistenceState.COMMITTED.
- Updates internally stored snapshots of the recently saved objects.
There is a way for the DataContext to undo all uncommitted changes:
- public void rollbackChanges()
This will restore the persistence state and the values of all registered objects to the values that objects had when they were fetched or the last commitChanges was executed. This effectively restores previously committed state of the object graph. Note that any NEW objects are unregistered from the context.