Using property extensions
Similar to key/value databases, extension properties provide high flexibility for database applications. Property extensions may be appended to any expandable object instance, i.e. property extensions are data type independent. In order to use the property extension feature, the following steps are required:
- Defining expandable data types as complex data types
- Defining an extension schema (set of extension properties) in the dictionary and/or database.
- Creating property extensions for expandable object instances and assigning values to property extensions.
Expandable data types are data types that contain an __EXTENSIONS reference. When adding an __EXTENSIONS reference, the system automatically adds proper reference attributes, which must not be changed except the create attribute. The create attribute defines the type of property extensions. Extension properties may be defined with two modes:
- automatic - property extensions are created whenever required
- managed - property extensions have to be created explicitly by the application
In order to support automatic extensions, create has to be set to true. For supporting managed extensions, create has to be set to false.
ODABA provides a system data type __XOBJECT, which might also be used as base type for user-defined data types. Besides other features, __XOBJECT supports automatic extension. In general, defining another __EXTENSIONS reference in a derived database type is not allowed. Thus, using __XOBJECT as base type, which also supports GUID and instance owner features, implies automatic extensions.
When not using __XOBJECT as base type, the user-defined data type definition decides with the definition of the __EXTENSIONS reference, whether managed or automatic extension is supported. When using an expandable data type as base type for another complex data type, this automatically becomes expandable. Specialized object instances inherit all property extensions from its base instance. Thus, when adding a property extension ext1 to any specialized instance inheriting from e.g. __XOBJECT, all base type instances may also access ext1. This is no problem for exclusive base types, but it might cause confusion when adding property extensions to instances with shared base types. E,g, when Patient and Employee inherit shared from Person, adding an extension property ext1 to a Patient instance, this property extension also becomes visible for the Person instance, but also for all Employee instances stored for this person.
In version 12, property overloading will be supported in derived data types, which allows overloading the __EXTENSIONS reference defined in __XOBJECT for supporting manged extensions for object instances inheriting from __XOBJECT.
Before creating property extensions, extension properties have to be defined in the dictionary or database (extension schema). Similar to complex data type properties, extension properties may be defined as attributes, references or relationships (but not as base types). Different ways of defining extension properties are described in Defining extension properties.
For optimizing access to extension attributes, attributes with a limited size are stored directly in an extension index provided for each extended object instance. Usually, numbers, logical (Boolean) values or date and time will be defined as extension attributes. Complex data types or larger text fields have to be defined as extension reference (just storing a database pointer to the data instance in the extension index).
Defining extension references is supported for single or multiple (collection) references. Typically, extension references refer to large text fields (here larger than 8 bytes) or complex (user-defined) data types. Any data type defined in the dictionary might be referenced. Referenced data types need not to be expandable. The only restriction is, that referenced data types must not contain relationships or shared base types.
One may also define extension relationships. Extension relationships refer to one or more instances with a complex data type. Data types referenced in a relationship have to be defined as expandable data types. Typically, extension relationships are defined as weak-typed __XOBJECT collections or weak-typed collections inheriting from such a common application specific data type.
Using more specific data types in extension relationships will restrict the usage of such extension relationships, i.e. those relationships cannot be appended to any kind of expandable object instance. Defining a relationship rel1 referring to instances of expandable data type A with an inverse relationship rel2 referring to instances of expandable data type B, restricts the usage of extension relationship rel1 to instances of data type B, only. Otherwise, the inverse relationship could not be maintained properly. This limitation results from consistency rules for inverse relationships and will not limit the usage of relationships without inverse relationships (which is not really suggested, since it will cause other consistency problems earlier or later).
In general, one may refer to extension properties similar to normal properties in by name (e.g. in a property or operation path or in Property::property() or Property::value()). Extension property names are, however, defined in global scope. Thus, extension property names may conflict with other property or function names and one should try to avoid:
- calling an extension property similar to Property or Value functions
- calling an extension property similar to other complex data type properties or functions
- property extension names starting with two underlines (those names are used frequently for meta-attributes)
One possible way is using capital letters for extension properties, when other properties always use lower case letters, another way is prefixing extension properties with a string never used in other names (e.g. ext_).
Extension property names are checked after checking data type property names, meta-attributes, local extents or enumerations and functions or actions defined for a complex data type, but before checking Property or Value functions. Hence, calling an extension property similar to a complex data type property or function requires explicit extension function for extension property access (Property::extensionPropert() or Property::extensionValue())
In order to distinguish between extension properties and Property or Value functions, one may scope Property or Value function names in operation paths (e.g. persons.Property::value(), when an extension property has been called value).
After defining extension properties and expandable data types, extension properties might be appended to any expandable object instance. Depending on using managed or automatic extension, property extensions are created explicitly or automatically on demand.
Creating a property extension automatically just happens when storing a value for the extension property (see example below). As long as no property extension has been stored, the value for the property extension is empty or contains the value defined as initialize value in the extension property. When not changing the default value, no property extension will be created in the instance, unless one explicitly requires creating the property extension by calling Property::provideExtension().
When trying to assign a value to a property extension that has not yet been created, it will fail when managed extension has been defined. In this case, property extensions have always to be created explicitly by calling Property::provideExtension().
In order to display property extensions assigned to an object instance, one may call Property::extensionProperty(number) as long as number is less than Property::extensionCount().
Details about accessing property extensions are described in Accessing property extensions.
... fragment ( Property &person ) { // person is an extendable data type
Value ext1(person,"ext1"); // creates a value handle for extension attribute ext1
ext1 = 123; // assigns a value, but not yet stored
ext1.save(); // stores the value to the instance
//changinmg selection in person automatically stores updated values
}
... osi_fragment ( Person &person ) { // person is an extendable data type
person.ext1 = 123; // will be stored automatically
// when changing the selection in person
}