Creating Handle hierarchy
For getting access to database details one may open a hierarchical structure of database access objects:
- [ Application ]
- [ Client ]
- Dictionary
- Database
- [ ObjectSpace ]
- Property
- Value
Each of those access handles allows to manage any number of subordinated access handles, i.e. one may several databases for the same dictionary etc. Handle hierarchies correspond to physical application resources, which are managed by the resource specific objects referenced by the handle:
Application - Global application object
Client - Server connection
Dictionary - Resource database
Database - Database
DBObject - Universe/transaction
Property - Instance/collection
Value - Value
Usually, the application has to create a dictionary, database and property handle in order to run 'normal' applications. More complicate applications (e.g. with multiple server connections) require explicitly defined application clients. Complex database applications may also store data in different object spaces, which requires explicitly opened object space handles.
The example below shows how to get access to persons in the sample database (simple case).
// creating a simple access hierarchy
... fragment ( ) {
Dictionary dict;
Database db;
// Applixation::mainClient is used as default client
dict.open("Sample.dev", Read, true);
db.open(dict, "Sample.dat", Read, true);
// object space is the database (root object space)
Property persons(db, "Person::Persons", Read);
Value name(persons,"name");
while ( persons.next() )
Application::output(name.toString());
}
Usually, access handles are created as empty access handles (constructor without parameters) and opened later on explicitly calling the corresponding open() function.
For convenience, Property and Value provide enhanced constructors, which implicitly call the appropriate open function.
In order to check, whether an access handle has been opened or not, all access handle classes provide an isValid() function, which returns true, in case the handle has been opened successfully.
Dictionary dict;
Database db;
dict.open("c:/ODABA/Sample/Sample.dev");
db.open(dict,"c:/ODABA/Sample/Sample.dat");
// global extent
Property company(db,"Company",Read);
// namespace extent
Property person(db,"Person::Persons",Read); // implicit open() call
Value name(person,"name"); // implicit open() call
Once, a handle hierarchy has been defined, handles can be used in many different ways for accessing data. Usually, dictionary and database will not change while running an application, but property handle and value hierarchies might become rather complex.
In order to access properties and values, property and value handle have to be in a certain state. E.g. in order to read data for children of a person, an instance has to be selected in the person property handle. In order to read or update a value for a child, the child has to be selected. When changing the selection state in the person property handle, the children property handle automatically becomes unselected and values for children are not accessible until a new instance has been selected for the person and the children property handle.
... fragment ( Database &rDatabase ) {
Property persons(rDatabase,"Person::Persons",Update);
// parent property (persons) has to be opened, but not selected
Property children(person,"children");
// parent property (children) has to be opened, but not selected
Value name(children,"name");
while ( person.next() ) // selects a person
while ( children.next() ) // selects a child
Application::output(name.toString()); // prints name for selected child
}
Similar to create and open access handles, they will be closed automatically when destroying an access handle. One may, however, also explicitly call the close() function.
All access handle objects have got a reference count. Thus, creating a handle copy (e.g. using the copy constructor) just creates a reference to the same access node. Thus, destroying or closing an access handle not necessarily deletes the access node.
When closing the last reference to an access node, all subordinated access nodes will be deleted and the access handles referring to it will be closed. E.g. when closing a database access node, all subordinated object space and property handles will be closed. Property handles opened with copyCursor() are also closed, when the original access node (cursor) will be destroyed.
In order to avoid inconsistent database states, all updates are stored before deleting an access node (i.e. closing the last access handle referring to it).