When you work with an object graph rather than directly with data in a database, you are working with copies of that data. While working with those copies, the integrity of the data within an object graph is crucial. Enterprise Objects uses several mechanisms to ensure the integrity of the data it fetches and manages in its object graphs. These mechanisms are:
Uniquing—Enterprise Objects maintains the mapping of each enterprise object to its corresponding database row and uses this information to ensure that an object graph does not have multiple objects representing the same database row—that each enterprise object is unique within a given object graph.
Snapshotting—When Enterprise Objects fetches data, it records the state of the fetched database row in a snapshot. The information in a snapshot is used to support Enterprise Object’s optimistic locking mechanism. It is also used when changes are committed back to a data source to update only the attributes that were changed since the last fetch.
Faults—The data in the objects at the destination of a fetched object’s relationships doesn’t need to be fetched until it’s actually needed. Until that point, however, a reference to those destination objects may be necessary. These references that don’t contain data are called faults.
These topics are discussed in more detail in the following sections.
Uniquing
Snapshotting
Uniquing and Faulting
Uniquing is the mechanism in which Enterprise Objects ensures that a row in a database is associated with only one enterprise object in a given editing context in an application. The uniquing of enterprise objects limits memory usage and guarantees that the enterprise objects you work with represent the state of their associated database rows as they were last fetched into the object graph.
Without uniquing, a new enterprise object would be created every time you fetch its corresponding row, whether explicitly or through the resolution of relationships. For example, consider the case of a simple relationship between employees and a manager. Bodhi, Brent, and Ernest are represented by employee enterprise objects and Katherine is represented by a manager enterprise object that is the destination of the employee’s manager relationship.
Without uniquing, when the database row representing Bodhi is fetched, an object representing Bodhi’s manager, Katherine, is created to resolve his manager relationship. Then, when the database row representing Ernest is fetched, another object representing Katherine is created to resolve his manager relationship. If the row representing Katherine is itself explicitly fetched, yet another enterprise object representing Katherine is created. In this scenario, Katherine’s row in the database can be altered by multiple enterprise object instances, resulting in objects that represent the same row but that may contain different and conflicting data.
With uniquing, however, in a given editing context, only one object representing Katherine is ever created. All the enterprise objects in a given editing context that refer to Katherine’s enterprise object refer to the same instance—they have a single view of Katherine’s data. So within a given editing context, there is no ambiguity with regard to the data in Katherine’s enterprise object. These two scenarios are illustrated in “Figure 6-4.”
How does uniquing work? Objects are uniqued based on their global ID. A global ID (com.webobjects.eocontrol.EOGlobalID) is formed from an object’s primary key and its associated entity. When a row is fetched to create an object in a particular editing context, its global ID is checked against the objects already in the editing context. If a match is found, the newly fetched object isn’t added to the context.
A single enterprise object instance exists in one and only one EOEditingContext, but multiple copies of an object can exist in different editing contexts. In other words, the scope of object uniquing is a particular editing context.
When an EODatabaseContext fetches objects from a database, a snapshot is recorded of the state of the fetched database row. A snapshot is a dictionary of a row’s primary keys, class properties, foreign keys used in relationships that are class properties, and the attributes of an entity that participate in optimistic locking. To learn how snapshots participate in optimistic locking, see “Inside Optimistic Locking.”
You can imagine that an application that fetches hundreds of rows of data builds up a large cache of snapshots. Theoretically, if enough fetches are performed, an Enterprise Objects application can contain all the contents of a database in memory. Clearly, snapshots must be managed in order to prevent this situation.
So how are snapshots cleaned up? This is the responsibility of a mechanism called snapshot reference counting. This mechanism keeps track of the enterprise objects that are associated with a particular snapshot—enterprise objects that contain data from a particular snapshot. When there are no remaining enterprise object instances associated with a particular snapshot (which Enterprise Objects determines by maintaining a list of these references), that snapshot is released.
Snapshot reference counting is handled automatically by the framework, so you don’t need to think about it.
When a fault is constructed for a to-one relationship, the global ID for that fault is checked to see if the fault or its fully initialized enterprise object counterpart already exists in a given editing context. If so, that object is used to immediately resolve the relationship. This preserves the uniqueness requirement for enterprise objects by ensuring that there’s never more than one global ID representing the same row in the database. Whether that global ID represents an actual enterprise object or a fault doesn’t matter, since the data is fetched when it’s needed.
If Enterprise Objects fetches data for an object that’s already been created as a fault, that fault is fired and the enterprise object finishes initializing.
Last updated: 2007-07-11