home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Demos / OOFILE / docs / relation.txt < prev    next >
Encoding:
Text File  |  1995-09-17  |  12.0 KB  |  249 lines  |  [TEXT/ttxt]

  1. PHASES OF DEFINING AND USING RELATIONSHIPS
  2.  
  3. SUMMARY
  4. 1) DECLARING THE TRAVERSAL PATHS
  5. Relationships in OOFILE, like other ODBMS, are managed by "traversal paths" between tables. 
  6.  
  7. OOFILE traversal paths are declared with two macros, the difference being a Ref being a 1-ary and a Set being an n-ary relationship.
  8.  
  9. REF_TABLE and SET_TABLE
  10.  
  11. eg:
  12. REF_TABLE(dbPeople)
  13. SET_TABLE(dbVisits)
  14.  
  15. CLASS_TABLE(dbPeople) {
  16. ...
  17.     dbVisitsSet    Visits;
  18. ...
  19.  
  20. };
  21.  
  22. CLASS_TABLE(dbVisits) {
  23. ...
  24.     dbPeopleRef    Person;
  25. ...
  26.  
  27. };
  28.  
  29.  
  30. 2) DEFINING THE CONCRETE RELATIONSHIP
  31. A link is defined between two concrete dbTable classes representing real tables (notthe table class definitions). The link may be named and consists of equal left and right sides defining:
  32. - the table being related
  33. - the traversal path in that table to its related table
  34. - optionally the name of the link
  35. - a join field, if the relationship is a runtime join
  36.  
  37. eg:
  38.     dbConnect_ctree    theDB;
  39.     dbPeople     People;
  40.     dbVisits    Visits;
  41.  
  42.    dbRelationship PatientVisits(People.Visits, Visits.Person, "Patient Visits");
  43.    PatientVisits.names(     "surgery visits",    "patient")
  44.                 .tables(    People,               Visits)
  45.  
  46. OR
  47. Declaring join fields implies the tables
  48.  
  49.    dbRelationship PatientVisits(People.Visits, Visits.Person, "Patient Visits");
  50.    PatientVisits.names(     "surgery visits",    "patient")
  51.                 .joinFields(People.PatientNo,     Visits.PatientNo);
  52.  
  53.  
  54. OR
  55. A simpler form of the above that can be used in a declaration - the only parameters are to the constructor and there's no further setup required. Thus, you could have a series of dbTable declarations and then dbRelationship declarations (see ooftest2.inc).
  56.  
  57.    dbRelationship PatientVisits(People.Visits,    Visits.Person, 
  58.                                 People.PatientNo, Visits.PatientNo);
  59.  
  60.  
  61. OR
  62. Again declaring join fields, and this time not naming either the relationship or traversal paths:
  63.  
  64. // this form more suitable for declaring a dbRelationship as part of another 
  65. // object than defining the relationship in that object's constructor
  66.    dbRelationship PatientVisits;  
  67.    PatientVisits.lhs(People.PatientNo, People.Visits);
  68.    PatientVisits.rhs(Visits.PatientNo, Visits.Person);
  69.  
  70.  
  71. 3) RELATED FIELD & TABLE EXPRESSIONS
  72. 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:  
  73. People.Visits->VisitDate()  // 1 link away
  74. People.Visits->Doctor->Name()   // 2 links away
  75.  
  76. Each operator-> along the path creates a link in a temporary chain.
  77.  
  78. The operator() evaluated at the end of the expression picks up the temporary chain and returns a reference to a field with the chain attached.
  79.  
  80. The same mechanism is used when operations are called on the related tables, eg:
  81. People.Visits.count();
  82.  
  83. ** all fields in the related table are pointed to by the same relationship chain object, including the related table itself.
  84.  
  85. NOTE: Whether it is People.Visits->... or People->Visits->... is totally up to you as to whether People is a pointer or not. The -> operator is only overloaded for the related files.
  86.  
  87.  
  88. 4) LOADING RELATED VALUES
  89. 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.
  90.  
  91. Relationship chains are marked as valid by the starting point - if you change its context then the current chains are invalidated.
  92.  
  93. 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.
  94.  
  95. If the chain is invalid, the links in the chain are followed from the left, loading the related context for each linked table. eg
  96. People.Visits->Doctor->Name()   // 2 links away
  97. loads a related context in Visits and again in Doctor.
  98.  
  99. 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.
  100.  
  101.  
  102. NOTE ON NAMES
  103. There is no restriction on naming, the use of Visits as a table object is purely a convention for the table dbVisits. Similary the traversal path People.Visits can be named anything you like. 
  104.  
  105.  
  106. DETAILS (with a c-tree bias)
  107. (You may want to skip to the CONCRETE EXAMPLEs at the end)
  108.  
  109. Note: in the following, the "prototypical" table and fields referred to are the originals, being the objects you would use in directly acessing that table in the database.
  110.  
  111. 1) DECLARING THE TRAVERSAL PATHS
  112. The REF_TABLE and SET_TABLE macros create very simple class definitions that are subclasses of dbRelRefBase. These classes include an operator-> which returns a pointer to a table. Thus a dbVisitsSet returns a dbVisits*. In the following description, dbRelRef can be assumed to stand in for one of these macro-generated subclasses.
  113.  
  114.  
  115. 2) DEFINING 
  116. By the time all the values have been supplied to the dbRelationship, it contains everything necessary to establish the relationship but has not actually done so. The dbRelationship constructor registers itself with the current connection, for later processing.
  117.  
  118. For join relationships, the default requires you to set the join field in the destination (rhs of relationship). You can set globally that join fields are updated by
  119.    dbRelationship::sAllJoinsUpdateJoinField = true; 
  120. or, for a given relationship  theRel:
  121.    theRel.joinsUpdateFields(true);
  122.  
  123. dbConnect_ctree::SetupConnection sends dbRelationship::buildRelationship to all the relationships. This in turn invokes dbRelHalf::buildRelationship for the left & right sides. The dbRelHalf is the true manager of the relationship, and it sets the dbRelRef (eg: People.Visits which is a dbVisitsSet.)
  124.  
  125.  
  126. 3a) RELATED FIELD EXPRESSIONS
  127. dbRelRef::LogTransition is called for each -> in the expression.
  128.  
  129. LogTransition simply appends a dbRelHalf* to a static list. 
  130.  
  131. Each -> also uses dbRelRef::RefersToTable to return a pointer to the prototypical table at the end of the link.
  132.  
  133. The second part of a related field expression is the parens on the final field, causing its operator() to be invoked. This is essential and, to cope with people forgetting these parens, the OOF_Debug setting will trap this side effect.
  134.  
  135. Each field type has an operator() which returns a field reference, either to itself or a special cloned field that's already been setup in the related table.
  136.  
  137. This includes a call to dbRelChain::buildChain as described in 3c).
  138.  
  139.  
  140. 3b) RELATED TABLE EXPRESSIONS
  141. For a related table, the operator-> will only be called if there is more than one link in the relationship eg: 
  142. People.Visits->Doctor.newRecord();
  143.  
  144. dbRelRef defines newRecord, count and all the other basic operations performed on a table. These versions all call dbRelRefBase::BuildRelChainToTable and then pass on the operation to the relationship chain.
  145.  
  146. BuildRelChainToTable performs all the steps described in 3a, calling LogTransition and ConsumePossibleRelationshipChain.
  147.  
  148. It concludes by calling dbRelChain::buildChain as described in step 3c), the same as for fields.
  149.  
  150.  
  151. 3c) BUILDING THE CONCRETE RELATIONSHIP CHAINS
  152. dbRelChain::buildChain calls dbTable::askTableToBuildValidChain from the first table in the chain. This factory will either:
  153. - return an already built chain with the same list of nodes, or
  154. - ask the candidate chain to convert itself into a real chain, by building a parallel list of cloned tables.
  155.  
  156. This sounds more complex than it really is. We start with a list of tables through which the relationship chain passes (in most cases, just the single end point, for a 1-link chain). However, the tables in this list can't be used to manage the actual relating process as they are the "prototypical" tables - they already have a current selection and should NOT be affected as a side-effect of relationships.
  157.  
  158. Thus, we copy each table along the chain to give us a "clone" table that *can* have its context modified.
  159.  
  160. The cloning process will end up copying:
  161. - the complete table as defined by the user
  162. - the field dictionary
  163. - the backend
  164. - alloc new buffer space in the backend
  165.  
  166. The only thing not copied or re-created is the current selection in the table - we will create our own selections as we validate relationships.
  167.  
  168.  
  169. 4) LOADING RELATED VALUES
  170. Now that the relationship chain is built, we have a list of cloned tables (and backends) sitting waiting to be given a search command to load the related selections. This is (again) a delayed evaluation until the last possible minute. Thus, we may create the relationship chains and (eg: if the user cancelled a dialog) never actually load the related data.
  171.  
  172. dbField::validateContextInCaseRelated is called by every operation that either assigns to or reads from a dbField descendant. This is one area where you see the need for each field in the related context to use the same relationship chain - we validate the chain once and, provided it is not marked invalid, the subsequent calls (by other related fields) do nothing.
  173.  
  174. Validating a context simply walks along the chain, calling dbRelHalf::refreshContext at each node, with a parameter of the cloned table at that node. The dbRelHalf provides the knowledge on how to load the related context and the cloned dbTable provides a context into which to load.
  175.  
  176. The actual relating mechanism varies. In the simple dynamic joins, it is a search carried out on the target field, based on the value in the join field.
  177.  
  178. For OID-based relationships a runtime search may be replaced by saved pointer links, depending on the specific class, the backend and the number of related records.
  179.  
  180. As mentioned above, chains have a valid flag which saves us traversing the relationship for each field. The valid flag is reset by the starting table - each time the current record changes. This is by newRecord, a search or an iteration function.
  181.  
  182.  
  183. 5) CONCRETE EXAMPLE - FIELD
  184. Let's illustrate the sequence with:
  185. People.Visits->Why() = "Flu";
  186.  
  187. People.Visits-> 
  188.    invokes a dbRelRefBase::operator->, adding a link to the 
  189.    (empty) temporary chain and returning a Visits*
  190.  
  191. Visits->Why() 
  192.    invokes dbChar::operator() returning a reference to a clone of the 
  193.    prototypical field, via dbField::GetRelatedFieldOrUs.
  194.  
  195.  
  196.  
  197. In copying the field, the dbField copy constructor calls 
  198.    OOF_mixRelChainEndPoint::ConsumePossibleRelationshipChain and picks up the 
  199.    temporary relationship chain
  200.  
  201. = "Flu";
  202.    invokes dbChar::operator=(char*) on the cloned field.
  203.  
  204.  
  205. 6) CONCRETE EXAMPLE - TABLE
  206. In this example, we are going to create a new related record, assume assignment of related values has occurred as shown in step 5), and show the saving of the related data.
  207.  
  208. People.Visits.newRecord();  // first related rec
  209. // assign fields
  210. People.Visits.newRecord();  // second related rec
  211. // assign fields
  212. People.saveRecord();
  213.  
  214.  
  215. People.Visits.newRecord()
  216.  
  217.    invokes dbRelRefBase::newRecord()
  218.    - adds a link to the temporary chain
  219.    - calls OOF_mixRelChainEndPoint::ConsumePossibleRelationshipChain
  220.    - builds the chain it just picked up
  221.    - calls dbRelChain::newRecord() which calls dbTable::newRecord on the
  222.      final table in the chain. This table is a clone and so the new record 
  223.      buffer created here doesn't affect any other context on that table.
  224.  
  225.      dbTable::newRecord (on the Visits clone) calls dbTable::ContextChange
  226.      which on the second call has a dirty record buffer to cache for later 
  227.      saving. Caching buffers to save is how any table behaves with its save 
  228.      option set to requireExplicitAndBuffer.
  229.  
  230.  
  231.  
  232. People.saveRecord();
  233.    calls dbRelChain::saveRecord for each currently attached chain (before saving 
  234.    its local buffers).
  235.  
  236.    dbRelChain::saveRecord:
  237. 1) walks down the chain, checking mJoinUpdatesJoinField at each point and, if it 
  238.    has changed, updates all the related records 
  239.  
  240. 2) calls dbTable::saveRecord on each table in the 
  241.    chain. This will normally only affect the final record in the chain but it is 
  242.    possible that a link may have been added through an intermediate chain, which    
  243.    needs saving. 
  244.  
  245.    In this case it just calls saveRecord on the cloned Visits, at the end of the 
  246.    chain.
  247.  
  248.    Because of the stacked saved buffer in the Visits clone, there are two 
  249.    records written.