Merged object spaces
Sometimes, one has to merge data from different databases. A typical case is merging resource databases in order to customize resources in a resource database. Providing an overload database for the ODABA system resource database, one may create customized ODABA tools by updating resources (forms, function code) and storing updates in the overload database. As long as not being changed, data is loaded from the original database. After being updated, data is stored and loaded from overload database.
In contrast to workspaces, which also provide a kind of overload feature, merging object spaces is based on key look-up, while workspaces use local identity (LOID) look-up. After copying a database, local identities will change and workspace overloads cannot be used anymore. Merged object spaces, however, do not depend on identities but on keys.
The simplest way of defining merged object spaces is using external data source definitions containing multiple data source paths. For providing customized resources for ODABA tools, one may define two database locations in the DATABASE option:
DATABASE=MyResources.dev;%ODABA_ROOT%/ode.dev
In this case, instances of an extent (e.g. ADK_Class, GUI resources) can be merged by reading first instances from MyResources.dev and adding instances from the ODABA resource database ode.dev. This allows merging existing definitions in the ODABA resource database with customer-defined definitions in MyResource.dev database.
Local collections referenced in an instance (e.g. fields or controls of an ADK_Class instance) are overloaded, too. This allows, e.g. adding fields in a customized application.
Merging data is supported for independent object spaces as well as for object space hierarchies. In order to activate merged object spaces, the application has to overload object spaces explicitly or mark object spaces in an object space hierarchy as overloaded (implicit overload).
Overloading object spaces means overloading instances and extending or overloading local and global collections, i.e. two different strategies depending on property definitions and option settings apply on collections:
- Extending collections
- Overloading collections
When extending collections, instances from collections in all involved object spaces are merged without duplicates (distinct union). Instances from top object space have highest priority. Overloading collections is based on unique keys, i.e. the collection must have at least one unique key (not necessarily a unique order), which is not an __IDENTITY key.
Collections with a limited number of instances (typically singular collections) are extended up to the maximum number of instances allowed for the collection. For singular collections, this is the same as overloading the collection.
Overloading collections means that the collection of a located instance completely replaces collections in overloaded object spaces. This also includes empty collections. Unidentified collections are always overloaded.
Whether collections are extended or overloaded depends on schema definition for the database. Extending collections is used for public extents, references and relationships. All other collections are overloaded.
In order to change the default behavior, the application may define collections to be extended explicitly by setting the ExtensionCollections option. The ExtensionCollections option must also contain names for extents to be extended. Extents not listed are overloaded. When the option is set, default behavior for public and not public collections is disabled and all collections not listed in the option are overloaded.
; explicite definition of extension collections in ini-file
; global collection ODC_ImpClass will be extended.
: Local function collection in class instances (pfunctions) are extended
; Local implementation collection in function instance are extended
ExtensionCollections=ODC_ImpClass;ODC_ImpClass.pfunctions;ODC_PFunction.implementations
When opening a property handle for an extent in an overloaded object space, an overload property will be created, which internally refers to extent property handles for all extents in overloaded object spaces. Internally, collections of all extents are merged by giving highest priority to the extent of the last overloaded object space.
Subordinated property handles for local collections are also opened as overloaded property handles by extending or overloading collections from all instances located in overloaded object spaces.
Access functions for overloaded collections behave slightly different from accessing ordinary collections.
- Changing access key for extended collections is supported for unique keys (except __IDENTITY key), only. Changing access key may change the content of the collection.
- Extended collections do not immediately react on changes made on involved collection by other property handles. In order to update an extended collection, Property::reopen() has to be called.
- Updated instances are stored on top property handle (localizing), always. This may cause creating instances for parent property handles, too.
- Deleting or removing an instance is possible only, when this instance is stored in top object space. When the instance is stored in top object space, but also in another object space of the overload list, the instance in the topmost object space containing the instance becomes active, i.e. the overloading instance is deleted, only. When not existing in another object space, the instance is removed from the extended collection.
- Renaming instances (changing key attributes) has an effect in top object space, only. I.e. overloaded instances in subsequent object spaces remain unchanged.
When a function implementation i01 of function read in class Person of the last object space has been updated and class person does not yet exit in top object space, a class instance Person with a function instance read is automatically created in the top object space before adding the updated implementation instance i01.
When updating instances stored in an overloaded database, those are copied to top object handle, i.e. data is localized. This action has two relevant side effects.
When the instance to be localized is not a global instance but owned by another instance, which is not an instance of the top object handle, the owner instance is localized, too. This process continues until reaching the topmost parent instance.
When localizing an instance, instances for protected collection properties are copied, when the collection is an owning collection. When the collection is not owning, instance references by name (primary key) are created, which will be resolved when referenced instances are localized. In this case, the collection is localized, but the instances are not. When the collection has got more than one index, name references are created for all indexes.
Collection properties marked as private or public are never copied. This is typical the case for secondary (inverse) relationships.