Retired Document
Important: This document may not represent best practices for current development. Links to downloads and other resources may no longer be valid.
Saving Data
This chapter discusses the mechanics of saving data within the Enterprise Objects frameworks.
It is divided into the following sections:
Objects Involved in Saving introduces the objects involved in saving data in an Enterprise Objects application.
Flow of Data During a Save discusses the flow of data during a save.
Phases of Saving discusses the mechanisms Enterprise Objects uses to ensure data integrity while saving.
Key Generation discusses how key generation works in Enterprise Objects.
Common Delegate Usage discusses common delegates used in the save process.
Generating Custom Primary Keys teaches you how to generate custom primary keys.
Using Compound Primary Keys discusses what you need to do to use compound primary keys.
Objects Involved in Saving
There are many objects involved in saving data in an Enterprise Objects application. The objects you’ll most commonly work with are introduced here.
- EOEditingContext
In Enterprise Objects, saves to the database most often originate with a
saveChanges
invocation on an editing context.- EOObjectStoreCoordinator
This object is responsible for figuring out which data source corresponds to the changes in a particular editing context. A given editing context can contain enterprise objects that are constituted from various data sources, so the object store coordinator manages the logistics of identifying these data sources when saving.
- EOCooperatingObjectStore
Each cooperating object store in an application is responsible for transmitting to its data source the changes handed to it by the object store coordinator. A cooperating object store (which is most often an instance of EODatabaseContext) is an abstract representation of a data source.
Other objects are involved in saving data, such as EODatabaseContext and EOAdaptorChannel, but you rarely need to interact with these objects programmatically.
Phases of Saving
The following phases are involved in pushing data back to a database:
Validation. The first phase of committing data to a database is validating the data. A large part of your application’s business logic involves validation—verifying that customers don’t exceed their credit limits, that test scores fall within an allowable range, that phone numbers contain only numbers, and so on. In your enterprise object classes, you implement methods that perform validation at different points in an application, such as when users attempt to save, delete, update, or perform other operations on a record.
By validating data in your enterprise object classes, you have an early opportunity to catch invalid data before it is committed to the database. Validation in enterprise object classes, however, is only a piece of the data integrity puzzle.
Referential integrity enforcement. Another part of the data integrity puzzle is relationship-level referential integrity rules that specify the parameters of relationships between entities. Like validation rules, referential integrity rules often reflect your custom business logic. In the context of the Enterprise Objects stack, referential integrity enforcement occurs at a lower level in the access layer. You define referential integrity rules in an application’s data model.
Enterprise Objects supports the following referential integrity rules: ownership, optionality, delete rule, and propagate primary key.
Key generation. When you use Enterprise Objects, you usually don’t have to worry about database artifacts such as primary and foreign key values. Primary and foreign keys are rarely meaningful parts of a business model; rather they’re used within a database to express relationships between tables. For example, the primary key of a LISTING table (LISTING_ID) doesn’t have any meaning to users as they identify property listings by address.
Most database application-development environments force you to worry about primary and foreign key generation. Part of the Enterprise Objects philosophy is that this is a task that the technology should take care of for you—your time as a developer is better spent focusing on business logic and building great applications, not on managing database key generation.
Enterprise Objects manages primary and foreign key data for you. Your enterprise object classes don’t have knowledge of primary and foreign keys and you don’t have to worry about generating or propagating primary key values when adding records to a database.
Flow of Data During a Save
A save operation begins with the invocation of saveChanges
on an editing context. If you programmatically create and manage editing contexts, you explicitly invoke this method. However, saveChanges
is commonly invoked automatically by elements of an Enterprise Objects application such as display groups.
Figure 8-1 provides a high-level overview of the save process.
Once a save is initiated, the following sequence occurs to retrieve data from a data source:
When
saveChanges
is invoked on an editing context, that editing context invokes the methodeditingContextWillSaveChanges
on its editors and delegates.The editing context processes, propagates, and validates changes for deleting.
The editing context processes and validates changes for saving.
The editing context commits the changes made to its objects to its parent object store by invoking the method
saveChangesInEditingContext
on its parent. For nonnested editing contexts, the parent is typically an instance of EOObjectStoreCoordinator. When an object store coordinator receives this invocation, it guides its cooperating object stores through a multipass save protocol in which each cooperating store saves its own changes and forwards remaining changes to other cooperating stores.After it receives the invocation of
saveChangesInEditingContext
, the object store coordinator invokes the methodprepareForSaveWithCoordinator
on each of its cooperating object stores. This informs each object store that a multipass save operation is beginning. When the object store is an EODatabaseContext (which is the most common case), it initiates the process of generating primary keys for any new objects in the editing context on whichsaveChanges
was invoked.The object store coordinator then invokes the method
recordChangesInEditingContext
on each of its cooperating object stores. This prompts each of those stores to examine the changed objects in the editing context, record any operations that need to be performed, and notify the coordinator of any changes that need to be forwarded to other cooperating stores.For example, if in its
recordChangesInEditingContext
method, a cooperating object store notices the removal of an object from an owning relationship but that object belongs to another cooperating store, it informs the other store by invokingforwardUpdateForObject
on the object store coordinator.The object store coordinator invokes the method
performChanges
on each of its cooperating object stores. This tells the stores to transmit their changes to their underlying databases. When the cooperating store is an EODatabaseContext, it responds to this invocation by constructing a list of adaptor operations based on the list of database operations that were determined in the last step. It then forwards this list of adaptor operations on to an available EOAdaptorChannel to be executed.If
performChanges
fails for any of the cooperating object stores, the methodrollbackChanges
is invoked on all the cooperating object stores in that access-layer stack.If
performChanges
succeeds for all of the cooperating object stores, the methodcommitChanges
is invoked on all of them, which tells the adaptor to commit its changes.If
commitChanges
fails for a particular cooperating store, the methodrollbackChanges
is invoked on that store and on all subsequent stores. However, the stores that have already committed their changes do not roll back. In other words, the object store coordinator doesn’t perform the two-phase commit necessary to guarantee consistent distributed updates.If the save operation is successful, the database contexts of the editing context from which the save operation originated update their snapshots.
Finally, if the save operation is successful, the editing context posts the notification EditingContextDidSaveChangesNotification.
Key Generation
One of the best features of Enterprise Objects is its ability to manage primary and foreign keys for you. It does this in part by providing a mechanism to automatically generate primary keys without requiring you to write any code. To take advantage of Enterprise Object’s automatic primary-key generation, an entity’s primary key must satisfy these guidelines:
It must be a simple integer; the adaptor cannot generate keys for noninteger data types.
It cannot be compound.
Automatic primary-key generation in Enterprise Objects works differently depending on the database, but you usually don’t need to be concerned with the details. With some databases such as OpenBase and MySQL, Enterprise Objects maintains a table in the database (EO_PK_TABLE
) to help it maintain a linear sequence of nonrepeating primary keys. With an Oracle database, Enterprise Objects doesn’t need this table and instead uses Oracle sequences to generate primary keys.
The advantage of using Enterprise Object’s automatic primary-key generation is that it is easy to use and makes your life as a developer much simpler. However, there are some disadvantages and limitations, including these:
It is database-specific.
It requires a roundtrip to the database; however, key generation is batched, so even when inserting hundreds of objects at once, key generation is inexpensive.
It cannot generate compound primary keys.
It doesn’t work if the database includes rows that are inserted by non-Enterprise Objects applications.
Fortunately, Enterprise Objects provides a number of hooks so you can provide primary key values. If an enterprise object or its snapshot already has a value for its primary key (which is the case for enterprise objects formed from preexisting data or for enterprise objects for which you set the primary key value explicitly in the enterprise object class), none of the key generation mechanisms are invoked. However, if this is not the case, the key generation mechanisms are given the opportunity to provide keys in this order:
If you implement a delegate for EODatabaseContext and the delegate implements the method
databaseContextNewPrimaryKey
and if that method returns a value other thannull
, the keys it generates are used. An implementation of this feature is discussed in Generating Custom Primary Keys.If you provide to an entity the name of a stored procedure to use during Get PK operations (the operation type
EOEntity.NextPrimaryKeyProcedureOperation
), that stored procedure is invoked to generate a primary key. See the chapter “Working With Entities” in EOModeler User Guide to learn about automatic stored procedure invocation.If an entity’s primary key’s data type is binary and it conforms to
EOTemporaryGlobalID.UniqueBinaryKeyLength
, Enterprise Objects automatically generates a primary key. This is discussed in Using Binary Keys.The adaptor automatically generates a primary key for an object whose entity has a noncompound, simple integer primary key.
If the first mechanism fails to provide a primary key, the second mechanism is used. This continues through the last mechanism; if it fails to provide a primary key, an exception is thrown.
In the case of an entity that is the destination of a relationship that propagates primary key, the destination entity is assigned a primary key immediately after its source entity is assigned a key. So for destination entities in a relationship that propagates primary key, the key generation mechanisms in the list above are not invoked for that entity (though they may be invoked for the source entity).
See Generating Custom Primary Keys and Using Compound Primary Keys for sample implementations of custom key generation.
Common Delegate Usage
A number of control points are provided that let you customize save operations in Enterprise Objects applications. Table 8-1 lists the delegate methods in EOEditingContext that you can use to customize save operations in the control layer, and Table 8-2 lists the delegate methods in EODatabaseContext that you can use to customize save operations in the access layer.
You can set the delegate for each object by invoking the class method setDefaultDelegate
.
Generating Custom Primary Keys
There are many reasons why you may want to or need to generate custom primary keys in an Enterprise Objects application. They are discussed in Key Generation. The following sections provide sample implementations of two of the custom-key generation mechanisms, using the EODatabaseContext delegate and using the automatic key generation for binary primary keys.
Using a Delegate
The EODatabaseContext class provides a delegate in which you can generate custom primary keys. This is especially useful if an entity has a compound primary key, but it is also useful if your application can’t use Enterprise Object’s automatic key generation for simple integer primary keys.
You can set the delegate by invoking EODatabaseContext.setDefaultDelegate(this)
in the class in which you implement the delegate method. An implementation of databaseContextNewPrimaryKey
is shown in Listing 8-1.
Listing 7-1 databaseContextNewPrimaryKey
implementation
public NSDictionary databaseContextNewPrimaryKey(EODatabaseContext dbCtxt, Object object, EOEntity entity) { |
NSArray rawRows = EOUtilities.rawRowsForSQL(new EOEditingContext(), "PKTester", "SELECT MAX(FOO_PK) FROM FOO"); |
NSDictionary rowWithPK = (NSDictionary)rawRows.objectAtIndex(0); |
Object maxPK = rowWithPK.objectForKey("FOO_PK"); |
int pk = (new Integer(maxPK.toString())).intValue(); |
NSMutableDictionary newPrimaryKey = new NSMutableDictionary(); |
NSArray entityPrimaryKeys = entity.primaryKeyAttributeNames(); |
Enumeration primaryKeyEnumerator = entityPrimaryKeys.objectEnumerator(); |
while (primaryKeyEnumerator.hasMoreElements()) { |
String pkName = (String)primaryKeyEnumerator.nextElement(); |
newPrimaryKey.takeValueForKey(new Integer(++pk), pkName); |
} |
return newPrimaryKey; |
} |
The method in Listing 8-1 returns a dictionary of key-value pairs; the keys are the names of the entity’s primary key attributes (which are returned by the method EOEntity.primaryKeyAttributeNames
) and the values are the values you generate in the method for each of those attributes.
In Listing 8-1, a SQL expression is sent to the database to determine the highest value for the entity’s primary key, FOO_PK
. That value is stored in the pk
variable, which is incremented in the while
loop to generate a unique primary key.
Using Binary Keys
Enterprise Objects provides another useful mechanism to generate primary keys. It generates a binary primary key if a primary key meets these criteria:
Its external data type is a “raw bytes” data type, such as Oracle
RAW
and OpenBasebinary
.Its internal data type is NSData.
Its internal data type width is 24 bytes.
The binary primary key generated in this case is a globally unique key based on an enterprise object’s EOTemporaryGlobalID. Generating primary keys this way has these advantages:
It doesn’t require a round trip to the database.
It is not database dependent.
The generated keys are globally unique.
Binary primary keys have the following characteristics, which can be considered disadvantages:
The generated keys are quite large.
Comparing keys requires more computing resources.
Binary keys can’t be part of a compound primary key.
Using Compound Primary Keys
Enterprise Objects supports tables that have compound primary keys. In EOModeler, you identify the attributes that are a part of the compound primary key by making those attributes primary keys (so the key icon is present in an attribute’s row). All of Enterprise Objects internal mechanisms that rely on primary keys (such as global ID creation) work just as well with compound primary keys as with simple primary keys. However, you cannot use a compound primary key and Enterprise Object’s automatic primary-key generation. You have to provide a primary key another way.
The easiest way to generate primary keys for compound primary keys is with the delegate method databaseContextNewPrimaryKey
. The implementation of this method in Generating Custom Primary Keys supports a compound primary key. It retrieves all of an entity’s primary keys and generates a unique value for each one.
Note that methods in Enterprise Objects that return primary key values usually return an NSDictionary object. This is to support the case in which a primary key is compound. Each key of the dictionary is the name of an attribute that is part of the compound key.
Copyright © 2002, 2007 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2007-07-11