Maintaining Dictionary Key Paths

A key path is a mechanism that enables you to define an automatically maintained dictionary key that is not an embedded property of the members of the dictionary, but is instead derived from the member objects.

When you define a key path, you specify a chain of references starting from the member class and finishing at an end point. At run time, the references are traversed to arrive at the end point that yields the key value.

In the example shown in the following image, myPart.mySupplier.name is the key path of the ModuleBySupplierNameDict dictionary.

In this example, each company has a dictionary of all of its modules (allModules of type ModuleBySupplierNameDict). This dictionary is keyed by the supplier name of the module’s part using a key path (myPart.mySupplier.name).

Automatic Key Maintenance

Dictionary keys are automatically maintained when the dictionary has one or more explicit inverse references; that is, when it participates as an end point in a bi-directional relationship. For example, if allModules and allParts did not have their respective inverses myCompany and mySupplier, automatic key maintenance would not be performed for them.

When a dictionary property participates in a relationship (as allModules and allParts do in this example), if any of the non-key path keys are changed the dictionary is automatically updated to reflect the new key values. Automatic key maintenance is also performed for key path keys. In the example used in this discussion, this means that when a module has been added to allModules, if any component of the key path myPart.mySupplier.name is changed, allModules is automatically updated to reflect the new key value. This automatic key maintenance is performed only because allModules participates in a relationship (that is, it has myCompany as an inverse).

To enable JADE to automatically maintain keys for all properties of a key path, each class on which a key path property is defined must have an inverse reference back to the prior key path property. (This is required so that if any point along a key path is changed, JADE can find its way back to the related collection or collections to update key values.) An exception is raised at run time if a key path component does not have an inverse reference to its prior component.

As the deletion of objects is atomic, transactions are aborted if an exception is raised during automatic maintenance actions. If the attempt to commit a transaction after an exception occurs during a delete action (for example, user code in a delete, or destructor, method), the transaction is aborted if any user-defined exception handler does not do so. After the exception, no objects will have been deleted.

Using the example shown in the image, the myPart.mySupplier.name key path myPart must have the inverse myModule and mySupplier must have the inverse allParts. If either of these inverses were missing, the first attempt to use the ModuleBySupplierNameDict collection in a relationship (allModules, in this case) would result in a 1099 exception (Key path component does not have an inverse to its prior component) at run time.

Resolving Exceptions

If your runtime application encounters 1099 exceptions, the following options are available to you.

Setting Key Path References to Null

When using key paths, take care when setting intermediate references in the key path to null (either directly by assigning to the property or indirectly by deleting the referenced object). For example, assume that allModules in the example used in this discussion contains five different modules, with each module related to a different part and with each part supplied by a different supplier, as listed in the following table.

Module myPart myPart.mySupplier myPart.mySupplier.name (key value)
module1 part1 supplier1 "Supplier One"
module2 part2 supplier2 "Supplier Two"
module3 part3 supplier3 "Supplier Three"
module4 part4 supplier4 "Supplier Four"
module5 part5 supplier5 "Supplier Five"

Imagine that for the first Part instance (part1), you set its supplier reference to null or you delete the supplier (which implicitly sets mySupplier to null because of the Supplier.allParts inverse). The mySupplier property is part of the key path and it is being updated, so allModules must automatically be updated to change the key value of the entry for module1. Because myPart.mySupplier is now null (meaning that myPart.mySupplier.name cannot be evaluated), the key value in the dictionary for module1 is set to null.

Now imagine that you set mySupplier on the second Part instance (part2) to null. The same thing happens, only it now results in a duplicate key value in allModules because module1 already exists with a null key. This will be a problem only if allModules does not allow duplicates.

When working with key paths and dictionaries that do not allow-duplicates, ensure that key path references are null for one member object at a time only.