Back to index


PHASES OF DEFINING AND USING RELATIONSHIPS

SUMMARY

1) DECLARING THE TRAVERSAL PATHS

Relationships in OOFILE, like other OODBMS, are managed by "traversal paths" between tables.

OOFILE traversal paths are declared with two macros, the difference being a Ref being a one-to-one or "1-ary" and a Set being an "n-ary" relationship.

A traditional M:N relationship is therefore declared with a Set ("n-ary") relationship on each side.

DECLARE_REF and DECLARE_SET

eg:
DECLARE_REF(dbPatients)
DECLARE_SET(dbVisits)

DECLARE_CLASS(dbPatients) {
...
dbVisitsSet Visits;
...

};

DECLARE_CLASS(dbVisits) {
...
dbPatientsRef Patient;
...

};

2) DEFINING THE CONCRETE RELATIONSHIP

A link is defined between two concrete dbTable classes representing real tables (not the table class definitions). eg:

dbRelationship PatientVisits(People.Visits, Visits.Person);

3) RELATED FIELD & TABLE EXPRESSIONS

A related field expression starts with a traversal path and involves the normal pointer operator->. This ends in a field and may go through intermediate traversal paths eg:
People.Visits->VisitDate // 1 link away
People.Visits->Doctor->Name // 2 links away

Each operator-> along the path creates a link in a temporary chain. The operator returns a real dbTable* that points to a "cloned" table. The cloned table manages the related selection.

The same mechanism is used when operations are called on the related tables, eg:
People.Visits->count();

4) LOADING RELATED VALUES

Loading the related context is delayed until needed. This could be prompted by a related table command (count, newRecord, start...) or accessing a related field.

Relationship chains are marked as valid by the starting point - if you change its context then the current chains are invalidated.

When a related value must be loaded, the chain may already be valid - implying a valid table at the end of the chain. This means the field just loads its value as normal.

If the chain is invalid, the links in the chain are followed from the left, loading the related context for each linked table. eg
People.Visits->Doctor->Name // 2 links away
loads a related context in Visits and again in Doctor.

This loading related context is independent of *how* the relationship is stored - it may be a runtime join over key fields, be stored pointers, physically aggregated records or some other mechanism.

NOTE ON NAMES

There is no restriction on naming, the use of Visits as a table object is purely a convention for the table dbVisits. Similarly the traversal path People.Visits can be named anything you like.

PARENT-CHILD RELATIONSHIPS

Relationships are considered Parent-Child when you add the related records through the Parent on the lhs. (More general linking, closer to the ODMG standard, is described below.)

This implies that new and changed records on the rhs of a relationships are saved automatically.

However, if you don't want deletes to be propagated, you can explicitly call propagateRelatedDeletes(); on the lhs expression, eg: in the constructor:

DECLARE_CLASS(dbPatients)
dbChar LastName, Othernames;
dbVisitsSet Visits;
dbLong Salary;
dbPatients() :
LastName(40, "Last Name", kIndexed),
Othernames(80, "Other names", kIndexed),
Salary("Salary", kIndexed)
{
Visits.propagateRelatedDeletes(false);
};

};

******* WARNING *******

If you use a pointer-based (non-join) relationship, the lhs table may be saved more than once. This could affect you if you override the dbTable::saveRecord() with one of your own, with side-effects. Normally it is not a problem.

The reason is for 1-1 relationships - the lhs table is saved first to get its record pointer, then the rhs table is saved, which returns its record pointer, then the lhs table must be saved again with the pointer to the rhs.

This behaviour will be optimised in future versions to avoid redundant saves. However, there will still be times when the lhs record is saved twice (if both are new records).

GENERAL RELATIONSHIPS

These relationships are between objects that exist independently of each other.

For example, consider possible relationships between databases dbPeople and dbCompanies (assuming no moonlighting on a second job!).

dbPeople --> runs

1:1

CEO <-- dbCompanies

dbPeople --> worksFor

1:N

employees <-- dbCompanies

dbPeople --> buysFrom

M:N

customers <-- dbCompanies

A limitiation of the current M:N relationships is that you must declare an intermediate table to do the mapping.

Thus, while we can navigate People.buysFrom->CompanyName, we actually have to declare a Purchases database, that sits in between:

dbPeople --> buysFrom

1:N

Purchases
(mapping table)

N:1

customers <-- dbCompanies


Back to index


(c) Copyright A.D. Software 1994-1997 (All Rights Reserved).
Last Updated: 8th March 1997