Important: The information in this document is obsolete and should not be used for new development.
Applying a qualifier with key path to top of horizontally mapped inheritance hierarchy generates invalid SQL.
Enterprise Objects Framework's query building mechanism doesn't handle relationships to inheritance hierarchies. For example, suppose that you are attempting to qualify a fetch through a to-many relationship (planes) that points to the top of a horizontally mapped inheritance hierarchy (for the entities Plane, FighterPlane, and TrainerPlane). If you want the query to test against all tables, you'd expect Enterprise Objects Framework to generate SQL similar to the following:
SELECT t0.AIRPORT_IDFROM PLANE t1, FIGHTER t2, TRAINER t3, AIRPORT t0WHERE |
(t1.LENGTH <= 1000 AND t0.AIRPORT_ID = t1.AIRPORT_FK) OR |
(t2.LENGTH <= 1000 AND t0.AIRPORT_ID = t1.AIRPORT_FK) OR |
(t3.LENGTH <= 1000 AND t0.AIRPORT_ID = t1.AIRPORT_FK) |
Instead, Enterprise Objects Framework generates the following SQL:
SELECT t0.AIRPORT_IDFROM PLANE t1, AIRPORT t0WHERE (t1.LENGTH <= 1000) |
AND t0.AIRPORT_ID = t1.AIRPORT_FK |
In other words, only the table for the root of the hierarchy is queried.
You can create a qualifier that generates the correct SQL by:
Adding relationships in the source entity to all the tables in the inheritance hierarchy. For example, to the Airport entity, you'd add the relationships toFighters and toTrainers to the destination entities FighterPlane and TrainerPlane, respectively. Mark the relationships so they aren't class properties.
When building your query, explicitly list these extra relationships. In the Planes example, you'd fetch from Airport where planes.length < 1000 OR toFigtherPlanes.length < 1000 OR toTrainerPlanes.length < 1000
Alternatively, you might be able to solve this problem more generally by writing a post processor for EOQualifiers that splits up clauses that perform inheritance tests. The post processor could even programmatically generate the additional relationships on demand and register them with the model using names like plane_Subclass_Fighter.
A generic EOQualifier post processor could be wired into Enterprise Objects Framework so that application writers don't have to know it exists. The right place for such a mechanism is probably in EOKeyValueQualifier's schemaBasedQualifierWithRootEntity: method (see EOSQLQualifier.h). You could put the post processor code in a subclass of EOKeyValueQualifier (with an appropriate call to super after the transformation, if any, is performed) and have your subclass pose as EOKeyValueQualifier.
Concurrent multithreaded saves may bypass optimistic locking check.
Although it has not yet been verified, EOF may have a brief window where two sessions modifying the same object will get last-one-wins behavior, instead of a merge (which is still last-one-wins at the attribute level). For example:
Editing context 1 locks, modifies object 1
Editing context 2 locks, modifies object 1
Editing context 1 saves changes, unlocks, change notification enqueued on editing context 1
Editing context 2 saves its modifications to object 1 without merging in editing context 1's modifications.
None.
EOF can generate incorrect SQL for disjunctions of qualifiers where one of the qualifiers involves an optional relationship.
The form of the SQL generated for an EOOrQualifier is similar to the SQL generated for an EOAndQualifier, but with "OR" substituted for "AND". For example, the qualifier attr1 = v1 OR rel.attr2 = v2 would generate a SQL WHERE clause something like:
WHERE (ENTITY.ATTR1 = v1 or REL.ATTR2 = v2) AND REL.PK = ENTITY.PK |
For a mandatory relationship 'rel', there will always be a match in the REL table and the above SQL WHERE clause will end up giving the correct result. However, for an optional relationship with a NULL value, the WHERE clause fails to return any rows even if there were rows in the ENTITY table that matched the value v1. The appropriate SQL would be:
WHERE ENTITY.ATTR1 = v1 or (REL.ATTR2 = v2 AND REL.PK = ENTITY.PK) |
which would give the correct results for all cases.
Separate your original fetch specification into independent fetch specifications that do not have disjunctions involving optional relationships, and concatenate the multiple results. Naturally, the same EO might be returned by multiple fetch specifications so you may need to filter out duplicates from the concatenated results. For complicated fetch specifications, it may be more effecient to use a custom SQL query (see documentation on EOCustomQueryExpressionHintKey in EODatabaseContext.h). If you choose to use custom SQL and you have multiple qualifiers across relationships, you should consider using DISTINCT since there may be multiple ways for one EO to satisfy the qualifier.
The EOF documentation implies that only a single SELECT statement is needed to perform a deep fetch when all the subentities of the fetch specification's entity map to the same table. However, only one specific case is handled with a single SELECT. In the general case, one SELECT is issued for each subentity.
EOF performs one SELECT for an inheritance hierarchy if each subentity is mapped to the same table and if each subentity either has a restricting qualifier of the form "attr = val", where "attr" is the same for all subentities and "val" is unique to each subentity, or the subentity is abstract and has no restricting qualifier. If some subentity doesn't qualify for the optimization, then its parent also doesn't qualify. However, if some subtree of an inheritance hierarchy qualifies for the single table fetch, then that subtree is fetched in one SELECT even though other subentities of the parent entity have to be fetched individually.
Arrange your single table inheritance heirarchy so that it meets the optimization criteria.
After calling myEODatabaseContext.invalidateAllObjects(), subsequent saves either fail to save changes or just raise.
If an application has any EOEditingContexts that contain unsaved inserted objects, any call to myEODatabaseContext.invalidateAllObjects() will leave those EOEditingContexts in an inconsistent state. If you try to save the changes in any such EOEditingContext, the inserted objects will not be saved, or you might get an exception because these objects are mistakenly treated as updated objects.
Replace all invocations of dbc.invalidateAllObjects()
with dbc.invalidateObjectsWithGlobalIDs(dbc.database().snapshots().allKeys())
The JDBC Adaptor does not "quote" SQL identifiers.
Any unusual SQL identifier (for example, a column name containing a space) needs to be quoted in an SQL statement. The JDBCPlugIns provided with the JDBC Adaptor do not add the required quotes. This can lead to parse errors from the database when these unusual identifiers are used in an SQL statement generated by the JDBC Adaptor.
If possible, change the SQL identifiers in your database schema so that quoting is not required. Otherwise, create a custom JDBCPlugIn subclass and turn on identifier quoting by setting the protected variable _externalQuoteChar = null in your plugin's constructor. This will cause JDBCExpression's implementation of externalNameQuoteCharacter() to use the database's quote character. Note that _externalQuoteChar is type String (not char).
Additions and removals in a to-many relationship are not saved if the foreign key isn't marked as a class property or as used-for-locking, and also if there is no inverse relationship defined.
Suppose that an entity's foreign key attribute isn't marked as a class property or as used-for-locking. If you designate the foreign key attribute as a to-many relationship's destination key, the foreign key value isn't always updated. This occurs because the destination entity doesn't know that the attribute participates in a relationship. Therefore, the destination entity doesn't fetch the foreign key from the database or update it. However, the problem will not occur if there is a inverse relationship defined for the to-many.
The simplest work-around is to mark attributes that are destination keys of a to-many relationship as used-for-locking. Alternatively, it is sufficient to define an inverse relationship for the to-many relationship. The inverse relationship does not need to be a class property. The final alternative is to make a small change to your application. At the beginning of the application (when all models are loaded into the model group), call EOModelGroup.defaultGroup().loadAllModelObjects().
This one-liner will insure that all (hidden) inverse relationships are created before the first fetch operation, which in turn will cause the necessary foreign keys to be fetched (and then saved).
EOF does not generate proper SQL for horizontal inheritance when qualifying over a key path.
When fetching with an EOFetchSpecification containing an EOKeyValueQualifier whose key is a keypath to an entity (SuperEntity) for which there is a subentity (SubEntity) inheriting from it using horizonal inheritance, EOF only joins with SuperEntity and ignores SubEntity. In this situation, an incomplete result may be returned. Vertical and Single-Table inheritance are not affected by this bug.
None. Try to use single-table inheritance instead of horizontal inheritance.
When trying to promote a raw row to an EO, a NullPointerException is thrown.
When raw rows are fetched from an Oracle database using EOUtilities.rawRowsForSQL(), the dictionaries returned contain key names in all uppercase. Since Oracle represents everything in uppercase, these keys correspond to the column names and not the usual EOF convention.
You can control the labels used in returning the result set by specifying them explicitly in the SELECT list in your SQL statement. Make the label for the primary key column match your PK attribute name in your model. That way, describeResults() will use the label as key. Something like this should work...
SELECT FOO_ID "fooID", BAR, BAZ FROM MY_TABLE WHERE ... |
A model should not have multiple class properties that refer to the same storage.
If the flattened property chain contains class properties, the flattened property should not also be a class property.
Do not mark the flattened property as a class property. Implement business logic methods that use key-value-coding expressions instead, for example "rel1.rel2.rel3" instead of "flattenedRel3".
Validation exceptions in EOF handle the undo stack inconsistently.
While performing an EOF operation (such as saveChanges) on an EOEditingContext which supports undo/redo (active by default), the state of the undo stack will differ depending on the kind of validation exception which is caught. For most validation exceptions, the undo stack will be untouched, and the application can invoke undo() or make some changes and try to save again.
However, for any validation exception stemming from delete propagation, undo() will already have been invoked. If an application wishes to try again, it will either need to apply the entire delete operation again, or redo() and then make a correction.
In generation, the only way to differentiate between the kinds of validation exceptions and which may require an redo() is to parse the exception message text.
None.
Last updated: 2004-12-02