# DRIVER: une couche objet virtuelle persistante pour le raisonnement sur les bases de données relationnelles

Franck LEBASTARD

► To cite this version:

Franck LEBASTARD. DRIVER: une couche objet virtuelle persistante pour le raisonnement sur les bases de données relationnelles. Génie logiciel [cs.SE]. INSA LYON, 1993. Français. NNT: 1993ISAL0030. tel-04508061

HAL Id: tel-04508061

https://theses.hal.science/tel-04508061

Submitted on 25 Mar 2024

HAL is a multi-disciplinary open access
archive for the deposit and dissemination of sci-
entific research documents, whether they are pub-
lished or not. The documents may come from
teaching and research institutions in France or
abroad, or from public or private research centers.

L'archive ouverte pluridisciplinaire HAL, est
destinée au dépôt et à la diffusion de documents
scientifiques de niveau recherche, publiés ou non,
émanant des établissements d'enseignement et de
recherche français ou étrangers, des laboratoires
publics ou privés.

<figure><img src="image_1.png" /></figure>

Distributed under a Creative Commons Attribution 4.0 International License
THÈSE

présentée devant

L'INSTITUT NATIONAL DES SCIENCES APPLIQUÉES
DE LYON

pour obtenir

LE GRADE DE DOCTEUR

Spécialité :

INGÉNIERIE INFORMATIQUE

par

Franck LEBASTARD

DRIVER :
UNE COUCHE OBJET VIRTUELLE PERSISTANTE
POUR LE RAISONNEMENT
SUR LES BASES DE DONNÉES RELATIONNELLES

Soutenue le 26 mars 1993 devant la Commission d'Examen

<table><tr><td>Jury MM. P. PRÉVOT</td><td>Président</td></tr><tr><td>P. BAZEX</td><td>Rapporteur</td></tr><tr><td>J. KOULOUMDJIAN</td><td>Examinateur</td></tr><tr><td>S. MIRANDA</td><td>Rapporteur</td></tr><tr><td>B. NEVEU</td><td>Examinateur</td></tr><tr><td>F. RECHENMANN</td><td>Rapporteur</td></tr></table>
À mes parents,

À ma femme Nadine
et ma fille Laura.
Remerciements

Ce travail a été réalisé au sein du projet SECOIA, à l'INRIA Sophia-Antipolis. Le projet SECOIA, qui regroupe des chercheurs de l'INRIA et du Ministère de l'Équipement, fait partie du CERMICS, laboratoire de recherches commun à l'INRIA et à l'École Nationale des Ponts et Chaussées (ENPC).

Je remercie tout particulièrement :

- Bernard LARROUTUROU, Directeur du CERMICS, pour m'avoir accueilli dans son laboratoire, et avoir fait preuve à mon égard d'une constante bienveillance, d'une grande patience et d'une non moins grande compréhension;

- Bertrand NEVEU, Chef du projet SECOIA, pour la confiance qu'il m'a accordée pendant ces années de travail, pour sa disponibilité exemplaire et sa gentillesse. Je lui suis en particulier très reconnaissant d'avoir toujours accepté de lire à maintes et maintes reprises mes écrits que je lui soumettais;

- l'INRIA, pour les moyens extraordinaires qu'il met à la disposition de ses chercheurs. Je n'oublierai jamais la qualité de travail dont j'ai pu bénéficier ici.

Je tiens également à remercier :

- Patrick PRÉVOT, Professeur à l'INSA de Lyon, pour l'honneur qu'il me fait à présider le jury de ma thèse. Je le remercie également très chaleureusement pour avoir effectué en mon nom de nombreuses démarches administratives, m'évitant ainsi de multiples déplacements à Lyon;

- Pierre BAZEX, Professeur à l'Institut de Recherche en Informatique de Toulouse (IRIT), Serge MIRANDA, Professeur à l'université de Nice-Sophia-Antipolis et Directeur du MBDS, et François RECHENMANN, Directeur de recherche à l'INRIA et Chef du projet SHERPA, pour avoir accepté, malgré leurs emplois du temps extrêmement chargés, d'être les rapporteurs de mon travail;

- Jacques KOULOUMDJIAN, professeur à l'INSA de Lyon, pour avoir accepté de participer au jury;

- Rose DIENG, Chef du projet ACACIA à l'INRIA, pour avoir imaginé un joli nom pour mon système —à savoir, l'acronyme DRIVER—, pour la lecture complète qu'elle a bien voulu faire de mon mémoire et pour les remarques avisées qui en ont résulté. Je la remercie aussi (et surtout) pour son amitié et son éternelle bonne humeur;

- Pierre HAREN, Directeur Général de la société Ilog, ancien Chef du projet SMECI, pour m'avoir accueilli dans son projet alors que j'étais encore à l'école et pour avoir su me donner le goût de la recherche. Je n'oublierai jamais que je fais aujourd'hui ce métier passionnant grâce à lui;
- *Patrick ALBERT* et *Hakim ERHILI*, de la société Ilog, pour les nombreuses discus-
sions que nous avons eues ensemble et qui se sont toujours révélées très utiles;

- *Jean-Patrick GIACOMETTI*, un de mes collègues préférés, pour sa grande aide à rédiger l'introduction et la conclusion de ma thèse;

- l'équipe du centre de documentation pour sa disponibilité en toutes circonstances,
et en particulier *Catherine ALAUZUN*, pour l'énorme travail qu'elle a fait sur ma
bibliographie;

- l'équipe du centre de calcul, qui est toujours là quand on a besoin d'elle;

- enfin, l'ensemble des membres des projets SECOIA et ACACIA avec qui j'ai apprécié de travailler et que je regrette de ne pouvoir tous citer.
"— Adieu, dit le renard. Voici mon secret. Il est très simple : on ne voit bien qu'avec le cœur. L'essentiel est invisible pour les yeux."

Antoine de Saint-Exupéry, "Le Petit Prince"
# Table des matières

## INTRODUCTION 17

## 1 ÉTAT DE L'ART 21

1.1 Vers une meilleure expressivité des données 23

1.1.1 Extensions statiques du modèle relationnel 23

1.1.2 Les modèles de données sémantiques 25

1.1.3 Les modèles de données hyper-sémantiques 29

1.1.4 Les modèles de données orientés-objet 30

1.2 Ajout de capacités de raisonnement aux SGBDR 35

1.3 Couplage d'un environnement IA avec un SGBD 41

1.4 Conclusion 42

## 2 DRIVER, COUCHE OBJET VIRTUELLE PERSISTANTE 45

2.1 Caractéristiques générales 47

2.1.1 Un pont entre deux mondes 47

2.1.2 Une couche objet virtuelle persistante 49

2.1.3 Des représentations optimales 51

2.1.4 Exploitation de plusieurs bases 53

2.2 Gestion de la persistance 53

2.2.1 Granularité de persistance 53

2.2.2 Persistance des classes et des correspondances 54

2.3 Transactions 55

2.3.1 Transaction DRIVER et transaction SGBD 55

2.3.2 Maintien de la cohérence 57

2.3.3 Défauts d'objet et objets relationnels 57

2.3.4 Accès aux champs et objets jumeaux 59

2.3.4.1 L'accès direct 59

2.3.4.2 Les objets jumeaux 59

2.3.4.3 Objets jumeaux et raisonnement 61

2.4 Exploitation avancée des connexions multiples 62

2.4.1 Vers des objets de persistance distribuée 63

2.4.2 Vers des représentations multiples contextuelles 64
<table><tbody><tr><td>4.1.2.2</td><td>Détails de l'algorithme</td><td>143</td></tr><tr><td>4.1.2.3</td><td>Optimisation de l'algorithme de chargement</td><td>147</td></tr><tr><td>4.1.3</td><td>Chargement récursif d'objets</td><td>150</td></tr><tr><td>4.1.3.1</td><td>Intérêt et danger</td><td>150</td></tr><tr><td>4.1.3.2</td><td>Principe et détails de l'algorithme</td><td>151</td></tr><tr><td>4.1.3.3</td><td>Coût de l'algorithme</td><td>152</td></tr><tr><td>4.1.4</td><td>Les requêtes de consultation</td><td>156</td></tr><tr><td>4.1.4.1</td><td>Représentation des requêtes de consultation</td><td>156</td></tr><tr><td>4.1.4.2</td><td>Construction d'une requête de classe en lecture</td><td>159</td></tr><tr><td>4.1.4.3</td><td>Exploitation des résultats</td><td>163</td></tr><tr><td>4.1.4.4</td><td>Optimisation des requêtes de sélection</td><td>166</td></tr><tr><td>4.2</td><td>Écriture des objets dans la base</td><td>169</td></tr><tr><td>4.2.1</td><td>Algorithme général d'écriture</td><td>169</td></tr><tr><td>4.2.1.1</td><td>Généralités</td><td>169</td></tr><tr><td>4.2.1.2</td><td>Principe de l'algorithme général d'écriture</td><td>171</td></tr><tr><td>4.2.1.3</td><td>Détails de l'algorithme</td><td>172</td></tr><tr><td>4.2.2</td><td>Mise-à-jour des objets dans la base</td><td>179</td></tr><tr><td>4.2.2.1</td><td>Principe de l'algorithme</td><td>179</td></tr><tr><td>4.2.2.2</td><td>Méthodes de mise-à-jour des différentes classes de champ</td><td>179</td></tr><tr><td>4.2.2.3</td><td>Les requêtes de mise-à-jour</td><td>186</td></tr><tr><td>4.2.3</td><td>Insertion d'objets dans la base</td><td>188</td></tr><tr><td>4.2.3.1</td><td>Difficultés posées par l'insertion d'objet</td><td>189</td></tr><tr><td>4.2.3.2</td><td>Les classes système de gestion d'écriture</td><td>190</td></tr><tr><td>4.2.3.3</td><td>Principe de résolution</td><td>194</td></tr><tr><td>4.2.3.4</td><td>Gestion des incohérences</td><td>195</td></tr><tr><td>4.2.3.5</td><td>Détails de l'algorithme</td><td>198</td></tr><tr><td>4.2.3.6</td><td>Méthodes d'insertion des différentes classes de champ</td><td>201</td></tr><tr><td>4.2.3.7</td><td>Les requêtes d'insertion</td><td>208</td></tr><tr><td>4.2.4</td><td>Écriture des champs de type "ensemble"</td><td>210</td></tr><tr><td>4.2.5</td><td>Modification d'objet en cours d'écriture</td><td>210</td></tr><tr><td>4.2.6</td><td> mistake fatale en cours d'écriture</td><td>210</td></tr><tr><td>4.3</td><td>Suppression des objets relationnels</td><td>211</td></tr><tr><td>4.4</td><td>Sélection d'objets relationnels dans la base</td><td>214</td></tr><tr><td>4.4.1</td><td>Généralités</td><td>214</td></tr><tr><td>4.4.2</td><td>CLERIC</td><td>216</td></tr><tr><td colspan="2">CONCLUSION</td><td>219</td></tr><tr><td colspan="2">BIBLIOGRAPHIE</td><td>223</td></tr><tr><td colspan="2">A ANNEXES</td><td>237</td></tr><tr><td>A.1</td><td>ASQUELL</td><td>239</td></tr><tr><td>A.2</td><td>Exemple de jointure objet restreinte</td><td>241</td></tr><tr><td>A.3</td><td>L'interface graphique de DRIVER</td><td>243</td></tr></tbody></table>
<table><tr><td>A.4 Conventions utilisées dans l'exposé des algorithmes</td><td>253</td></tr><tr><td>B MANUEL DE RÉFÉRENCE (DRIVER v1.35)</td><td>255</td></tr><tr><td>B.1 Construction d'un schéma de correspondances</td><td>259</td></tr><tr><td>B.1.1 Création et recherche d'un schéma de correspondances</td><td>259</td></tr><tr><td>B.1.2 Description de la représentation relationnelle</td><td>262</td></tr><tr><td>B.1.3 Description de la représentation objet</td><td>268</td></tr><tr><td>B.1.4 Description des correspondances</td><td>280</td></tr><tr><td>B.1.5 Description relationnelle : fonctions de bas niveau</td><td>293</td></tr><tr><td>B.1.6 Création des classes et des relations</td><td>302</td></tr><tr><td>B.1.7 Génération automatique de classes et de relations</td><td>302</td></tr><tr><td>B.2 Les primitives utilisateur</td><td>305</td></tr><tr><td>B.2.1 Connexion et communication avec le SGBD</td><td>305</td></tr><tr><td>B.2.2 Filtrage et manipulation des objets relationnels</td><td>306</td></tr><tr><td>B.2.3 Manipulation explicite des défauts d'objet</td><td>312</td></tr><tr><td>B.2.4 Accès à l'objet relationnel</td><td>313</td></tr><tr><td>B.2.5 Écriture d'objets dans la base de données</td><td>317</td></tr><tr><td>B.2.6 Persistance des schémas de correspondances</td><td>319</td></tr><tr><td>B.2.7 Interaction avec l'utilisateur</td><td>320</td></tr><tr><td>B.2.8 Divers</td><td>321</td></tr><tr><td>B.3 Filtrage des champs en lecture et écriture</td><td>323</td></tr><tr><td>B.4 Utilisation d'un nouveau modèle objet</td><td>327</td></tr><tr><td>B.4.1 Manipulation des modèles objet</td><td>327</td></tr><tr><td>B.4.2 L'interface fonctionnelle objet</td><td>328</td></tr><tr><td>B.5 Exemple de base relationnelle, smecidemo</td><td>333</td></tr><tr><td>B.6 Exemple de schéma de correspondances</td><td>339</td></tr><tr><td>B.7 Exemple de session d'utilisation</td><td>345</td></tr><tr><td>B.8 Exemple de prise en compte d'un modèle objet</td><td>349</td></tr><tr><td>B.9 Messages d'erreurs et autres de DRIVER</td><td>359</td></tr><tr><td>B.10 Index des fonctions et des variables</td><td>367</td></tr><tr><td>INDEX</td><td>374</td></tr></table>
Table des figures

<table><tr><td>1.1</td><td>Un exemple de relation NF2</td><td>24</td></tr><tr><td>1.2</td><td>Exemple de schéma décrit dans un modèle sémantique générique</td><td>26</td></tr><tr><td>1.3</td><td>Hiérarchie de classes sous ORION</td><td>34</td></tr><tr><td>2.1</td><td>Un schéma de correspondances entre deux représentations</td><td>48</td></tr><tr><td>2.2</td><td>La couche objet virtuelle</td><td>50</td></tr><tr><td>2.3</td><td>Exemple de hiérarchie mêlant classes persistantes ou non</td><td>51</td></tr><tr><td>2.4</td><td>Défaut d'objet et objet relationnel</td><td>58</td></tr><tr><td>2.5</td><td>Masquage de l'objet relationnel par l'objet jumeau</td><td>60</td></tr><tr><td>2.6</td><td>Exemple d'arbre d'états de SMECI</td><td>63</td></tr><tr><td>3.1</td><td>Structures relationnelles : emp, table des employés</td><td>69</td></tr><tr><td>3.2</td><td>Données relationnelles : contenu de la table emp</td><td>70</td></tr><tr><td>3.3</td><td>Exemples de classes</td><td>71</td></tr><tr><td>3.4</td><td>Exemples d'objets</td><td>72</td></tr><tr><td>3.5</td><td>Hiérarchies de classes de modèles objet</td><td>73</td></tr><tr><td>3.6</td><td>Associations de classes et de relations</td><td>85</td></tr><tr><td>3.7</td><td>Correspondances objet - n-uplet relationnel</td><td>86</td></tr><tr><td>3.8</td><td>Associations champ atomique - attribut de relation principale</td><td>87</td></tr><tr><td>3.9</td><td>Jointure de la table emp vers la table person</td><td>87</td></tr><tr><td>3.10</td><td>Associations champ atomique - attribut de relation secondaire</td><td>88</td></tr><tr><td>3.11</td><td>Jointure de la table emp vers la table dept</td><td>90</td></tr><tr><td>3.12</td><td>Associations champ objet - chaîne de jointures</td><td>91</td></tr><tr><td>3.13</td><td>Tables project et emproj</td><td>95</td></tr><tr><td>3.14</td><td>Associations champ "ensemble d'objets" - chaîne de jointures</td><td>96</td></tr><tr><td>3.15</td><td>Association champ "ensemble d'atomes" - attribut</td><td>97</td></tr><tr><td>3.16</td><td>Table deptsite</td><td>97</td></tr><tr><td>3.17</td><td>Correspondance d'un champ "ensemble ordonné d'objets"</td><td>98</td></tr><tr><td>3.18</td><td>Associations champ "objet de classe inconnue" - attribut</td><td>99</td></tr><tr><td>3.19</td><td>Correspondance d'un champ objet2 : la table emp complétée</td><td>100</td></tr><tr><td>3.20</td><td>La table deptproducts</td><td>100</td></tr><tr><td>3.21</td><td>Correspondance de champs calculés</td><td>101</td></tr><tr><td>3.22</td><td>Auto-jointure, correspondance de champ objet</td><td>102</td></tr><tr><td>3.23</td><td>Auto-jointure de la table emp vers elle-même</td><td>103</td></tr><tr><td>3.24</td><td>L'environnement letjoins</td><td>107</td></tr></table>
<table>
  <tr>
    <td>3.25</td>
    <td>Hiérarchie des classes de champ</td>
    <td>123</td>
  </tr>
  <tr>
    <td>3.26</td>
    <td>Hiérarchie des classes de contraintes et jointures</td>
    <td>132</td>
  </tr>
  <tr>
    <td>4.1</td>
    <td>Un défaut d'objet dans l'environnement de SMECI</td>
    <td>140</td>
  </tr>
  <tr>
    <td>4.2</td>
    <td>Algorithme de chargement d'un défaut d'objet</td>
    <td>141</td>
  </tr>
  <tr>
    <td>4.3</td>
    <td>Algorithme de chargement d'objets de même classe</td>
    <td>144</td>
  </tr>
  <tr>
    <td>4.4</td>
    <td>Algorithme optimisé de chargement d'objets de même classe</td>
    <td>148</td>
  </tr>
  <tr>
    <td>4.5</td>
    <td>Une hiérarchie de classes</td>
    <td>149</td>
  </tr>
  <tr>
    <td>4.6</td>
    <td>Algorithme de réduction de la file d'attente des classes</td>
    <td>150</td>
  </tr>
  <tr>
    <td>4.7</td>
    <td>Algorithme de chargement récursif d'objets</td>
    <td>151</td>
  </tr>
  <tr>
    <td>4.8</td>
    <td>Syntaxe d'une requête SQL de consultation de données</td>
    <td>156</td>
  </tr>
  <tr>
    <td>4.9</td>
    <td>Requêtes SQL de consultation de données sous Ingres</td>
    <td>157</td>
  </tr>
  <tr>
    <td>4.10</td>
    <td>Algorithme de construction d'une requête de classe en lecture</td>
    <td>160</td>
  </tr>
  <tr>
    <td>4.11</td>
    <td>Objet Driver-select initialisé pour la classe Employé</td>
    <td>160</td>
  </tr>
  <tr>
    <td>4.12</td>
    <td>L'objet Driver-select du champ employés de la classe Projet</td>
    <td>162</td>
  </tr>
  <tr>
    <td>4.13</td>
    <td>L'objet Driver-select du champ moy-sal de la classe Département</td>
    <td>162</td>
  </tr>
  <tr>
    <td>4.14</td>
    <td>L'objet Driver-select de la classe Employé</td>
    <td>163</td>
  </tr>
  <tr>
    <td>4.15</td>
    <td>N-uplets solutions à la requête de la classe Employé</td>
    <td>164</td>
  </tr>
  <tr>
    <td>4.16</td>
    <td>N-uplets solutions aux requêtes des champs</td>
    <td>165</td>
  </tr>
  <tr>
    <td>4.17</td>
    <td>Dictionnaires d'accès aux valeurs des champs</td>
    <td>166</td>
  </tr>
  <tr>
    <td>4.18</td>
    <td>Règles d'équivalence utilisées pour la réduction des restrictions SQL</td>
    <td>167</td>
  </tr>
  <tr>
    <td>4.19</td>
    <td>Algorithme général d'écriture d'un ensemble d'objets</td>
    <td>170</td>
  </tr>
  <tr>
    <td>4.20</td>
    <td>Objets à mettre à jour dans la base</td>
    <td>175</td>
  </tr>
  <tr>
    <td>4.21</td>
    <td>Algorithme de mise-à-jour d'un objet dans la base</td>
    <td>178</td>
  </tr>
  <tr>
    <td>4.22</td>
    <td>Méthodes info-for-update</td>
    <td>180</td>
  </tr>
  <tr>
    <td>4.23</td>
    <td>Algorithme de contained-object-key</td>
    <td>182</td>
  </tr>
  <tr>
    <td>4.24</td>
    <td>Algorithme de contained-object-key (suite)</td>
    <td>183</td>
  </tr>
  <tr>
    <td>4.25</td>
    <td>Algorithme de allowed-objects</td>
    <td>185</td>
  </tr>
  <tr>
    <td>4.26</td>
    <td>Syntaxe d'une requête SQL de mise-à-jour de données</td>
    <td>187</td>
  </tr>
  <tr>
    <td>4.27</td>
    <td>Objets Driver-update de mise-à-jour de l'objet james</td>
    <td>188</td>
  </tr>
  <tr>
    <td>4.28</td>
    <td>Objets Driver-class-saving des classes Employé et Vendeur</td>
    <td>192</td>
  </tr>
  <tr>
    <td>4.29</td>
    <td>Objet Driver-saving initial de johnson pour la classe Employé</td>
    <td>194</td>
  </tr>
  <tr>
    <td>4.30</td>
    <td>Gestion d'incohérence dans la résolution d'écriture d'objet</td>
    <td>196</td>
  </tr>
  <tr>
    <td>4.31</td>
    <td>Algorithme d'insertion d'un objet dans la base</td>
    <td>199</td>
  </tr>
  <tr>
    <td>4.32</td>
    <td>Algorithme d'insertion d'un objet dans la base (suite)</td>
    <td>200</td>
  </tr>
  <tr>
    <td>4.33</td>
    <td>Méthodes info-for-writing</td>
    <td>202</td>
  </tr>
  <tr>
    <td>4.34</td>
    <td>Algorithmes de write-object-field-if-possible</td>
    <td>204</td>
  </tr>
  <tr>
    <td>4.35</td>
    <td>Algorithmes de write-object-field</td>
    <td>205</td>
  </tr>
  <tr>
    <td>4.36</td>
    <td>Objet Driver-saving de johnson (classe Employé) après la première passe</td>
    <td>206</td>
  </tr>
  <tr>
    <td>4.37</td>
    <td>Objet Driver-saving de johnson (classe Employé) après la seconde passe</td>
    <td>207</td>
  </tr>
  <tr>
    <td>4.38</td>
    <td>Syntaxe d'une requête SQL d'insertion de données</td>
    <td>208</td>
  </tr>
  <tr>
    <td>4.39</td>
    <td>Objets Driver-insert de mise-à-jour de l'objet johnson</td>
    <td>209</td>
  </tr>
  <tr>
    <td>4.40</td>
    <td>Algorithme de traitement des erreurs fatales</td>
    <td>211</td>
  </tr>
  <tr>
    <td>4.41</td>
    <td>Algorithme de suppression d'objet</td>
    <td>212</td>
  </tr>
</table>
<table><tr><td>4.42</td><td>Exemple de sélection d'objets relationnels</td><td>215</td></tr><tr><td>4.43</td><td>Justification de la sélection d'objets en deux temps</td><td>215</td></tr><tr><td>4.44</td><td>Hiérarchie de la classe Driver-sql-op</td><td>216</td></tr><tr><td>A.1</td><td>Panneau principal de l'interface graphique de DRIVER</td><td>243</td></tr><tr><td>A.2</td><td>Panneau de connexion à la base</td><td>243</td></tr><tr><td>A.3</td><td>Panneau des classes</td><td>244</td></tr><tr><td>A.4</td><td>Définition d'une classe</td><td>244</td></tr><tr><td>A.5</td><td>Édition d'une classe</td><td>245</td></tr><tr><td>A.6</td><td>Édition d'un champ</td><td>245</td></tr><tr><td>A.7</td><td>Édition d'une contrainte de classe</td><td>246</td></tr><tr><td>A.8</td><td>Édition de corps de contrainte</td><td>246</td></tr><tr><td>A.9</td><td>Éditeur de contraintes de classe</td><td>246</td></tr><tr><td>A.10</td><td>Panneau des tables</td><td>247</td></tr><tr><td>A.11</td><td>Importation du schéma de la base</td><td>248</td></tr><tr><td>A.12</td><td>Édition d'une table</td><td>249</td></tr><tr><td>A.13</td><td>Édition d'un attribut</td><td>249</td></tr><tr><td>A.14</td><td>Panneau des correspondances (1)</td><td>250</td></tr><tr><td>A.15</td><td>Panneau des correspondances (2)</td><td>251</td></tr><tr><td>A.16</td><td>Édition d'un critère d'ordre de champ "ensemble"</td><td>252</td></tr><tr><td>A.17</td><td>Édition d'une jointure</td><td>252</td></tr></table>
# INTRODUCTION

L'Intelligence Artificielle (IA) aujourd'hui veut traiter des problèmes si importants qu'elle ne peut plus se passer des services des Bases de Données (BD).

En tant que chercheurs en IA, nous avons ressenti ce besoin d'exploiter, dans nos raisonnements, de grandes bases de connaissances, de taille bien supérieure à celle de nos bases habituelles. Nous nous sommes alors tournés vers les bases de données.

L'idée d'exploiter conjointement les techniques d'IA et BD est un des faits marquants de la recherche en informatique de ces dernières années. D'un côté, les chercheurs en BD se sont intéressés à la façon d'améliorer l'expressivité des données dans les BD (représentation de données implicites, de règles, ...). De l'autre, les chercheurs en IA ont eu de plus en plus besoin de manipuler et de raisonner sur de grandes quantités d'informations. Très naturellement, des travaux des deux domaines ont porté sur des thèmes communs pour finalement former un nouveau champ d'activité, connu sous le nom de *Systèmes à Grandes Bases de Connaissances*, ou *Bases de données expertes*, ou *SGBD déductifs*, ou encore *Systèmes experts persistants*.

Nous nous sommes tout de suite heurtés à une difficulté importante à laquelle se trouve confronté tout utilisateur de Système de Gestion de Bases de Données (SGBD) venant du Génie Logiciel ou des langages de programmation, et qui est connue sous le nom d'"Impedance Mismatch" [CM84]. Ce problème d'adaptation est double. D'une part, il tient au fait qu'on exploite élément par élément des données ensemblistes ("record at a time vs data set"); nous ne considérerons pas ici ce point d'achoppement important. D'autre part —et c'est à quoi nous nous limiterons—, il tient à la différence des modèles de données : le modèle de données d'un SGBD est généralement différent de celui de l'application qui l'exploite. Une conversion des données s'avère alors nécessaire à chaque échange d'information.

À l'heure actuelle, deux types de SGBD émergent<fnref n="1" /> principalement : les SGBD relationnels et les SGBD orientés objet. Développeurs d'outils d'IA utilisant divers modèles objet, nous nous sommes naturellement d'abord intéressés aux seconds. Le problème de

<footnote n="1">¹Sans être en opposition : approche "évolutionnaire" ↔ approche "révolutionnaire" [MVGP92].</footnote>
l'Impedance Mismatch est apparu plus que jamais. En effet, les SGBD orientés objet actuels ne proposent pas à leurs utilisateurs de décrire le modèle objet qu'ils souhaitent voir géré. Nous avions donc, d'un côté, les modèles objet de nos outils, et de l'autre, celui proposé par le SGBD orienté objet. Dans tous les cas, une conversion était nécessaire pour transférer ou recopier un objet de la base dans l'environnement d'un outil, et inversement.

L'utilisation d'un SGBD relationnel nous a posé les mêmes problèmes. Le modèle relationnel propose en effet des structures de données qui sont également très différentes de celles de nos modèles objet, si bien que de semblables conversions sont également nécessaires.

Après étude de l' existant, nous avons finalement choisi de développer un système nouveau, cumulant de nombreux avantages des SGBD relationnels et des SGBD orientés objet.

Ce système, appelé DRIVER, définit une couche objet au dessus des bases de données relationnelles. Il propose des correspondances entre des concepts communs à de très nombreux modèles objet et ceux existant dans le modèle relationnel. Ces correspondances générales permettent d'associer des représentations objet à toute base de données relationnelle. En particulier, elles permettent, aux yeux de l'utilisateur, de transformer un SGBD relationnel en SGBD orienté objet.

Ce système se distingue des SGBD objet du marché par deux avantages.

D'abord, l'utilisateur **choisit** le modèle objet qui va être géré. Ainsi, les bases de connaissances auxquelles donne accès DRIVER sont directement visibles et accessibles dans le modèle objet de l'utilisateur. Pour la même raison, les objets de l'utilisateur peuvent très facilement devenir persistants, selon sa convenance.

Ensuite, la seconde originalité de DRIVER est qu'il permet la prise en compte **immédiate** de toute base relationnelle **sous forme objet** : tous les catalogues, toutes les bases de données disponibles sous forme relationnelle sont immédiatement utilisables sous forme de bases de connaissances objet. Et inversement, toute base de connaissances objet construite avec DRIVER peut être tout aussi rapidement proposée aux nombreux utilisateurs de SGBD relationnels. Ce second point fort de DRIVER est d'autant plus intéressant que le modèle relationnel est un standard complètement formalisé et unique, et que les SGBD basés sur ce modèle constituent aujourd'hui la quasi-totalité du marché.

Les difficultés qui se sont posées à nous ont été assez nombreuses.

La principale a été de préserver, dans la définition de nos correspondances, le double rôle de DRIVER, à savoir permettre aussi bien la prise en compte des données relationnelles dans un environnement objet que d'apporter la persistance aux objets de cet environnement. Pour cette raison, les correspondances relationnelles d'un certain nombre de concepts objet n'ont pas été identifiées de façon immédiate, en particulier celles des champs à objet ou encore l'absence de valeur dans les champs de type objet.

Un autre problème important a été posé par l'insertion d'objets dans la base. Ce problème cumulait en fait deux difficultés, la première qu'on peut résumer sous le titre d'"insertion de n-uplets dans une vue", et la seconde, liée au fait que les objets constituent
entre eux un graphe complexe.

Nous verrons plus avant que certaines difficultés n'ont pas encore été surmontées.

En particulier, si nous fournissons un outil efficace de filtrage d'objets *dans la base*, nous n'avons pas aujourd'hui de solution aussi efficace permettant le filtrage *sur l'ensemble de la couche objet virtuelle* (*base* + environnement) de *DRIVER*.

## Plan de lecture

Cette thèse présente une étude détaillée du modèle *DRIVER*. Nous nous sommes efforcés de décrire le système de façon progressive de manière à permettre au lecteur de s'informer selon ses besoins. Le généraliste ou l'utilisateur se satisfera de la description générale qui est faite du système chapitre 2 tandis que le spécialiste poursuivra son investigation jusqu'aux études de l'implantation et des algorithmes de communication qui sont faites aux chapitres 3 et 4.

Le chapitre 1 présente l'état de l'art<fnref n="2" /> en matière de Grandes Bases de Connaissances. Y sont répertoriés les travaux dans ce domaine, visant, pour certains, à développer le pouvoir expressif des modèles de données traditionnels, et, pour d'autres, à introduire des capacités de raisonnement au sein même des SGBD.

Le second présente les caractéristiques générales de *DRIVER* et de la couche objet virtuelle et persistante qu'il propose. Les principales fonctionnalités du système sont également décrites et les idées phares qui les sous-tendent, telles celles de *correspondance*, *schéma de correspondances*, *objet jumeau* ou encore *défaut d'objet*, sont introduites.

Le chapitre 3 présente le modèle relationnel, un modèle objet de référence<fnref n="3" />, et propose des correspondances entre les deux. Les correspondances relationnelles des différents concepts objet sont considérées les unes après les autres.

Il se termine en présentant le méta-schéma, schéma de correspondances permettant de rendre persistantes les informations système contenues dans les schémas. La description du méta-schéma est une occasion de détailler un certain nombre de classes système de *DRIVER*.

Le chapitre 4 présente les principaux algorithmes utilisés par *DRIVER*. Sont notamment détaillés les algorithmes de chargement d'objets, d'insertion, de mise-à-jour et de suppression d'objets dans la base.

En annexe, le lecteur trouvera copie du manuel de référence de *DRIVER*, où sont présentées toutes les fonctionnalités du logiciel. Leur description est suivie d'un certain nombre d'exemples, dont un exemple de configuration du système pour un modèle objet donné —en l'occurrence celui de SMECI—, un exemple complet de schéma de correspondances et

<footnote n="2">²À paraître également dans [Leb93].</footnote>

<footnote n="3">³En fait, résultant du regroupement de concepts objet communs à de nombreux modèles objet.</footnote>
un exemple de session d'utilisation.
# Chapitre 1

## ÉTAT DE L'ART
Les Systèmes de Gestion de Base de Données (SGBD) sont devenus le cœur des systèmes d'information. Au fur et à mesure que leur puissance s'est développée, les utilisateurs se sont faits de plus en plus nombreux. Provenant de domaines variés, leurs besoins ont rapidement dépassé ceux des traditionnelles applications de gestion. Les nouveaux domaines d'application comme la Conception et Fabrication Assistée par Ordinateur (CFAO), l'Ingénierie Assistée par Ordinateur (IAO), la domotique, le traitement d'images, les systèmes à base d'hypertextes, les systèmes d'analyses géographiques et statistiques, l'informatique militaire temps réel, les systèmes experts en général, ont besoin de manipuler et de stocker de grandes quantités d'objets complexes.

Leurs besoins, mal satisfaits par les SGBD classiques, sont à l'origine de travaux de recherche qu'on peut regrouper en deux catégories :

* les études visant à développer le pouvoir expressif des modèles de données traditionnels;

* celles cherchant à introduire des capacités de raisonnement au sein même des SGBD.

# 1.1 Vers une meilleure expressivité des données

Un certain nombre de travaux cherchent à étendre les capacités d'expressivité du modèle relationnel (cf. §3.1.1) en lui adjoignant de nouveaux concepts. Il est en effet extrêmement restrictif d'imposer à un utilisateur de BD d'exprimer toute information sous la forme de simples n-uplets de valeurs atomiques. Non seulement cette modélisation des données est lourde et maladroite, mais elle entraîne en plus pour les applications des surcoûts en temps liés au traitement de requêtes inutilement complexes.

## 1.1.1 Extensions statiques du modèle relationnel

Dans le modèle relationnel, l'obligation pour une relation de ne comprendre que des attributs atomiques est imposée par la première forme normale (1FN) ou, en d'autres termes, par la logique du premier ordre. Les efforts de nombreux chercheurs visent donc à relâcher cette contrainte pour permettre l'utilisation de structures de données plus puissantes.

Un certain nombre d'extensions au modèle relationnel ont été développées ces dernières années pour permettre la gestion d'objets complexes. Les plus connues sont le modèle NF² de Jaeschke et Schek [JS82], le modèle des relations emboîtées de Fischer et Thomas [FT83], le modèle d'Abiteboul et Beeri [AB84, AG87, BA87] plus général mais aussi plus récent que les deux précédents, le modèle B-relationnel de Le Thanh [LeT86], le modèle de données Franco-Arménien FAD (Franco-Armenian Data model) de Bancilhon, Briggs, Khoshafian et Valduriez [BBKV87, BK89], le modèle ALGRES de Ceri, Crespi-Reghizzi,
Lavazza et Zicari [CCRG$^{+}$88, CCRZ$^{+}$90].

Généralement, on regroupe tous ces modèles sous la dénomination de *modèles NF²* (Non-First Normal Form) car la première forme normale y est abandonnée. Dans ces modèles, le type des attributs n’est pas nécessairement atomique, il peut être lui-même une relation. Un attribut de type relation comprend lui-même des attributs qui peuvent être également de type relation, et ainsi de suite. Ces relations NF² sont également appelées *relations emboîtées*.

<table><caption>Figure 1.1 : Un exemple de relation NF2</caption><thead><tr><th colspan="8">{ DEPARTMENTS }</th></tr><tr><th rowspan="2">Deptno</th><th rowspan="2">Mgrno</th><th colspan="4">{ PROJECTS }</th><th rowspan="2">Budget</th><th colspan="2">{ EQUIPMENT }</th></tr><tr><th>Projno</th><th>Pname</th><th colspan="2">&lt; MEMBERS &gt;</th><th>Qu</th><th>Type</th></tr><tr><th></th><th></th><th></th><th></th><th>Empno</th><th>Function</th><th></th><th></th><th></th></tr></thead><tbody><tr><td rowspan="3">314</td><td rowspan="3">56194</td><td rowspan="3">17</td><td rowspan="3">CGA</td><td>39582</td><td>Leader</td><td rowspan="3">320,000</td><td>2</td><td>3278</td></tr><tr><td>56019</td><td>Consultant</td><td>3</td><td>PC/AT</td></tr><tr><td>69011</td><td>Secretary</td><td>1</td><td>PC</td></tr><tr><td rowspan="3">218</td><td rowspan="3">71349</td><td rowspan="3">25</td><td rowspan="3">HPAR</td><td>58912</td><td>Staff</td><td rowspan="3">440,000</td><td>2</td><td>3278</td></tr><tr><td>90011</td><td>Leader</td><td>2</td><td>PC/AT</td></tr><tr><td></td><td></td><td></td><td>1</td><td>3179</td></tr><tr><td></td><td></td><td></td><td></td><td>92100</td><td>Staff</td><td></td><td>1</td><td>PC/GA</td></tr><tr><td></td><td></td><td></td><td></td><td></td><td>Leader</td><td></td><td></td><td></td></tr></tbody></table>

Un exemple de telle relation est présenté figure 1.1; il est tiré de [PD89]. La relation **DEPARTMENTS** comprend deux attributs de type relation **PROJECTS** et **EQUIPMENT**. **PROJECTS** en comprend lui-même un qui est **MEMBERS**. Les accolades "{"..."}" et les crochets "<"...">" identifient les relations et les attributs de type relation. Les premières signalent les collections non ordonnées et les seconds, celles où l'ordre des éléments est préservé.

Les relations emboîtées minimisent la redondance de données et permettent un traitement efficace des requêtes puisque les jointures sont réalisées à l'intérieur même des relations.

Enfin, ces modèles sont particulièrement intéressants du fait qu'ils reposent sur des bases théoriques solides. Elles sont récapitulées dans [Lev92].

Trois systèmes majeurs concrétisent actuellement les efforts d'implémentation qui ont été faits dans ce domaine. Ce sont le système POSTGRES réalisé à Berkeley [RS87, SK91], le projet AIM (Advanced Information Management prototype) du centre IBM à Heidelberg [DKA$^{+}$86, PA86, PT86, PD89], enfin le projet DASDBS (DArmStadt DataBase System) de l'Université de Darmstadt [DPS86, PSS$^{+}$87, SPS87, SS89]. Le système VERSO de l'INRIA [BRS82, Bid86, SAB$^{+}$89] a également été l'une des réalisations phares de ce domaine, mais son développement a été interrompu en 1989.
Pour finir, ajoutons que d'autres travaux récents cherchent à étendre la sémantique du modèle relationnel par des approches différentes.

Le modèle B-rel [BV92] reformule les concepts du modèle B-relationnel [LeT86] en
préservant le caractère centré valeur du concept de relation tout en étendant le concept
de domaine vers le concept de classe d'objets.

Le modèle relationnel V2 [Cod90] étend le modèle relationnel en introduisant un opé-
rateur de jointure récursive et en offrant la possibilité d'utiliser des fonctions (prédéfinies
ou définies par l'utilisateur) lors de la manipulation des données.

UniSQL [Kim92], le nouveau SGBD de Won Kim, offre pour sa part un modèle qui réunit les caractéristiques des modèles NF2 et des modèles orientés objet. Développé à la suite du SGBD orienté objet Orion (cf. §1.1.4), ce SGBD relationnel étendu bénéficie ainsi de l'expérience acquise par ses auteurs.

1.1.2 Les modèles de données sémantiques

Dans la plupart des modèles de données traditionnels comme les modèles relationnel,
hiérarchique et réseau, l'information est représentée sous la forme de n-uplets de valeurs
atomiques, comparables d'une certaine manière à de simples enregistrements dans un
fichier. Les structures de données proposées à l'utilisateur y sont très proches de celles
utilisées dans la représentation physique. Comme la sémantique des relations qui lient les
données n'est pas explicitement précisée, toute description de connaissances se traduit
invariablement par la perte de la signification des relations entre les données.

Les modèles de données sémantiques furent développés pour offrir un plus haut niveau d'abstraction dans la représentation des données et des relations qui les lient entre elles [HK87b]. Ils permettent aux concepteurs de BD de structurer les données de façon beau-coup plus naturelle.

Tout en ayant chacun leurs particularités, ces modèles présentent un ensemble de ca-
ractéristiques communes. Ils offrent une représentation explicite des objets, des attributs
(fonctions) et des relations entre objets, des constructeurs de types, en particulier les
constructeurs IS-A pour définir des graphes de types, enfin, des mécanismes permettant
de dériver de nouvelles informations dans un schéma donné.

Un *type abstrait* est décrit comme un ensemble d'attributs. Ses instances sont des objets
et possèdent de ce fait un identificateur interne. Ainsi, deux objets différents peuvent avoir
même valeur. On peut définir des sous-types à un type abstrait au moyen des constructeurs
IS-A (relation de *généralisation* du sous-type par le type père). Les objets instances de sous-
types héritent des attributs définis pour les types ancêtres. Dans de nombreux modèles,
un sous-type peut hériter de plusieurs types pères (héritage multiple).

Il existe deux autres constructeurs de types importants qui sont l'agrégation et le grou-
pement. Le premier permet de définir un nouveau type qui est un agrégat<fnref n="1" /> d'attributs, le
second, un ensemble d'éléments de même type. L'agrégat est moins puissant que le type

<footnote n="1">¹Produit scalaire</footnote>
abstrait : on ne peut pas lui définir de sous-type et ses occurrences ne sont pas des objets, car ils n'ont pas d'identificateur interne. Une occurrence d'agrégat n'est identifiable que par sa valeur. Ainsi, deux objets différents peuvent avoir même valeur, pas deux occurrences d'agrégat.

La figure 1.2 présente un exemple de schéma tiré de [HK87b]. Il s'agit du schéma d'une base de voyageurs internationaux décrit dans un modèle sémantique générique spécifié par l'auteur. Ce schéma va nous permettre d'illustrer rapidement l'ensemble des concepts des modèles sémantiques qu'on vient d'évoquer.

<figure><img src="image_2.png" /><figcaption>Figure 1.2 : Exemple de schéma décrit dans un modèle sémantique générique</figcaption></figure>

Le schéma comprend deux types abstraits **PERSON** et **BUSINESS**, représentés par des triangles. **PERSON**, par exemple, compte quatre attributs **HAS-NAME**, **LIVES-AT**, **SPEAKS** et **GOES-TO**. **PERSON** a trois sous-types **LINGUIST**, **TOURIST** et **BUSINESS-TRAVELER**, représentés par des cercles. Les relations **IS-A** qui les lient à **PERSON** sont représentées par des flèches doubles.

Il existe deux catégories de sous-types dans les modèles sémantiques, les sous-types dont l'instanciation est spécifiée par l'utilisateur, et les sous-types dérivés. **TOURIST** et **BUSINESS-TRAVELER** sont de la première catégorie car une personne ne peut être l'un et/ou l'autre que si l'utilisateur de la base l'a explicitement précisé. Par contre, **LINGUIST** est un sous-type dérivé car toute personne parlant au moins deux langues en est automatiquement instance.
Le type ADDRESS de la base est un agrégat de trois attributs STREET, CITY et ZIP<fnref n="2" />. Les types LANGAGES<fnref n="3" />, DESTINATIONS et ACTIVITIES sont des groupements. Dans les schémas, les agrégats sont représentés par des nœuds × (⊗) et les groupements par des nœuds ⋆ (⊗). Le type ACTIVITIES n'est pas détaillé comme les deux autres. On précise à la place que l'attribut ENJOYS est multi-valué en doublant la tête de la flêche. Les deux représentations sont équivalentes.

Enfin, le schéma comprend un attribut dérivé LANG-COUNT. Cet attribut dérive de l'information (un nombre de langues parlées) à partir de l'attribut SPEAKS défini pour PERSON qui retourne l'ensemble des LANGUAGE parlés par la personne correspondante.

Les modèles de données sémantiques ont trois qualités principales :

* La première est d'accroître la séparation entre les niveaux conceptuel et physique. Illustrons ce point en comparant deux requêtes effectuant le même travail, la première consultant notre base de voyageurs, la seconde une BD relationnelle contenant les "mêmes" données. Nous souhaitons connaître la ville dans laquelle réside l'employeur de Mary. Nous l'obtenons dans une BD sémantique par :

```
print LOCATED-AT(WORKS-FOR('Mary')).CITY
```

Si notre base relationnelle comprend les deux tables BUSINESS(BNAME, STREET, CITY, ZIP) et EMPLOYEE(ENAME, EMPLOYER, OCCUPATION) qui contiennent les informations respectivement sur les entreprises et les employés de ces entreprises, la requête peut être :

```
SELECT CITY
FROM BUSINESS
WHERE BNAME IN
    (SELECT EMPLOYER
FROM EMPLOYEE
WHERE ENAME = 'Mary'
)
```

Dans le premier cas, il n'est nullement fait allusion au niveau logique de la base; seuls les attributs (fonctions) sont utilisés pour retrouver l'information. Dans le second, il est nécessaire de "naviguer" dans la base en précisant les tables impliquées et les jointures à utiliser pour passer de l'une à l'autre.

* La seconde qualité de ces modèles est qu'ils permettent de diminuer fortement la surcharge sémantique des différents types de relation. Tout type de relation peut y avoir une représentation distincte, ce qui n'est bien sûr pas le cas dans le modèle relationnel.

<footnote n="2">²Notation : ADDRESS = STREET × CITY × ZIP</footnote>

<footnote n="3">³Notation : LANGAGES = LANGUAGE*</footnote>
* La troisième est de fournir des outils d'abstraction performants pour manipuler les schémas. On peut par exemple, lors de l'édition d'un schéma, préciser le niveau de détail auquel on souhaite le visualiser. Grâce à la forte modularité que ces modèles apportent, on peut aussi très aisément isoler dans un schéma les informations concernant un type donné. Enfin, les mécanismes de dérivation peuvent aider à comprendre un schéma et à mieux l'exploiter en en extrayant encore de l'information qui y était latente, implicite. Ces mécanismes permettent de sélectionner des données, d'effectuer des calculs dessus pour mettre à jour la base ou retourner un résultat.

Trois modèles sémantiques sont particulièrement connus :

* le Modèle Entité-Relation (ER);

* le Modèle de Données Fonctionnel (FDM, Fonctional Data Model);

* le Modèle de Données Sémantique (SDM, Semantic Data Model).

Le modèle Entité-Relation (ER), proposé par Chen en 1976 [Che76], est considéré comme l'un des premiers modèles de données sémantiques, même si le terme “sémantique” n'était pas encore utilisé à ce moment-là. Il permet de modéliser le monde sous forme d'entités et de relations entre entités, les deux pouvant être représentés par un ensemble d'attributs mono-valués. Initialement, ce modèle n'avait pas de constructeur IS-A, mais des mécanismes ont été proposés depuis pour les lui ajouter [BLN86, TYF86].

Le Modèle de Données Fonctionnel (FDM) a été introduit en 1976 par Kerschberg et Pacheco [KP76]. Il a été le premier modèle sémantique à s'appuyer sur des relations fonctionnelles, c'est-à-dire les attributs. Il est simple et élégant. Il donne aux schémas une représentation graphique facilement compréhensible en faisant ressortir les types atomiques et les attributs au lieu des constructeurs de type, notamment l'agrégation et la généralisation. L'un des avantages principaux de ce modèle est de permettre l'utilisation directe des fonctions lors de la manipulation des données. Il fut le premier à être doté de mécanismes de dérivation [Shi81].

Le Modèle de Données Sémantique (SDM), présenté par Hammer et McLeod [HM81], a été l'un des premiers à tirer pleinement parti du constructeur “groupement” et des mécanismes de dérivation. Ces derniers ont par exemple été utilisés pour permettre d'avoir plusieurs perspectives, plusieurs points de vue sur de mêmes données sous-jacentes. SDM est particulièrement réputé pour ses multiples primitives de spécification d'attributs et sous-types dérivés. Il a par exemple quatre catégories de constructeurs IS-A :

* ceux définis par attributs,

* ceux définis par opérations sur d'autres types (union, intersection, ...),

* ceux définis comme domaine d'attribut,

* enfin, ceux spécifiés (ou contrôlés) par l'utilisateur.
Plus récent que ER et FDM, il est plus riche que ses deux prédécesseurs. Il est aussi plus complexe et donc plus difficile à utiliser.

De nombreux autres modèles de données sémantiques existent.

SAM* (Semantic Association Model) [Su83, Su86] et IRIS [DKL85] sont deux autres modèles sémantiques hautement structurés, possédant l'ensemble des caractéristiques décrites dans notre exemple. SAM* est spécialisé dans les formes spéciales de l'agrégation. IRIS s'est particularisé en proposant à la fois constructeurs de types et attributs. Le système IRIS a par la suite évolué pour adopter un modèle orienté-objet [FBC+87].

Le modèle structurel (SM, Structural Model) [WEM79], RM/T, le développement conceptuel par Codd de son modèle relationnel [Cod79] et GEM [Zan83, TZ84] sont des modèles sémantiques définis au-dessus du modèle relationnel. Les systèmes correspondants ont été implémentés comme des couches au-dessus d'un SGBDR.

TAXIS [MBG+87], GALILEO [ACO85], SHM+ (Extended Semantic Hierarchy Model) [BR84] et INSYDE [KM85] sont des modèles sémantiques axés sur la gestion dynamique des schémas. Dans ces modèles, les schémas peuvent évoluer en cours de transaction (évolution partagée).

Enfin, Format [HY84] et LDM (Logical Data Model) [KV84, Kup85] sont des modèles qui ont été développés à des fins de recherches théoriques.

### 1.1.3 Les modèles de données hyper-sémantiques

Les modèles de données hyper-sémantiques [PTE89] constituent un nouveau progrès par rapport aux modèles sémantiques pour représenter de façon pertinente les situations du monde réel. Cette nouvelle classe de modèles étend leurs capacités en leur permettant de gérer des connaissances à l'aide de concepts issus de la recherche en intelligence artificielle, comme l'inférence, le flou, ou les contraintes.

KDM (Knowledge/Data Model) de Potter et Kerschberg [PK86] est un exemple de cette nouvelle classe de modèles. KDM permet de capturer de façon homogène la sémantique des données et celles des connaissances. Données et connaissances y sont modélisées de manière unifiée et le formalisme permettant d'accéder aux unes et aux autres est également identique. Il n'y a pas de séparation données-connaissances.

Cette unité de représentation est rendue possible en dotant le modèle de trois nouveaux constructeurs de type qui s'ajoutent aux constructeurs classiques généralisation (relation "est un", IS-A), classification (relation "est instance de"), agrégation ("est une partie de"), groupement ("est membre de"). Ils sont :

* contrainte. Ce constructeur permet de placer une restriction sur un aspect d'un objet, une opération ou une relation avec d'autres objets/données. La relation qui lui est associée est "est une contrainte sur".

Exemple : chaque niveau d'étude doit comprendre un certain nombre de matières, chacune devant être dispensée pendant un nombre d'heures donné;

* heuristique. Ce constructeur permet d'attacher un mécanisme de dérivation d'infor-
mations à des objets/données par la relation "est une heuristique sur".

Exemple : Les cours devant être suivis par un étudiant pour une période donnée sont déterminés en fonction du programme d'étude correspondant au niveau de l'étudiant, des cours qu'il a déjà suivis, et des cours qui seront donnés dans cette période.

* *temporel.* Ce constructeur permet de lier des types objet (types abstraits ou sous-types de type abstrait) au travers de relations synchrones ou asynchrones.

Exemple : La modélisation d'un niveau d'étude doit prendre en compte le fait que les cours doivent être terminés pour que les examens puissent être passés, et que ces examens sont suivis d'un stage, puis d'un travail de rédaction de rapport sur le stage.

Pour finir, signalons que le paradigme représentationnel <Attribut, Objet, Valeur> est utilisé en KDM pour représenter les données, les méta-données, les connaissances et les méta-connaissances dans le modèle. Un avantage de cette représentation unifiée est que ces quatre types d'information sont directement accessibles et contrôlables par l'utilisateur. Sa finalité est de permettre la conception et le développement de *Systèmes de Bases de Données Experts* (EDBS).

## 1.1.4 Les modèles de données orientés-objet

Ces dernières années, les chercheurs en BD se sont particulièrement intéressés à intégrer les aspects comportemental et dynamique des données dans les formalismes de modélisation. Ce choix a été énormément influencé par le paradigme Orienté-Objet des langages de programmation. Il s'est traduit par l'apparition de *modèles de données orientés-objet*.

Il n'y a pas encore de définition universellement reconnue précisant ce qu'est un modèle orienté-objet. Cependant, un certain nombre de règles d'or ont été proposées par un groupe de chercheurs SGBDOO de renom [ABD+89] et doivent être satisfaites pour qu'un SGBD puisse prétendre au label "Orienté-Objet". Ces règles se scindent en deux groupes, l'un précisant ce qu'est un système orienté-objet (les huit premières), l'autre, ce qu'est un SGBD (les cinq dernières) :

* *Le système doit supporter les objets complexes.*

Un objet complexe est un ensemble structuré de données, formé à partir d'objets plus simples à l'aide de *constructeurs*. Les objets les plus simples sont les entiers, les réels, les chaînes de caractères et autres données atomiques classiques. Les constructeurs sont nombreux, mais trois sont essentiels : l'ensemble (ou *Set*, le groupement des modèles sémantiques), la *liste* (ou *list*, l'ensemble ordonné) et le *n-uplet* (ou *tuple*, l'agrégation des modèles sémantiques). Les constructeurs doivent être orthogonaux entre eux, c'est-à-dire que tout constructeur doit pouvoir être appliqué à tout objet dans n'importe quel ordre.

* *Chaque objet doit avoir une identité propre.*

Comme dans les modèles sémantiques, chaque objet doit être identifié par une référence interne unique. Cette règle permet l'égalité de valeur de deux objets différents.
Elle autorise aussi le partage d'objets et évite de ce fait toute duplication de données.
La mise à jour des données s'en trouve simplifiée et plus efficace.

* *Les objets doivent être encapsulés.*

La manipulation des objets et l'accès aux données qu'ils contiennent doivent être indépendants de leur représentation interne. De ce fait, ces opérations ne doivent être réalisées que par l'intermédiaire d'une interface constituée d'un ensemble de fonctions appelées **méthodes**. L'invocation d'une méthode sur un objet s'effectue par un *envoi de message*. Cet envoi de message précise l'objet concerné, le message c'est-à-dire le nom de la méthode, plus des paramètres éventuels.

* *Le système doit gérer la notion de classe ou de type.*

La classe et le type sont différenciées par leurs rôles dans le système. La fonction du type est essentiellement de vérifier la correction d'un programme à la compilation alors que celle de la classe est plutôt d'être un outil de création et de manipulation d'objets, "une usine et un entrepôt à objets". Dans certains systèmes, la différence entre les deux est très subtile.

Classe et type caractérisent un groupe d'objets ayant les mêmes propriétés; ils se décrivent par ces propriétés qui forment une structure et par des méthodes dont certaines sont les accesseurs aux propriétés des objets.

* *Ces classes (ou types) doivent hériter de leurs ancêtres.*

Il doit être possible de définir une sous-classe à une autre classe précédemment définie. Cette sous-classe est une spécialisation de sa classe mère. Outre ses propres propriétés et méthodes, elle hérite en plus des propriétés et des méthodes de ses classes ancêtres<fnref n="4" />.

* *L'appel de méthode doit pouvoir n'être résolu qu'à l'exécution.*

Des méthodes de même nom peuvent être définies pour des classes différentes, y compris des classes d'une même hiérarchie. De ce fait, quand une fonction qui manipule des objets dont la classe n'est pas connue fait appel à une telle méthode, le système doit déterminer à l'exécution celle qui est concernée. Pour un objet donné, la méthode qui va être appliquée est celle définie pour sa classe si elle existe, sinon celle de sa<fnref n="5" /> classe mère, et ainsi de suite.

* *Le système doit être calculatoirement complet,*

c'est-à-dire permettre l'utilisation de n'importe quelle fonction calculable<fnref n="6" /> lors de la manipulation des objets. La complétude est obtenue en offrant une connexion raisonnable avec les langages de programmation existants. De toute façon, l'interface de beaucoup de systèmes consiste en un jeu de primitives qui s'ajoutent à celles d'un tel langage.

<footnote n="4">⁴Classe mère, classe mère de classe mère et ainsi de suite.</footnote>

<footnote n="5">⁵En héritage simple.</footnote>

<footnote n="6">⁶Au sens mathématique.</footnote>
* *Le système doit être extensible.*

Nous avons vu qu'il devait être possible de définir de nouveaux types qui s'ajoutent à ceux qui sont prédéfinis. Il ne doit y avoir aucune différence dans l'usage des types système et des types définis par l'utilisateur. Cette règle doit aussi être vraie pour les opérations sur les types.

* *Le système doit offrir la persistance aux données.*

Par définition, on qualifie de *persistante* une information qui continue d'exister après l'exécution d'un programme et qui peut être réutilisée lors de nouvelles exécutions [ABC+83]. La persistance doit être orthogonale au modèle, c'est-à-dire qu'un objet doit pouvoir devenir persistant sans qu'il soit nécessaire de le copier explicitement dans la base.

* *Le système doit pouvoir gérer de larges bases de données.*

La gestion du stockage secondaire est un trait classique des SGBD. Il est en général géré au moyen d'un certain nombre de mécanismes qui sont la gestion d'index, le regroupement de données, le transfert de données en mémoire, la sélection de chemins d'accès et l'optimisation de requête. Ces mécanismes doivent être totalement cachés pour l'utilisateur, de façon à assurer l'indépendance entre les niveaux logique et physique.

* *Le système doit permettre les accès concurrents.*

* *Il doit gérer les pannes.*

En cas de panne matérielle ou logicielle, le système doit être capable de restaurer la base dans un état cohérent.

* *Enfin, le système doit fournir des fonctionnalités permettant d'interroger la base.*

Les huit premières règles définissent la base de tout modèle objet. La plupart des modèles objet se différencient les uns des autres parce qu'ils sont proposés au travers de langages de programmation différents et/ou parce qu'ils offrent des fonctionnalités supplémentaires par rapport au modèle minimal.

Par exemple, l'héritage peut être multiple, le modèle objet peut permettre la pose de contraintes sur les classes ou les propriétés, etc. Dans de nombreux modèles objet, les classes et les propriétés sont elles-mêmes des objets instances de leurs propres classes<fnref n="7" />. Ainsi, il est souvent possible d'enrichir de tels modèles par de nouvelles fonctionnalités en définissant de nouvelles propriétés ou de nouvelles sous-classes aux méta-classes et aux classes de propriétés. Dans les modèles objet modernes, tout est objet.

Un exemple de modèle objet –celui géré par *DRIVER*– est décrit en §3.1.2 et des exemples d'objets et de hiérarchies de classes sont présentés figures 3.3 et 3.4.

Les cinq dernières règles définissent un SGBD. Beaucoup de SGBD Orienté-Objet proposent un certain nombre de caractéristiques supplémentaires qui les rendent encore plus attrayants. Elles sont principalement la vérification et l'inférence de type, la distribution,

<footnote n="7">⁷Une classe de classe est une méta-classe.</footnote>
des systèmes de transaction évolués et la gestion des versions d'objet.

On pourrait penser que les modèles objet ont une approche peu différente de celle des modèles sémantiques. C'est vrai en ce sens qu'ils fournissent tous deux des mécanismes pour construire des structures de données complexes avec relations entre objets. C'est inexact si on considère l'aspect comportemental des données. Dans les modèles objet, les classes comprennent les méthodes qui permettent d'effectuer des calculs complexes sur les données ou de définir le comportement des objets (comme graphiquement à l'écran, par exemple). Les méthodes ont un pouvoir d'expression incomparable par rapport aux attributs dérivés des modèles sémantiques.

Enfin, si les modèles de données sémantiques proposent des structures de données complexes particulièrement adaptées aux applications commerciales, ces structures sont insuffisantes pour les domaines d'application nouveaux où les types complexes tels les types multi-médias (texte, image, son) ou les types utilisateur très gourmands en occupation disque, sont requis [HK87a, SKL88]. Les nouvelles applications sont généralement interactives et requièrent des SGBD hautement dynamiques. Leurs utilisateurs ont besoin de contrôler le comportement des objets et de modifier dynamiquement les classes en leur rajoutant par exemple de nouvelles propriétés ou de nouvelles méthodes.

De très nombreux SGBD orienté-objet sont développés, même si peu ont pour l'instant atteint la phase de commercialisation. Ceux qui sont présentés ci-après ont été choisis parce qu'ils ont été l'objet ou à l'origine de très nombreuses publications.

GEMSTONE [CM84, MS86, MS87] a été le premier SGBD objet commercialisé et est de ce fait très populaire. Il est une extension du langage de programmation Smalltalk, duquel il adopte d'ailleurs le modèle objet. Il offre ainsi la persistance à ses objets et l'enrichit d'un langage de requêtes nommé OPAL qui permet un accès associatif des données.

ORION [KBB+87b, KBB+87a, BCG+87, BKKK87, KBC+88, Kim90] est un prototype de SGBD objet développé aux États-Unis. Il est implanté en Common Lisp et ses primitives s'ajoutent donc aux fonctions de ce langage. Les méthodes de classes doivent aussi être exprimées en LISP.

Il propose une hiérarchie de classes prédéfinies dont la racine est la classe *Object* (figure 1.3). *Object* a trois sous-classes qui sont *PrimitivType*, *Class* et *Collection*. *PrimitivType* est la classe mère de tous les types atomiques de base de ORION, à savoir entier, réel, chaîne de caractères, booléen, etc... *Class* est une méta-classe dont les méthodes permettent de créer et manipuler de nouvelles classes. Ses instances sont donc des objets classe. *Collection* est la classe des groupes<fnref n="8" /> d'objets. Elle a pour sous-classe *Set*, classe des ensembles<fnref n="9" /> d'objets. Les classes que définit l'utilisateur, *Employee*, *Salesman* et *Manager* dans la figure, ont pour ancêtre prédéfini *Object*. ORION génère automatiquement les classes ensemble sous-classes de *Set* qui leur correspondent et qui sont ainsi disponibles pour la définition des propriétés. L'héritage multiple est autorisé.

ORION est particulièrement efficace dans la gestion dynamique des schémas. Il per-

<footnote n="8">⁸Ensembles où un objet peut apparaître plusieurs fois.</footnote>

<footnote n="9">⁹Au sens mathématique du terme.</footnote>
<figure><img src="image_3.png" /><figcaption>Figure 1.3 : Hiérarchie de classes sous ORION</figcaption></figure>

met à tout moment les mises à jour de schéma qui sont aussitôt visibles des utilisateurs concurrents de la base. Ces mises-à-jour peuvent affecter les classes, les propriétés, les méthodes et le graphe d'héritage.

Ce SGBD est également réputé pour sa gestion efficace des objets, des versions d'objet, des transactions et des données multi-média.

O2 [BDK92] est un SGBD orienté-objet conçu et réalisé en France à l'INRIA et aujourd'hui commercialisé par la société O2 Technology. Il est implanté en C et C++. Ses caractéristiques générales, notamment son modèle objet, vérifient assez fidèlement les règles énoncées précédemment. O2 gère aussi l'héritage multiple, la vérification et l'inférence de type, la distribution, les transactions longues et les transactions emboîtées, les versions d'objet et les versions de BD. Par contre, il ne propose pas de gestion dynamique de schéma. Implanté en C et C++, il s'est aussi ouvert à d'autres langages comme Basic et Lisp (BasicO2 et LispO2).

ONTOS [Ont91] est un SGBD objet écrit en C++. Il est une réécriture du SGBD objet VBASE [Atw85, Ont86, AH87, Ont88] initialement écrit en C, dont la commercialisation avait été abandonnée suite à son manque de succès. Il s'intègre à C++ (v2.x) dont il rend persistant le modèle objet. Un des points forts d'ONTOS est son auto-descriptivité : le méta-schéma, le schéma et les données ont une représentation objet uniforme et l'interface d'accès à tous ces objets est également la même. Le fait de permettre à l'utilisateur du SGBD de manipuler les objets du schéma, par exemple les classes, rend possible une gestion dynamique de schéma très puissante. ONTOS gère également l'héritage multiple, la distribution, les transactions longues et les transactions emboîtées. Apparemment, il ne gère pas les versions d'objets.

IRIS [FBC+87] est un SGBD qui était initialement basé sur le modèle de données fonctionnel (cf. §1.1.2) et qui s'est ensuite doté des caractéristiques d'un modèle orienté-objet. Il vérifie toutes les règles énoncées définissant un SGBDOO tout en continuant
de bénéficier de son passé comme modèle sémantique. Une propriété est une fonction, une classe est un ensemble de fonctions, etc... En fait, le schéma apparaît comme un ensemble de types abstraits. La méta-base étant intégrée dans le graphe des types, la gestion dynamique des schémas est autorisée. IRIS gère aussi l'héritage multiple, l'intégrité référenciel et le contrôle de cardinalité, les versions d'objets. IRIS est écrit en C.

MATISSE [MAT92] est le SGBD objet successeur de G-BASE [Gra86a, Gra86b]. Comme lui, il ne vérifie que partiellement les règles précédemment énoncées. Ainsi, le seul constructeur d'objets existant est la *liste*. Le constructeur *ensemble* n'est pas supporté, ce qui est très contraignant. D'autre part, la persistance n'est pas orthogonale au modèle objet. Il n'est pas possible dans un programme de travailler sur des objets non persistants de même structure que les objets persistants. Ainsi, apporter la persistance à un objet qui ne l'est pas ne peut se faire que par recopie. Une qualité essentielle de MATISSE est sa gestion dynamique de schéma, rendue possible par le fait que la méta-base est décrite dans le modèle objet du SGBD. Une autre, originale, réside dans le fait qu'il est possible de poser toute sorte de contraintes, par exemple sur les attributs de classe, sur les liens entre objets (cardinalité,...). Leur expression peut être librement fixée par l'utilisateur.

De très nombreux autres SGBD orienté-objet ont été ou sont réalisés. Parmi eux, on peut citer *OM* [Mis84], *POMS* [CAC+84], *CPOMS* [BC85], *EXODUS* [CDRS86, CDF+86, CDV88], *Trellis/Owl* [SCB+86, OBS86], *PROBE* [MD86, Man87, DDGO87], *ObServer* [HZ87], *Polymnia* [But87], *VISION* [Car87], *MONADS* [RK87, Ros90], *MUSHROOM* [WWH87, WW90], *Mneme* [EMS88, ME89], *ODE* [AG89a, AG89b], *GEODE* [Puc89, PT89], *Cricket* [SZ90], *PAM* [KSD+90], *Objectivity* [LG91, MW91], *ObjectStore* [LLOW70], *NICE-C++* [MM91].

On peut préciser que comme Ontos, ObjectStore, Objectivity et NICE-C++ sont des C++ persistants.

## 1.2 Ajout de capacités de raisonnement aux SGBDR

De nombreux travaux ont pour objet de pourvoir le modèle relationnel de capacités de raisonnement. Leur base commune consiste à interpréter une base de données relationnelle comme une *théorie du premier ordre* [Nic79, GMN84, Rei84]. Dans une telle interprétation, les n-uplets d'une relation sont vus comme des *axiomes*, c'est-à-dire des formules logiques dont on pose la valeur de vérité à "vrai" (n-uplet = "proposition" vraie).

En fait, ces axiomes sont plus précisément des *formules atomiques instanciées*, car ils sont constitués d'un unique prédicat (ayant le nom d'une relation de la base !) n-aire, appliqué à un ensemble d'arguments ne comportant pas de variable libre.

On peut donc enrichir la théorie en lui ajoutant des axiomes plus complexes, par exemple des formules formées de phrases de prédicats pouvant comprendre en argument des variables libres. Ces axiomes rendent possible l'expression de *connaissances*, et par
voie de conséquence, la déduction de nouveaux faits appelés théorèmes. Par hypothèse, tout fait non démontrable est faux (négation par échec).

Ces axiomes peuvent être présentées sous forme de clauses en éliminant les quantificateurs des formules. En général (cf. plus loin), les clauses autorisées par les systèmes sont les clauses de Horn, de la forme $P1 \wedge P2 \wedge P3 \wedge \dots \wedge Pn \to Q$. Ce sont des clauses formées d'une disjonction de prédicats avec un unique prédicat positif ($Q$) et 0 à $N$ prédicats négatifs :

$$ \{P1 \wedge P2 \wedge P3 \wedge \dots \wedge Pn \to Q\} \Leftrightarrow \{Q \vee \neg P1 \vee \neg P2 \vee \dots \vee \neg Pn\} $$

Les clauses de Horn sont aussi appelées règles logiques. Par convention et pour en améliorer la lisibilité, les règles sont présentées "dans l'autre sens", la virgule symbolisant le connecteur "et" à la place de $\wedge$ :

$Q \leftarrow P1, P2, \dots, Pn \quad \text{se lit} \quad \text{"Q si } P1, P2, \dots, Pn\text{"}$.

$Q$ est la tête de règle et $P1, P2, \dots, Pn$ le corps.

Dans la base de données, $Q$ est interprété comme une relation dérivée, encore appelée relation déduite, définie au moyen des $P_i$, relations de la base ou autres relations dérivées. Pour éviter les cycles lors du calcul des relations dérivées, on interdit dans toute règle que $Q$ corresponde à une relation de la base. Par définition, l'ensemble des relations dérivées forment la base en compréhension (intentional database) tandis que l'ensemble des faits contenus dans les relations implantées constituent la base en extension (extensional database).

Prenons un exemple. Considérons les relations **parent**, **homme** et **femme** suivantes formant la base **lien_de_parenté** :

<table><thead><tr><th colspan="2">Table parent</th></tr><tr><th>nom ETH</th><th>nom enfant</th></tr></thead><tbody><tr><td>pierre</td><td>paul</td></tr><tr><td>sophie</td><td>nicole</td></tr><tr><td>nadine</td><td>sophie</td></tr><tr><td>pierre</td><td>nicole</td></tr></tbody></table>

<table><thead><tr><th colspan="2">Table homme</th></tr><tr><th>nom</th><th>nom enfant</th></tr></thead><tbody><tr><td>pierre</td><td>paul</td></tr></tbody></table>

<table><thead><tr><th colspan="2">Table femme</th></tr><tr><th>nom</th><th>nom enfant</th></tr></thead><tbody><tr><td>nadine</td><td>nicole</td></tr><tr><td>sophie</td><td>nicole</td></tr></tbody></table>

Selon l'interprétation précédente, on peut voir dans la base **lien_de_parenté** la base en extension :

* parent(pierre, paul)      homme(pierre)
* parent(sophie, nicole)    homme(paul)
* parent(nadine, sophie)    femme(nadine)
* parent(pierre, nicole)    femme(sophie)
* parent(pierre, nicole)    femme(nicole)
où *parent*, *homme* et *femme* sont des prédicats et où chacune de leurs instanciations précédentes est considérée comme un axiome. Par convention, les termes constants commencent par une minuscule alors que les variables commencent par une majuscule.

Comme on l'a vu, on peut enrichir cet ensemble d'axiomes de règles logiques telles qu'on les a définies. Un exemple de base en compréhension peut être :

*pere(X, Y) ← parent(X, Y), homme(X)*
*mere(X, Y) ← parent(X, Y), femme(X)*
*fils(X, Y) ← parent(Y, X), homme(X)*
*fille(X, Y) ← parent(Y, X), femme(X)*
*grandpere(X, Y) ← pere(X, Z), parent(Z, Y)*
*grandmere(X, Y) ← mere(X, Z), parent(Z, Y)*
*ancetre(X, Y) ← ancetre(X, Z), parent(Z, Y)*
famille(X, Y) ← ancetre(Z, X), ancetre(Z, Y)*

où *pere*, *mere*, *fils*, *fille*, *grandpere*, *grandmere*, *ancetre* et *famille* sont des relations dérivées.

On appelle *langage de règles* le langage permettant de définir les relations dérivées de la base en compréhension. Les langages de règles développés jusqu'à présent ont, dans leur grande majorité, adopté la syntaxe “à la Prolog” présentée ci-avant.

Un intérêt important de cette syntaxe est qu'elle permet d'exprimer aussi les requêtes : une requête est un axiome comportant des variables libres que l'on précède d'un point d'interrogation “?”. Le système va alors déterminer, à l'aide de son démonstrateur de théorèmes, toutes les instanciations possibles des variables libres qui rendent vrai l'axiome correspondant à la question. Adressée à la base précédente, la question “De qui Nicole est-elle la fille ?” s'exprime ainsi :

*?fille(nicole, X)*
La réponse en est :
*X = sophie*
*X = pierre*

La référence en matière de langage de règles de SGBD est *Datalog* [Ulm86, CGT89]. Datalog peut être vu comme une variation de Prolog avec une sémantique ensembliste [GV90]. Cette sémantique rend le résultat d'un programme indépendant de l'ordre des clauses. Le langage est donc complètement déclaratif.

En dépit de ses propriétés formelles, *Datalog* est assez limité. En particulier, il n'a pas la puissance du calcul relationnel, même si, contrairement à lui, il gère la récursivité. La relation *ancetre* de la base en compréhension précédente en est un exemple. Datalog permet l'union, la projection et la jointure, mais pas la restriction et la différence.
Des extensions ont rendu ce langage plus attractif pour une utilisation réelle. Cependant, elles introduisent chacune leurs lots de restrictions d'usage, en particulier parce qu'elles rendent possible la déclaration de programmes dont la sémantique est ambiguë.

* La première a été de permettre l'utilisation de prédicats prédéfinis dans le corps des règles. Ces prédicats sont identifiés par des symboles spéciaux comme `<`, `≤`, `>`, `geq`, `=`, `≠`, et sont généralement utilisés en notation infixe. Ils permettent la restriction relationnelle. La règle suivante est un exemple d'utilisation d'un tel prédicat :
  `plus_vieux(X, Y) ← age(X, A1), age(Y, A2), A1 > A2.`

* La seconde extension a été de permettre l'utilisation de la négation dans le corps des règles. En effet, en *Datalog*, le signe de négation `¬` n'est pas autorisé à apparaître. La négation permet la différence relationnelle et en particulier la gestion des exceptions. Un exemple d'utilisation de la négation peut être :
  `non_cadre(X) ← employe(X, Emploi), ¬employe(X, president), ¬employe(X, directeur).`

* La troisième termine de donner à *Datalog* la puissance du calcul relationnel. Elle consiste à autoriser l'utilisation des fonctions arithmétiques et mathématiques classiques `+`, `-`, `/`, `*`, `LOG`, `EXP`, `SIN`, ... comme arguments de prédicats dans les règles. Un exemple d'utilisation de fonction arithmétique peut être :
  `metro(Depart, Dest, T1 + T2) ← metro(Depart, X, T1), metro(X, Dest, T2).`

De nombreuses autres extensions de *Datalog* ont été proposées, permettant par exemple la mise-à-jour des faits par les règles ou l'usage de clauses plus générales que les clauses de Horn.

Deux d'entre elles sont particulièrement remarquables car elles permettent au SGBD relationnel sous-jacent de notamment supporter les modèles $NF^2$ présentés en §1.1.1. La première autorise la définition d'attributs de type relation, la seconde, d'attributs multivalués :

* La définition d'attributs de type relation est rendue possible en étendant la notion de fonction et en en permettant la création par l'utilisateur.

En première approche, le prédicat et la fonction se ressemblent car ils se caractérisent tous deux par un symbole (leur nom) et un ensemble d'arguments. Cependant, leurs rôles sont distincts : le prédicat est un terme de formule alors que la fonction est un argument de prédicat ou d'autres appels fonctionnels; la valeur d'un prédicat est booléenne alors que celle d'une fonction peut être quelconque.

L'intérêt de la fonction définie par l'utilisateur est de rendre possible la structuration sous forme d'objets complexes des arguments de prédicat. En ce sens, on peut la comparer aux constructeurs **agrégation** des modèles sémantiques et **n-uplet** des modèles objet (cf. §1.1.2 et 1.1.4).

* La définition d'attributs multivalués est, quant à elle, rendue possible en définissant un constructeur d'ensemble appelé *groupement* et noté `<>`. Ce constructeur permet
d'unifier une variable avec un ensemble de valeurs vérifiant une formule donnée. Par exemple, dans la règle suivante, la variable *Employes* est unifiée avec l'ensemble des noms des employés rattachés au département :

$$ \text{membre}(\text{Dept}, \text{Employes}) \leftarrow \text{employe}(\text{Nom}, \text{Dept}), \text{Employes} = \langle \text{Nom} \rangle . $$

Ce constructeur **groupement** est tout-à-fait comparable aux constructeurs **groupe-ment** des modèles sémantiques et **ensemble** des modèles objet.

Il est proposé complété d'un certain nombre de prédicats (*member*(*Element*, *Set*), *union*(*Set*, *Set1*, *Set2*), ...) et fonctions (*COUNT*, *MIN*, *MAX*, *AVG*) prédéfinis utiles pour la gestion des ensembles. De plus, les prédicats prédéfinis usuels (=, ≠, <, ≤, ...) ont été élargis aux ensembles : par exemple, le comparateur < appliqué à deux ensembles est un test d'inclusion stricte.

L'exemple suivant, tiré de [CGT89], a été réalisé en *LDL* (Logic Data Language) [CGK+90]. Il présente un ensemble de faits complexes comprenant des fonctions et des ensembles :

*person(name(joe, berger), birthday(1956, june, 30), children({max, sarah, jim})).*

*person(name(joe, coker), birthday(1956, june, 30), children({bill, sarah})).*

*person(name(bebe, suong), birthday(1958, may, 5), children({jim, max, sarah})).*

Ici, *name*, *birthday* et *children* sont des symboles de fonctions. Les variables peuvent représenter des objets atomiques, comme des constantes, ou des objets composés. La règle suivante sélectionne les noms des personnes ayant même prénom et même date de naissance :

$$ \text{similar}(X, Y) \leftarrow \text{person}(\text{name}(Z, X), B, C), \text{person}(\text{name}(Z, Y), B, D). $$

Par cette règle, on peut déduire le nouveau fait *similar(berger, coker)*.

La règle suivante définit un prédicat *eqchilds(X, Y)* sélectionnant les noms (complets) des personnes dont les enfants ont exactement les mêmes prénoms :

$$ \text{eqchilds}(X, Y) \leftarrow \text{person}(X, B, C), \text{person}(Y, D, C). $$

Par cette règle, on peut déduire le nouveau fait

$$ \text{eqchilds(name(joe, berger), name(bebe, suong))}. $$

On constate ici que l'ordre des éléments d'ensemble n'intervient pas dans la comparaison.

Les ensembles peuvent être définis par énumération, comme dans la déclaration des faits précédents, ou à l'aide du constructeur **groupement** <>. La règle suivante construit l'ensemble des personnes ayant un enfant s'appelant Sarah :

$$ \text{sarahpar}(\langle X \rangle) \leftarrow \text{person}(\text{name}(A, X), B, C), \text{member}(\text{sarah}, C). $$

Cette règle génère le nouveau fait *sarahpar({berger, coker, suong})*.

De très nombreux prototypes de recherche sont actuellement développés à partir de *Datalog* et de ses variations. La plupart sont réalisés en couplant un langage de programmation logique, *PROLOG* en général, avec un SGBD relationnel. Trois types de couplage peuvent être identifiés [GV90] :
* Le *couplage faible*. Cette première approche consiste à bâtir une interface d'appel au SGBD depuis le langage. L'utilisateur écrit ses programmes dans le langage et invoque explicitement le SGBD en utilisant des prédicats dédiés apportés par l'interface. En général, ces prédicats sont *select*, *insert*, *delete* et *update*, et provoquent l'envoi au SGBD de requêtes correspondantes.

* Le *couplage fort*. Cette fois, l'interprèteur de règles (PROLOG) est modifié de façon à rendre invisible le SGBD. L'utilisateur voit les n-uplets relationnels contenus dans la base comme des faits de son programme.

* L'intégration. Enfin, ici, les mécanismes de déduction sont intégrés au SGBD relationnel dont le noyau se trouve enrichi d'un gestionnaire de règles, d'un évaluateur de règles et d'opérateurs spécialisés réalisant efficacement certaines opérations coûteuses comme la récursion.

PROSQL [CW84] est une illustration de couplage faible. Ici, l'interface n'ajoute que le seul prédicat unaire SQL à Prolog. L'argument est une chaîne de caractères devant être une requête SQL de sélection, d'insertion ou de mise-à-jour. La requête peut comprendre des variables Prolog, instanciées ou non au moment de l'appel, selon qu'elles constituent des paramètres ou permettent de recueillir un résultat.

ALGRES [CCRG+88, CCRZ+90], EPSILON [Kou85, CFK+86, Mol87], NAIL! (Not Another Implementation of Logic!) [MUG86, MNS+87], un prototype réalisé par le CERT [CD88], un autre par Philips [DRV86] sont autant d'exemples de couplage fort.

POSTGRES [RS87, SK91], SABRINA* [Kie89] sont des exemples d'intégration de capacités déductives à un SGBDR, le premier à Ingres, et le second à Sabrina. Les deux systèmes sont en fait développés par les équipes à l'origine des deux SGBDR qui disposent donc de leurs sources. Ils offrent tous deux le support d'objets complexes, spécifiés sous forme de types abstraits (ADT pour Abstract Data Types) définis par l'utilisateur. Ce sont donc des systèmes extensibles. Tous deux proposent également un gestionnaire de règles; celui de SABRINA* s'appelle RDL1 . Les règles sont des règles de production de type OPS-5 [BFKM85], de la forme "si conditions alors actions". La partie conditions est une expression du calcul relationnel et la partie actions, un série d'insertions et de suppressions de faits dans des relations déduites. Ces règles sont en effet orientées mise-à-jour; elles peuvent également être utilisées pour exprimer des déclencheurs (triggers en anglais) qui s'exécutent quand leurs conditions d'activation sont réalisées.

Certains systèmes correspondent à plusieurs catégories à la fois. C'est le cas d'un prototype développé à l'ECRC qui est en fait composé de trois sous-systèmes EDUCE, DEDGIN et PROLOG/KB [BDN+86]. EDUCE réalise un couplage faible entre PROLOG et le SGBDR Ingres. DEDGIN est construit au-dessus d'EDUCE et réalise le couplage fort. PROLOG/KB est également construit au dessus d'EDUCE. Il assure le stockage des règles dans une base de données et offre un modèle sémantique à l'utilisateur, du type Entité-Relation plus constructeurs IS-A.
Le projet "Calculateur de cinquième génération" FGCS de l'ICOT (Institute for New Generation Computer Technology) [MKM+83, Fuc84, Ito86] a pour but de réaliser une "machine bases de données". Cette machine est en fait formée d'une machine matérielle et d'une machine logicielle (système d'exploitation). La première machine matérielle développée fut DELTA, maintenant remplacée par KAPPA. Toutes deux ont été exploitées avec PROLOG PSI, puis avec PROLOG PIM qui est une version parallèle de PSI. Dans ce projet, les couplages fort et intégration sont utilisés.

LDL (Logic Data Language) [TZ86, NT89, CGK+90], est un autre SGBD déductif proposant un langage qui étend Datalog, notamment par la gestion de la négation et des ensembles [Bee87], et des termes complexes [Zan85]. LDL est original notamment par le fait qu'il n'est pas construit au-dessus d'un SGBDR mais d'une machine bases de données parallèle BUBBA [Bor88].

## 1.3 Couplage d'un environnement IA avec un SGBD

Enfin, un certain nombre de travaux en IA visent à doter les systèmes raisonneurs de grandes bases de connaissances en les couplant à des SGBD. En général, un "traducteur" entre le modèle de données du système raisonneur et celui du SGBD est proposé de façon à permettre les transferts.

C'est la solution que nous avons retenu. La description du logiciel DRIVER [Leb90a, Leb90b, Leb92a, Leb92b] qui est la concrétisation de ce travail est l'objet de la présente thèse.

À notre connaissance, trois autres couplages ont été réalisés.

Le premier est celui qui a été réalisé entre Proteus [Rus89] et Orion [Kim90]. Orion est le SGBD orienté objet dont nous avons déjà parlé. Proteus est un système de maintien de la vérité (TMS) dans lequel les données ont une représentation à base de frames. Dans ce couplage, l'une des difficultés les plus significatives à surmonter a été la non-gestion par Orion du concept de méta-classe requis par Proteus. Mis à part ce point, les deux modèles de données offrent des similarités importantes et sont donc compatibles.

En fait, dans l'approche choisie, Proteus n'est pas différent d'une autre application construite sur Orion. Proteus exploite Orion par appels de fonctions tandis qu'Orion n'appelle jamais Proteus. Orion fournit simplement un support persistant aux instances de frames de Proteus qui seraient volatiles autrement. Le moteur de la base reste séparé du moteur d'inférence et le gestionnaire de connaissance ne maintient la connaissance que dans son domaine, c'est-à-dire en mémoire : la connaissance est donc bien séparée des données. Orion est simplement utilisé comme un stockage d'objets passif attendant les accès de Proteus.

Un couplage a également été réalisé entre KEE [FK85] et les SGBD relationnels. Le système qui réalise ce couplage est connu sous le nom de KEEconnection [KEE87]. Nous ne ferons pas de description détaillée de KEEconnection car le principe du couplage est
identique à celui qui a été retenu en DRIVER, à savoir basé sur la notion de schéma de correspondances.

Ajoutons seulement qu’à la différence de DRIVER,

* le modèle objet géré ne peut être choisi, c’est nécessairement celui de KEE. En fait, KEEconnection a été réalisé pour KEE et ne peut être utilisé sans lui.

* L’accès à un objet ne peut être réalisé qu’en le chargeant.

* L’absence de valeur dans un champ de type objet n’est pas autorisée.

* Quand un objet dans la base en référence d'autres par l'intermédiaire de champs objet, son chargement provoque nécessairement le chargement de ceux-là, et récursivement. Ainsi, si le réseau d'objets est suffisamment étoffé, toute la base peut être chargée en mémoire.

* Seuls les champs de type atomique et objet (simple) sont gérés. Il n’est pas possible de “ mapping” des champs de type “ensemble”, “ensemble ordonné”, ou encore “calculé”.

Enfin, un troisième type de couplage a été réalisé entre les systèmes à base de connaissance de type réseau sémantique et les SGBD orientés objet par le logiciel PDKB (Persistent Data/Knowledge Base) [HL90]. Afin de prendre en compte la représentation des connaissances des réseaux sémantiques, PDKB propose un ensemble d'axiomes formalisés sous forme d'expressions "à la Prolog" (logique du premier ordre) qui étendent les capacités de modélisation des SGBDOO. Ces abstractions formalisées incluent la généralisation/spécialisation (IS-A), l'agrégation, la composition, l'association et le groupement.

Ces travaux sont à rapprocher de ceux effectués sur Datalog, les modèles de données sémantiques (SDM) et les modèles entité-relation étendus. Mais la différence fondamentale entre PDKB et ces modèles est que PDKB propose les principes d'organisation sémantiques précédents et les axiomes de déduction formalisés dans le même environnement. PDKB pallie ainsi à l'incapacité de Datalog de représenter des situations du monde réel par abstractions sémantiques, et à celle des SDM d'offrir une représentation formelle du modèle de données.

## 1.4 Conclusion

Au cours de ce rapide tour d'horizon, nous avons pu constater qu'il existait de nombreuses façons de gérer de la connaissance dans une base de données et de raisonner dessus. Cependant, malgré cette diversité, les systèmes qui les implantent utilisent tous une architecture globale qui se range dans l'une des deux catégories suivantes [Sto90] :
* Dans la première, les données sont stockées dans le SGBD, et la connaissance, dans le système expert. Les données pertinentes selon la situation sont chargées à la demande dans l'espace de travail.

Cette architecture est celle de l'exploitation de la plupart des systèmes que nous avons vus. C'est celle de beaucoup d'interfaces entre PROLOG et les SGBDR, c'est aussi celle de DRIVER et des applications utilisant les SGBD orientés objet.

Son principal inconvénient provient du fait que les SGBD actuels sont incapables de prévenir l'interface que des données chargées en mémoire sont par ailleurs modifiées dans la base par un accès concurrent. Dans cette alternative, la gestion dynamique des données est donc un vrai problème.

* Dans la seconde, les données ET la connaissance sont stockées dans le SGBD.

Il existe, comme nous l'avons vu, un certain nombre de prototypes ayant intégré un système de règles au sein d'un SGBD. Cependant, ces systèmes de règles sont primitifs par rapport à ceux existant en IA. Il n'y a pas de contrôle de l'inférence; il est impossible d'explorer des arbres de preuves et de revenir en arrière dans un raisonnement. En fait, les critiques de l' existant sont nombreuses et fondées. Mais, dans le futur, des bases de connaissances plus complexes seront certainement gérées de cette façon.

En dépit de ses inconvénients et de performances parfois médiocres, le premier type d'architecture semble donc être actuellement la meilleure solution pour le développement d'applications complexes ayant besoin de gérer de larges bases de connaissances.
# Chapitre 2

## DRIVER, COUCHE OBJET
VIRTUELLE PERSISTANTE
### 2.1 Caractéristiques générales

Notre souci initial était de permettre au Générateur de Systèmes Experts SMECI [Sme92] d'accéder à des bases de données relationnelles et de les exploiter dans le cadre du raisonnement.

Nombreuses sont en effet les applications SMECI souhaitant pouvoir orienter leur raisonnement par la consultation de larges bases de données. La possibilité d'interroger une base permet par exemple de connaître les matériaux produits dans une région pour y minimiser le coût d'une construction, de consulter les normes techniques pour que cette construction soit conforme aux règles de l'art, de déterminer les fondations en fonction du site, de sélectionner sur catalogue les poutres d'acier utilisables selon le dimensionnement effectué par le système... On constate que les utilisations en conception sont innombrables. Elles le sont également dans d'autres domaines quand la base de données est utilisée comme support pour le partage d'informations. Un système expert spéculateur peut par exemple tirer profit de la consultation en temps réel des fluctuations boursières pour gérer des porte-feuilles de façon optimale. Un pilote automatique peut moduler l'allure et le cap d'un navire en fonction du trafic et de la météo tenus à jour dans une base de données commune, etc...

Une autre de nos préoccupations était d'apporter la persistance aux objets manipulés par SMECI qui présentent un intérêt pour une utilisation ultérieure, en particulier l'ensemble de ses bases de connaissances et le produit de ses analyses.

Il peut en effet être utile et très efficace de pouvoir retrouver les solutions proposées à des problèmes posés lors de sessions précédentes. Les applications raisonnant d'abord par analogie sur les cas déjà rencontrés avant d'éventuellement poursuivre en utilisant des codes de calcul plus complexes et plus coûteux en temps sont des exemples de systèmes demandeurs de persistance.

Enfin, bien que SMECI ait été à l'origine de nos travaux sur les bases de données, il nous a semblé important que l'outil qui allait nous permettre de gérer de grandes bases de connaissances persistantes en soit complètement indépendant de façon à pouvoir être ensuite utilisé de façon autonome.

#### 2.1.1 Un pont entre deux mondes

Nos efforts se sont concrétisés au travers du système DRIVER [Leb90c, Leb90a, Leb90b] dont l'une des caractéristiques principales est de définir une couche objet pour les bases de données relationnelles.
DRIVER permet de présenter les données relationnelles sous forme d'objets complexes et les rend directement accessibles pour exploitation dans l'environnement objet du client<fnref n="1" />. C'est d'autre part ce même client qui précise le modèle objet dans lequel les données relationnelles sont exprimées. De cette façon, les objets sont particulièrement adaptés à l'usage qu'il souhaite en faire et peuvent être manipulés dans l'environnement de la même façon que ceux qui ne sont pas issus d'une base de données. Tous peuvent s'intégrer harmonieusement dans la même couche objet.

Par ailleurs, DRIVER permet également de rendre persistants dans une base de données relationnelle des objets de l'environnement qui ne représentent pas déjà des données d'une telle base.

Dans le premier cas, le système associe une représentation objet à des structures relationnelles. À l'inverse, dans le second, il associe une représentation relationnelle à des structures objet. À chaque fois sont utilisées de semblables correspondances mettant en rapport les deux représentations, correspondances en fait indépendantes du sens dans lequel s'effectue la transaction. Elles constituent le *schéma de correspondances* (figure 2.1) qui permet la traduction et le transfert des données d'un monde dans l'autre lors des échanges gérés par DRIVER entre la base et l'environnement objet.

<figure><img src="image_4.png" /><figcaption>Figure 2.1 : Un schéma de correspondances entre deux représentations</figcaption></figure>

Un schéma de correspondances contient trois catégories d'informations : celles relatives aux deux représentations de données, plus celles qui assurent leur rapprochement.

Sa construction peut être effectuée de différentes manières. En général, l'une des deux représentations existe déjà. Reste donc à définir l'autre et à indiquer les liens qui les unissent.

La solution la plus simple peut consister à générer automatiquement les éléments

<footnote n="1">¹Environnement Le_Lisp pour l'instant puisque notre prototype a été développé dans ce langage.</footnote>
manquants du schéma. **DRIVER** dispose de telles fonctionnalités qui permettent la prise en compte et l'exploitation immédiates d'une table relationnelle dans l'environnement et l'attribution tout aussi rapide de la persistance à une classe [Leb92b].

Cette possibilité peut n'être considérée que comme une solution à court terme. Elle est en effet assez brutale. Les éléments générés mécaniquement ne peuvent être que stéréotypés, ils manquent en plus de lisibilité. Il est en général préférable de décrire plus finement ses correspondances, notamment pour choisir une représentation objet pour des données relationnelles. Une des raisons principales est que seul l'utilisateur connaît la sémantique de certains attributs, en particulier ceux qui constituent des références vers d'autres tables. Le système n'a aucun moyen de la déterminer. Par souci de clarté, le terme "attribut" fera toujours référence dans notre propos à une colonne de table relationnelle tandis que le terme "champ" se rapportera à la propriété d'une classe d'objets.

Le schéma de correspondances peut également être construit à l'aide d'un éditeur graphique prévu à cet effet [BM92] (cf. §A.3). Le néophyte est alors pris par la main et guidé pas à pas. À chaque étape, les choix proposés tiennent compte de ce qui a déjà été fait, rendant impossible l'introduction d'incohérences ou d'incompatibilités.

L'utilisateur avancé préfèrera directement décrire son schéma à partir de l'interface fonctionnelle [Leb92b]. C'est la méthode la plus précise et la plus efficace. Un contrôle très strict de chaque déclaration est effectué, assurant ainsi la validité du schéma à chaque étape de sa construction. L'éditeur graphique dont on a précédemment parlé repose en réalité sur l'interface fonctionnelle. C'est une puissante aide qui permet de construire simplement un schéma sans connaître le nom des primitives et leurs arguments.

### 2.1.2 Une couche objet virtuelle persistante

Les données relationnelles dans la base sont interprétées comme des objets potentiels qui résultent des associations définies dans le schéma de correspondances. Ces objets potentiels sont appelés les *objets relationnels*. **DRIVER** permet l'accès immédiat aux objets relationnels depuis l'environnement. Cet accès peut par exemple être réalisé en construisant en mémoire les images des objets relationnels sélectionnés. Ces images sont appelées les *objets jumeaux* (cf. §2.3.4.2). Cependant, si le client le souhaite, l'accès peut être directement effectué dans la base de données sans construction dans l'environnement des jumeaux correspondants.

Du fait du couplage qu'il réalise avec les SGBDR, **DRIVER** élargit l'ensemble des objets directement accessibles et manipulables dans l'environnement en adjoignant aux objets spécifiquement de l'environnement les objets représentants de données relationnelles. L'ensemble de ces objets constituent la *couche objet virtuelle de DRIVER*. Comme le système fournit en outre toutes les fonctionnalités nécessaires pour administrer la persistance et l'attribuer à tel ou tel objet ou à telle ou telle classe, on qualifie également cette couche objet virtuelle de persistante (figure 2.2).

DRIVER donne accès aux objets relationnels par une fonction qui permet de les filtrer
<figure><img src="image_5.png" /><figcaption>Figure 2.2 : La couche objet virtuelle</figcaption></figure>

(cf. §4.4). Ainsi, il est possible de sélectionner dans la base des objets relationnels vérifiant certains critères et de les exploiter ensuite au sein de la couche objet, par exemple dans le cadre du raisonnement d'un système expert.

Le filtre est un prédicat à *n* variables où chacune d'entre elles représente une instance d'une classe précisée, décrite dans le schéma de correspondances. Le corps du prédicat est un ensemble de contraintes liant généralement ces *n* variables et pouvant être des comparaisons entre objets, entre champs, entre champs et valeurs. Les substitutions solutions sont donc des *n*-uplets d'objets relationnels. Les objets filtrés ne sont pas construits dans l'environnement. Sont retournées à la place les références des objets relationnels concernés qui permettent de construire ensuite des "objets-liens" vers ces objets relationnels.

Ces "objets-liens" vers la base de données sont appelés des défauts d'objet. Ils rendent dans l'environnement la manipulation d'un objet relationnel qui a été filtré strictement identique à celle d'un simple objet de même classe en mémoire. Toutes les méthodes associées à sa classe sont également valables pour les deux catégories d'objets si bien qu'un utilisateur final peut complètement ignorer, lorsqu'il manipule un objet, si ce dernier est une représentation de données relationnelles ou un simple objet de l'environnement sans rapport avec la base. Les défauts d'objet sont plus précisément décrits en §2.3.3 et en §4.1.1.2.

Au besoin, par exemple pour consulter ou mettre à jour la valeur d'un champ, il est possible, à partir du défaut d'objet, de construire dans l'environnement l'objet jumeau de l'objet relationnel correspondant.

On a vu que la couche objet pouvait comprendre des classes définies pour représenter des structures relationnelles. Elle comprend également celles à qui on a associé une représentation relationnelle pour pouvoir écrire leurs instances dans la base. Par souci de simplification, on qualifiera de *persistantes* l'ensemble de ces classes qui ont pour point commun d'être décrites dans un schéma de correspondances. Une classe persistante peut offrir la persistance à chacune de ses instances. Les informations propres à la classe sont elles-mêmes persistantes grâce au *méta-schéma* (cf. §3.3.2).

Les classes persistantes s'intègrent complètement aux hiérarchies de classes définies
<figure><img src="image_6.png" /><figcaption>Figure 2.3 : Exemple de hiérarchie mêlant classes persistantes ou non</figcaption></figure>

dans l'environnement (figure 2.3). Toute classe peut posséder une ou plusieurs sous-classes persistantes.

Une classe qui possède au moins une classe ancêtre persistante a des instances qui peuvent prétendre à la persistance. En effet, les instances d'une classe sont a fortiori instances des classes ancêtres. La classe hérite de toute qualité qui n'est pas redéfinie, notamment de la persistance. Malgré tout, seuls les champs hérités et décrits dans le schéma sont persistants. Les autres, en particulier les champs locaux, ne le sont pas.

Quand on définit une sous-classe persistante à une classe non persistante, les champs hérités ne sont généralement pas persistants, car sans correspondance relationnelle. Heu-reusement, il est possible de contourner cet obstacle en décrivant leurs correspondances dans le schéma de façon délocalisée, au niveau de la sous-classe (cf. §3.1.3.2).

### 2.1.3 Des représentations optimales

Il existe nombre de manières de représenter sous forme d'objets un ensemble de données relationnelles, comme il existe nombre de manières d'exprimer sous forme relationnelle un ensemble de classes.

La façon pertinente d'exprimer dans le monde objet une information contenue dans un attribut dépend de la sémantique qu'on lui prête et de l'utilisation qu'on va en faire.

Ainsi, pour telle application, tel programmeur donne comme correspondance à notre attribut un champ atomique (cf. §3.2.3) qui permet l'accès à des données brutes, par exemple des entiers. Dans une autre ou avec un autre programmeur, le même attribut peut servir à définir une jointure<fnref n="2" /> qui est la correspondance d'un champ de type "objet".

<footnote n="2">²Au sens SGBD.</footnote>
Un champ de type "objet" ou "ensemble d'objets" (lien entre objets) a en effet pour correspondance naturelle une jointure qui est un lien entre deux tables. Autre exemple : lorsque cette jointure est définie entre deux tables principales (correspondantes de deux classes) et est, par exemple, l'expression d'une relation 1:N, on a le choix entre lui associer un champ de type "objet" dans l'une ou définir un champ de type "ensemble d'objets" dans la seconde. On peut même lui associer les deux, moyennant certaines restrictions d'usage.

Le choix existe également pour affecter une représentation relationnelle à un ensemble de classes. Une classe et ses sous-classes sont associées à une table appelée *table principale* (cf. §3.2.2) et la plupart des correspondances des champs sont définies sur cette table. Cependant, il est possible de choisir la correspondance d'un champ sur une autre qui devient de ce fait *table secondaire* de la hiérarchie de classes précédente (cf. §3.2.3.1).

Par ailleurs, une jointure, définie pour accéder à une information, un ensemble d'informations, à un objet ou à un ensemble d'objets, peut être définie de multiples façons possibles.

Ces exemples donnent un aperçu de l'ensemble des choix qui s'offrent au concepteur d'un schéma de correspondances qui tente de coupler une représentation à l'autre. On peut en outre faire la remarque que le concepteur n'est nullement tenu, lorsqu'il définit un schéma, d'y décrire la totalité des structures auxquelles il a accès, que ce soit dans une représentation ou dans l'autre.

Quand son but est d'attribuer la persistance à un ensemble de classes, nul n'est besoin de décrire les autres. Il lui est également inutile de définir au sein d'une classe à rendre persistante, la correspondance de champs dont l'écriture dans la base de données ne présente pour lui aucun intérêt.

Quand il souhaite associer une représentation objet à des données relationnelles qu'il a besoin d'exploiter, il est également libre de ne décrire de la base que les structures qui l'intéressent et, bien sûr, de ne définir que leurs correspondances.

La structure des classes qui en résultent et la forme sous laquelle les données choisies s'expriment dans les objets sont ainsi adaptées aux besoins réels de l'utilisateur ou du système raisonneur.

Si un schéma semble optimal pour résoudre un certain problème, une autre formulation de ce même problème peut faire apparaître d'autres façons de le résoudre et laisser émerger un autre schéma possible. Une information à gérer dans la représentation objet peut y être exprimée de multiples façons et dans des champs de types différents. C'est la manière par laquelle cette information va être exploitée qui doit guider dans le choix de sa représentation. Cette flexibilité de la représentation objet associée aux données relationnelles permet de l'adapter et de la faire évoluer en fonction des besoins qui changent.
### 2.1.4 Exploitation de plusieurs bases

Il est possible de tirer profit de l'accès à plusieurs bases de données en définissant pour chacune d'entre elles un schéma de correspondances. Chaque schéma décrit alors comment chaque base est exploitée.

Lorsque plusieurs schémas ont été définis dans le même environnement, chacun d'entre eux y explicite un certain nombre de classes persistantes qui s'intègrent dans la hiérarchie des classes.

Une exploitation avancée de ces possibilités de connexions multiples est ébauchée en §2.4.

## 2.2 Gestion de la persistance

### 2.2.1 Granularité de persistance

Avec *DRIVER*, une information contenue dans un objet est dite persistante si elle a été déclarée persistante (!) et s'il existe un schéma de correspondances dans lequel est décrite sa correspondance relationnelle.

Le seul fait de définir une correspondance ne suffit pas à rendre persistantes les données concernées. En effet, dans un environnement comme celui d'un système expert, l'information évolue beaucoup et il n'est pas toujours souhaitable de conserver l'ensemble des données transposables dans la base grâce à une correspondance. Il vaut mieux avoir la possibilité d'attribuer cette persistance de façon plus précise en sélectionnant explicitement ce qui doit être retenu.

C'est le cas avec *DRIVER*. D'une manière générale, le schéma de correspondances permet de décrire la correspondance relationnelle de classes et de champs. Tout objet instance d'une classe décrite dans un schéma peut prétendre à la persistance. Deux objets appartenant à une même classe peuvent être différenciés parce que l'un possède cette qualité et l'autre non. Il appartient à l'utilisateur de l'accorder à ceux qui lui semblent présenter un intérêt pour une réutilisation ultérieure.

Au sein d'une même classe rendue persistante peuvent être combinés champs persistants ou non. À nouveau, il est possible de sélectionner, cette fois au cœur même des objets persistants, les informations pouvant prétendre à la pérennité. Les champs non persistants sont tout simplement ignorés pendant les écritures ou lectures d'objets dans la base.

Ces champs non persistants ne doivent pas forcément être déclarés dans le schéma. Ils n'y ont leurs places que si le schéma est également mis en œuvre pour définir dans le modèle objet client les classes auxquelles ils appartiennent. Dans ce cas, leur absence dans la description de la classe dans le schéma se traduirait par leur absence dans la définition de la classe dans le modèle objet utilisé. Quand le schéma n'est pas utilisé pour cela, quand, par exemple, les classes préexistent au schéma, nul n'est besoin de déclarer à *DRIVER* les champs qu'on ne désire pas rendre persistants.

Il peut être parfois utile de supprimer momentanément la persistance d'un champ. Cette opération peut facilement être réalisée avec *DRIVER*, par exemple pendant la phase
de mise au point de l'application ou encore pour augmenter localement les performances du système pendant certaines opérations de transfert de données entre l'environnement et la base. Le même type d'opération est également possible avec les classes et se traduit par une gestion encore plus précise de la persistance qui supporte ainsi la notion d'exception. Tel champ d'ordinaire persistant ne l'est plus dans certaines conditions d'exécution de l'application.

Enfin, il peut être parfois utile de décrire, au niveau d'une sous-classe persistante, la correspondance de champs définis dans une classe mère. Ceci est particulièrement vrai quand cette classe mère n'est pas persistante.

Quand, par exemple, le modèle objet utilisé présente une classe racine unique et que cette classe possède un champ *nom*, toutes les classes qui seront ensuite définies hériteront de ce champ *nom*. S'il est souhaitable de rendre l'une de ces classes persistante, il peut être tout aussi souhaitable d'également rendre le champ *nom* persistant sans pour autant devoir attribuer la persistance à la classe racine.

DRIVER permet ainsi de décrire au niveau d'une classe la correspondance de champs non locaux qui seront gérés comme les autres en ce qui concerne la persistance, mais qui seront ignorés lorsque la classe sera définie dans le modèle objet client en utilisant le schéma de correspondances.

## 2.2.2 Persistance des classes et des correspondances

Le schéma de correspondances contient des informations relatives aux classes et aux relations qu'il aide à rapprocher et relatives aux correspondances établies entre ces classes et relations. Ces trois types d'information sont aisément séparables pour effectuer des opérations spécifiques à chacune d'entre elles comme définir des classes dans le modèle objet utilisé ou créer des relations dans la base.

DRIVER assure la sauvegarde des informations structurelles qui lui sont nécessaires en gérant la persistance des schémas de correspondances.

Cette persistance des schémas et de l'ensemble des informations qu'ils contiennent est assurée grâce à un *méta-schéma* (cf. §3.3.1). Ce schéma associe des relations système à l'ensemble des classes système de DRIVER qui participent à la description des schémas de correspondances. Ces relations forment de par leur nature une sorte de méta-base, un *dictionnaire de données complexes*.

Il peut arriver que DRIVER soit instancié avec un modèle objet dont il ne puisse pas gérer l'ensemble des concepts, tel celui de méta-classe. Les informations sur les classes contenues dans un schéma de correspondances sont alors insuffisantes pour prendre en compte l'ensemble des caractéristiques du modèle objet. Apporter la persistance aux schémas de correspondances ne suffit donc pas pour rendre persistantes toutes les informations structurelles du modèle objet client.

Si des caractéristiques particulières de ce modèle objet ne sont pas implicitement gérées par DRIVER et que leur absence ne permet pas de reconstruire correctement l'ensemble
des informations structurelles, il est tout à fait possible de définir un schéma de correspondances en complément du méta-schéma, associant ces traits qui ne sont pas gérés à des tables "système" supplémentaires.

## 2.3 Transactions

Un objet persistant modifié ou créé dans l'environnement n'est pas immédiatement mis à jour dans la base. Cette phase d'écriture et de répercussion dans la base de modifications effectuées en mémoire doit être explicitement provoquée par un ordre de validation de *transaction*.

### 2.3.1 Transaction DRIVER et transaction SGBD

Une *transaction* `DRIVER` est une session propre à un ensemble d'objets persistants pendant laquelle toute modification de l'un d'entre eux n'est effective que dans la couche objet virtuelle à laquelle ils appartiennent. Tous les autres clients de la base, en particulier des données relationnelles correspondant à ces objets, continuent de les "voir" comme si elles n'avaient pas été modifiées. Les mises à jour ne sont visibles pour les autres qu'après validation de la transaction.

La notion de *transaction* existe aussi au niveau du SGBD et implique l'ensemble des données d'une base. C'est une session d'accès à une base, propre à un utilisateur. Elle se valide par exemple en SQL au moyen de la commande `COMMIT`.

Toute requête adressée au SGBD dans le cadre d'une telle transaction verrouille les données concernées et les rend provisoirement inaccessibles pour les autres transactions. Ce verrouillage existe quel que soit le type de la requête, qu'elle soit une sélection, une insertion, une mise à jour ou une suppression de n-uplet. Selon le SGBD sont verrouillées les seules données concernées, les pages<fnref n="3" /> qui les contiennent ou les relations entières. Tout client voulant accéder dans sa propre transaction à des données verrouillées par un autre est mis en attente. Il reste bloqué jusqu'à ce que ces données soient à nouveau disponibles, jusqu'à ce que le "verrouilleur" valide sa transaction.

La transaction `DRIVER` ne se confond pas avec la transaction `SGBD`. Cet attachement aurait pu être préjudiciable pour toutes les applications nécessitant le partage de données. En fait, la validation de transaction `SGBD` est entièrement gérée par `DRIVER` qui l'effectue à bon escient, de façon totalement indépendante de celle de son propre système de transactions qui s'applique aux objets et uniquement sur demande.

Une transaction `DRIVER` implique soit l'ensemble des objets persistants de la couche virtuelle, soit un groupe d'objets qui est précisé à la validation. Sa granularité est d'un objet, c'est-à-dire que la plus petite transaction peut ne concerner qu'un objet (cf. §4.2.1.1). Si cet objet en référence d'autres directement ou indirectement par l'intermédiaire de

<footnote n="3">³La page est une entité de stockage propre aux BD</footnote>
champs de type objet, il est possible d'inclure automatiquement ces objets référencés dans la transaction. Dans ce cas, ils sont également mis à jour dans la base lors de la validation.

Le fait que la transaction DRIVER n'implique pas forcément l'ensemble des objets persistants de la couche virtuelle est intéressant pour des raisons d'efficacité. De cette manière, à chaque phase de mise-à-jour de la base n'est concernée qu'une partie choisie de la couche objet, ceci se traduisant notamment par des gains en performances appréciables. Comme nous l'avons déjà souligné, la transaction SGBD, elle, implique à chaque fois l'ensemble des données contenues dans une base.

Enfin, les validations des deux types de transaction ne coïncident pas et sont effectuées selon des fréquences différentes. En DRIVER, une validation de transaction SGBD est faite<fnref n="4" /> après chaque opération de transfert de données entre la couche objet et la base. Si un objet ou un groupe d'objets est chargé en mémoire, une validation SGBD libère aussitôt les relations verrouillées par la lecture. Si un champ d'objet est directement lu dans la base, cette lecture est immédiatement suivie d'une validation analogue.

L'insertion ou la mise à jour de données dans la base se termine également par une validation SGBD. Toute opération d'écriture dans la base d'un objet ou d'une information contenue dans un objet commence en DRIVER par une lecture dans la base des données correspondantes. Cette lecture, par contre n'est pas immédiatement suivie d'une validation SGBD et se traduit ainsi concrètement par le verrouillage des données consultées. Elles peuvent ensuite être mises à jour sans risque d'intervention intempestive d'un autre client. L'écriture est close par la validation de transaction SGBD qui rend visibles tous les éventuelles modifications de la base.

L'accès ou la modification d'informations contenues dans la base sont ainsi non bloquants pour les autres. Les validations de transaction SGBD qui accompagnent la plupart des accès de DRIVER à la base de données garantissent des temps de verrouillage du SGBD extrêmement courts, ce qui est très utile lors de l'exploitation d'une base partagée.

Si l'application DRIVER est seule à utiliser la base ou si l'on souhaite adopter une politique plus sûre de gestion des accès concurrents, il est possible d'éviter les fréquentes validations SGBD qui sont faites en changeant la valeur d'un l'indicateur système (driver-commit-after-reading). Ceci peut se traduire par une efficacité accrue du système car DRIVER n'effectue plus de validation de transaction SGBD qu'au moment où il valide la transaction DRIVER. Dans ce cas, les transactions SGBD et DRIVER sont confondues.

En conclusion, le mécanisme de transaction au niveau de la couche objet apporte un confort important dans la manipulation des objets persistants. Ceux-ci peuvent ainsi être modifiés, complétés, affinés sans subir les lourdeurs liées à l'écriture en temps réel sur disque.

De façon générale, le mécanisme des transactions permet de protéger l'intégrité et la sûreté des données qui restent cohérentes au sein de la base.

<footnote n="4">⁴Par défaut. Voir plus loin le rôle de l'indicateur système driver-commit-after-reading.</footnote>
### 2.3.2 Maintien de la cohérence

Un certain nombre de modifications effectuées sur des objets peuvent être cohérentes dans leur ensemble et incohérentes si elles ne sont faites que partiellement. De telles modifications sont réalisées de façon sûre et validées conjointement par une même transaction alors qu'une mise-à-jour incrémentale aurait provoqué une incohérence momentanée au sein du SGBD.

Les transactions DRIVER comportent en plus des transactions SGBD un certain nombre de caractéristiques spécifiques qui vont dans le sens d'une plus grande sécurité pour les données contenues dans la base.

Une validation de transaction DRIVER permet de mettre à jour dans la base un groupe d'objets persistants. Les modifications effectuées dans l'environnement sur les objets persistants peuvent ainsi être validées de façon incrémentale, par exemple, après contrôle de leur validité.

Quand la base est partagée, un même environnement peut contenir simultanément des objets persistants dont la fréquence de validation dans la base doit varier de l'un à l'autre. On peut par exemple distinguer les objets qu'on sait uniquement manipulés par notre environnement et qui n'ont pas besoin d'être validés très fréquemment. À l'inverse, il y a ceux qui contiennent des informations partagées entre plusieurs systèmes et qui doivent être validés dès qu'ils sont modifiés. Grâce aux transactions DRIVER, ces différents types d'objets peuvent être gérés de façon optimale en minimisant les accès au SGBD.

En cas d'incohérence dans la couche virtuelle, au sein d'un groupe d'objets persistants précisément identifié, il est possible, en annulant la transaction en cours pour ces objets, de revenir en arrière et de leur redonner une valeur cohérente. Cette valeur est celle de leurs objets relationnels respectifs dans la base. Ils reflettent donc à nouveau très précisément les données relationnelles qu'ils représentent dans l'environnement. Le groupe est ainsi replacé dans un état qui a précédemment été reconnu valide.

Il est intéressant de remarquer que seul ce groupe est revenu à des valeurs antérieures. Le reste de la couche objet reste inchangé, en particulier les autres objets persistants modifiés qui n'ont pas encore été validés dans la base.

### 2.3.3 Défauts d'objet et objets relationnels

DRIVER permet une manipulation identique des objets de l'environnement et des *objets relationnels* au sein de la couche objet virtuelle. Rappelons que les objets relationnels sont les *objets potentiels* contenus dans la base de données qui résultent des correspondances effectuées.

Les objets relationnels sont atteints au moyen d'une primitive qui permet de les filtrer. Cependant, cette primitive ne renvoie pas directement les objets en question, mais leurs références. Ces dernières peuvent être confiées à une autre primitive qui délivre des objets système DRIVER qui “représentent” les objets relationnels correspondants dans l'environnement en l'absence d'objets jumeaux. Ces objets système sont les *défauts d'objet*.
Un défaut d'objet constitue un lien vers l'objet relationnel qu'il représente en mémoire (figure 2.4). Il contient toutes les informations nécessaires pour retrouver dans la base les données relationnelles associées et leur donner une représentation objet. En particulier, il connaît un schéma de correspondances, la classe principale de l'objet représenté et sa valeur de clé qui l'identifie de manière unique dans la base. Par le schéma de correspondances, il accède à la connaissance permettant de faire passer les données d'une représentation dans l'autre et à un canal de communication par lequel le dialogue avec la base est possible.

<figure><img src="image_7.png" /><figcaption>Figure 2.4 : Défaut d'objet et objet relationnel</figcaption></figure>

À la demande et de façon transparente, le défaut d'objet est capable d'interroger la base et d'accéder aux informations souhaitées contenues dans l'objet relationnel qu'il représente.

En fait, il se manipule comme un objet classique de la couche objet. Pour l'utilisateur, l'objet qu'il met en œuvre "est" l'objet relationnel, car le défaut d'objet se comporte comme s'il était celui qu'il représente. Les champs définis pour la classe de l'objet relationnel sont normalement accessibles et peuvent être simplement consultés et mis à jour. Les méthodes sont également applicables. Le défaut d'objet répond correctement aux envois de message qui lui sont adressés et qui sont en réalité destinés à l'objet relationnel.

Le défaut d'objet est petit; il occupe très peu de place en mémoire. À l'opposé, il peut représenter des objets relationnels de taille quelconque qui peuvent donc être a fortiori très gros. Par cette technique du défaut d'objet, DRIVER permet ainsi l'exploitation, depuis l'environnement, de bases de données importantes. La mémoire n'est pas surchargée puisque les données n'ont pas besoin d'être chargées pour être prises en compte. Inversement, il est également possible, de ce fait, de décentraliser au niveau des bases de données certains modules de connaissance, parfois peu utilisés, qui, jusqu'à présent, étaient gérés en mémoire.
### 2.3.4 Accès aux champs et objets jumeaux

Le défaut d'objet permet d'accéder de deux façons différentes aux informations contenues dans l'objet relationnel.

#### 2.3.4.1 L'accès direct

La consultation ou la mise-à-jour d'un champ de l'objet relationnel peut se traduire par un accès à la base où sont directement lues ou écrites les données relationnelles correspondant à la valeur du champ. Chaque accès se traduit par l'envoi d'une requête au SGBD. Après l'opération, le défaut d'objet est toujours un défaut d'objet.

Ce type d'accès est intéressant à plus d'un titre. Il permet de consulter certaines informations contenues dans de gros objets sans alourdir la mémoire plus que ne le réclamerait l'application. Dans le cas d'objets partagés, on accède ainsi à des valeurs qui sont toujours à jour. Des données issues d'autres utilisateurs de la base peuvent ainsi être immédiatement prises en compte.

Il présente cependant certains inconvénients. *N* accès à un objet se traduisent par *N* requêtes envoyées au SGBD, ce qui peut être lourd et manquer d'efficacité quand on travaille beaucoup avec les mêmes objets. C'est particulièrement vrai quand on sait que ces objets en question ne sont pas partagés avec d'autres clients du SGBD et que deux consultations successives d'un même champ ne risquent pas de renvoyer deux résultats différents. Enfin, un inconvénient de taille existe concernant la mise-à-jour. L'accès direct en écriture dans la base enfreint les règles du système des transactions *DRIVER*. Toutes les modifications effectuées ainsi ne peuvent pas être défaites par une annulation de transaction. L'usage de cette fonctionnalité en écriture doit donc être utilisée avec beaucoup de précautions.

#### 2.3.4.2 Les objets jumeaux

L'autre alternative consiste à déclencher la construction d'un *objet jumeau* lors d'une tentative d'accès au champ d'un objet relationnel encore représenté par un défaut d'objet. L'objet jumeau est une image fidèle de l'objet relationnel. Seulement, contrairement à lui, il a l'avantage d'être un objet de l'environnement à part entière, héritant de toutes les propriétés du modèle objet client. À sa construction, il remplace purement et simplement le défaut d'objet si bien que toute connaissance de l'environnement qui référençait le défaut d'objet référence maintenant le jumeau. Une fois construit, le jumeau masque l'objet relationnel associé (figure 2.5). La primitive *DRIVER* d'accès aux objets relationnels renvoie d'ailleurs le jumeau s'il existe, au lieu du défaut d'objet.

Le jumeau est un objet de l'environnement ayant pour classe celle de l'objet relationnel. Quand il est construit, seules les valeurs atomiques et les ensembles sont chargés. Tous les objets relationnels auxquels il fait référence dans l'un de ces champs sont remplacés par des défauts d'objet. Là encore, si certains disposent déjà de leurs jumeaux, ce sont ces derniers qui sont retrouvés et utilisés.
<figure><img src="image_8.png" /><figcaption>Figure 2.5 : Masquage de l’objet relationnel par l’objet jumeau</figcaption></figure>

Il était bien sûr possible de placer et construire directement dans les champs objet les jumeaux des objets relationnels référencés. Mais, pour peu qu’ils référencent à leur tour de nouveaux objets et que le réseau entre objets soit suffisamment étoffé, on aurait vite fait de charger ainsi récursivement toute la base de données en mémoire. L’intérêt de la couche objet virtuelle s’en serait trouvé singulièrement amoindri.

Le principe des jumeaux s'intègre complètement à celui des transactions DRIVER. Quand un objet relationnel subit une tentative de modification, son jumeau est construit. C'est lui qui est mis à jour; les données relationnelles dans la base ne sont pas touchées. À ce moment, le contenu du jumeau ne correspond donc plus à celui de l'objet relationnel, mais cette différence n'est pas perceptible depuis l'environnement puisque le jumeau masque l'objet relationnel dès qu'il est créé. Tout nouvel accès sur l'objet relationnel est maintenant effectué sur le jumeau. Aucune requête n'est plus adressée au SGBD. À la validation de la transaction, toutes les modifications effectuées sur l'objet sont reportées sur l'objet relationnel, sur les données de la base. L'objet relationnel et le jumeau sont à nouveau strictement identiques. À l'inverse, si la transaction est annulée, tous les objets jumeaux sont remplacés par des défauts d'objet. Les modifications effectuées en mémoire sont ainsi perdues. L'objet jumeau disparaissant, le masquage de l'objet relationnel disparaît également.

Les avantages de cette gestion des accès aux objets relationnels sont assez importants. Tout d'abord, la préservation du système des transactions est une chose importante. Par ailleurs, en règle générale, le système gagne aussi en efficacité. Pour charger un objet (ou plusieurs) d'une classe comportant *N* champs de type "ensemble" ou "calcul multi-n-uplets", *DRIVER* effectue 1 + *N* requêtes au SGBD (cf. §4.1.2.1). Les applications comprenant de nombreuses définitions de champs de ces types bénéficient alors de la diminution du nombre de communications avec le SGBD.

Un autre avantage des objets jumeaux concerne les systèmes raisonneurs. Cet avantage est décrit dans la section suivante.

Il est toujours possible d'accéder directement à un objet relationnel dans la base de
données, y compris quand son jumeau a été construit. Le fait que son représentant dans l'environnement soit un défaut d'objet ou un jumeau ne fait pas de différence à ce niveau; les primitives d'accès direct restent disponibles.

Cette possibilité est intéressante à plus d'un titre. On peut ainsi à tout moment sur-
veiller l'évolution d'un paramètre dans une base de données partagée, quel que soit le
représentant de l'objet relationnel dans l'environnement. Quand le jumeau est formé, il
est également possible d'accéder directement à la valeur d'un champ dans la base pour la
comparer avec celle contenue dans le jumeau. Enfin, l'accès direct à la base depuis l'objet
jumeau permet aussi de gérer en mémoire la liste des valeurs successivement prises par
un champ.

Quand l'objet jumeau a été construit, l'accès direct dans la base de données à la valeur d'un champ peut déclencher sur ce jumeau des réflexes de mise-à-jour qui sont inhibés par défaut.

En consultation, si la valeur du champ lue dans la base est différente de celle se trouvant
dans l'objet jumeau, la différence indique que les données relationnelles correspondantes
ont été modifiées par un autre utilisateur de la base depuis la construction du jumeau.
Dans ce cas, DRIVER peut ne pas se contenter de rendre la nouvelle valeur du champ. Il
peut également mettre à jour le champ dans l'objet jumeau.

En écriture, quand on accède directement à la base pour modifier le champ d'un objet relationnel, on peut également mettre à jour le champ correspondant de l'objet jumeau si sa valeur diffère de celle qu'on affecte dans la base.

Ces réflexes présentent un intérêt pour les applications où la base est partagée et où
il est important d'avoir dans l'environnement des objets qui reflètent au maximum l'état
de cette base de données.

Ils présentent cependant certains inconvénients importants qui empêchent de les laisser actifs par défaut. Leur principe est incompatible avec le système de transactions de DRIVER. Il est troublant par exemple qu'un accès en lecture dans la base se traduise automatiquement par l'écriture d'un champ dans l'environnement.

Parfois, des objets sont dépendants les uns des autres du fait de l'existence d'une
relation liant certains de leurs champs. Si la consultation dans la base d'un champ prenant
part à cette relation se traduisait par sa mise-à-jour brutale dans l'environnement, la
cohérence du groupe d'objets pourrait être anéantie.

2.3.4.3 Objets jumeaux et raisonnement

Certains générateurs de systèmes experts comme SMECI disposent d'objets réversibles.
Voyons en quoi les jumeaux présentent un réel intérêt pour ce type de système.

Retraçons d'abord rapidement le principe général de SMECI.

Le processus de conception, sur lequel est basé le raisonnement de SMECI, consiste à
assembler des objets élémentaires qui peuvent être soit entièrement connus à l'avance, soit
connus de manière partielle. Les paramètres inconnus sont alors déterminés par l'expert à
l'aide de règles. A l'aide de la connaissance dont il dispose, SMECI génère en parallèle les différentes solutions compatibles avec les spécifications et contraintes données au départ [Nev86].

Le moteur travaille sur des états qui représentent la solution en cours d'élaboration. Un état est donc caractérisé par les objets du processus de conception, plus ou moins précis selon l'état d'avancement de la solution.

Le moteur développe ainsi un arbre d'états (figure 2.6) où chaque branche correspond à une variante envisagée, où les nœuds le long de chaque branche sont les états de plus en plus précis par lesquels est passé le moteur. Une fois cet arbre complet, la feuille de chaque branche est soit une solution (feuille n°402 de notre exemple), soit un état qui a été reconnu incohérent par une règle de contradiction (feuilles n°s76, 79, etc...), soit un état bloqué sur lequel aucune règle ne peut s'appliquer.

Au cours du raisonnement, lorsque, pendant la construction de l'arbre des états, le moteur reconnaît son état courant comme étant une feuille, il revient en arrière d'état en état jusqu'au premier où il lui reste au moins une variante à explorer. Il choisit la plus prometteuse par rapport à une fonction d'évaluation et la développe à son tour.

Quand on souhaite exploiter des objets relationnels dans le cadre du raisonnement apparaît le problème posé par la réversibilité des objets de l'environnement.

Si l'on supposait l'absence du mécanisme des objets jumeaux se poserait le problème de la non-réversibilité des données relationnelles dans la base. En effet, les SGBD relationnels ne gèrent pas en standard de liste, administrée par datation ou numéro de version, des valeurs successivement prises par un attribut au cours du temps. En cas de retour-arrière de SMECI, apparaîtrait la difficulté de remettre à jour, dans l'état à partir duquel repartirait le raisonnement, les objets relationnels qui auraient été directement modifiés dans la base au cours de l'exploration de la branche abandonnée. Une différence essentielle s'établirait au sein de la couche objet virtuelle entre les objets SMECI présents dans l'environnement qui sont réversibles et les objets relationnels qui sont incapables, en cas de retour-arrière du système, de retrouver leurs valeurs originelles.

Les objets jumeaux évitent cette distinction et conservent à la couche objet son homogénéité. Quand un objet relationnel est consulté par le système expert, DRIVER génère automatiquement le jumeau qui est un objet SMECI à part entière et le lui substitue pour la suite du raisonnement. Le jumeau comme tout objet SMECI est réversible. Il peut évoluer au fil du raisonnement et retrouver ses anciennes valeurs en cas de retour-arrière, sans risquer de perturber la cohérence de la base de données. Si le moteur revient en arrière vers un état antérieur à la création de ce jumeau, le masquage disparaît et c'est l'objet relationnel qui est à nouveau éventuellement unifié dans la poursuite du raisonnement.

## 2.4 Exploitation avancée des connexions multiples

Plusieurs schémas peuvent être définis, associant chacun une représentation objet à des données relationnelles.
<figure><img src="image_9.png" /><figcaption>Figure 2.6 : Exemple d'arbre d'états de SMECI</figcaption></figure>

2.4.1 Vers des objets de persistance distribuée

Aucune cohérence entre schémas n'est gérée par DRIVER. Lors de la construction d'un schéma, aucune vérification de compatibilité n'est effectuée entre les nouvelles déclara- tions et les autres schémas déjà existants.

Il est ainsi possible d'accorder la persistance à une classe dans un schéma en l'associant
à une certaine table, puis de l'accorder dans un second à une sous-classe de la première
en l'associant à une autre table. Cette redéfinition de la correspondance d'une classe au
niveau d'une sous-classe est interdite en DRIVER au sein d'un même schéma (cf. §3.2.2).

Cette absence de contrôle peut être exploitée pour sauver par ce biais un objet en
plusieurs parties, chacune dans une base différente. Dans le cas classe/sous-classe évoqué
ci-dessus, les champs définis au niveau de la mère sont écrits dans une base, ceux qui
sont locaux à la sous-classe sont écrits dans une autre. D'une façon plus générale, cer-
tains champs d'une classe peuvent être décrits dans un schéma, d'autres dans un autre,
etc... Lors de chaque validation de transaction, selon le schéma courant sont écrits dans
la base tels ou tels groupes de champs.

Ces objets relationnels *distribués* ont une identité propre dans chaque base. Chacune de
leurs références est donc constituée d'un ensemble de couples schéma de correspondances
- clé dans la base.

Le défaut d'objet, "objet-lien" vers un objet relationnel, n'est pas tout-à-fait adapté à
référencer un tel objet relationnel distribué dans la version actuelle de DRIVER. En effet,
il ne connaît qu'un seul schéma de correspondances et qu'une clé dans la base associée au
schéma. Cependant, étendre le système serait aisé. L'accès à un champ d'un objet se ferait simplement en recherchant la classe de l'objet et le champ dans les schémas référencés par le défaut d'objet. Une fois le champ retrouvé<fnref n="5" />, le schéma qui le contient déterminerait le canal de communication, la base et la référence de l'objet dans cette base. La suite de l'opération, notamment la construction de la requête resterait la même.

Dans la version actuelle de DRIVER, un défaut d'objet pourrait référencer un objet relationnel distribué à condition, lors de l'accès aux champs, de préciser au défaut d'objet le schéma dans lequel est décrite la correspondance du champ concerné. Un mauvais trait est que, lors du déclenchement de la construction d'un objet jumeau, seuls seraient chargés les champs de l'objet définis dans le schéma référencé par le défaut d'objet.

En l'état actuel des choses, ce genre de manipulation d'objets à persistance distribuée est fortement déconseillé à un utilisateur non avancé.

## 2.4.2 Vers des représentations multiples contextuelles

Du fait de l'indépendance complète des schémas, aucune information issue d'un autre schéma ne peut être exploitée lors de la construction de l'un d'entre eux. Il est par exemple impossible de définir des champs de type objet destinés à recueillir des instances d'une classe déclarée dans un autre schéma. On peut cependant faire la remarque que ce genre d'opération n'aurait de sens que si les deux schémas étaient définis sur la même base. En effet, la correspondance d'un champ objet est une chaîne de jointures (cf. §3.2.3.2). Si les deux schémas étaient définis sur des bases différentes, des requêtes adressées au SGBD effectueraient des jointures entre tables de bases différentes, ce qui serait insensé.

Par contre, plusieurs schémas peuvent être définis pour une même base. Cette perspective peut être intéressante quand il s'agit de prendre en compte des données relationnelles dans l'environnement. On peut en effet définir plusieurs représentations objet pour de mêmes données, chacune étant exprimée dans un schéma différent.

Ces différentes représentations objet co-existent dans le même environnement. Leur intérêt est qu'elles sont toutes pertinentes pour une certaine tâche, dans un certain contexte. Elles permettent d'accéder à de mêmes données de façons multiples et optimales en fonction du contexte. De cette façon, les champs peuvent directement contenir l'information dans la forme la plus adaptée. Le filtrage des objets relationnels correspondants est lui-même plus efficace, les requêtes générées et adressées au SGBD étant optimisées.

Un autre intérêt de ces représentations objet multiples est la possibilité d'avoir simultanément dans l'environnement différents arbres d'héritage établis selon des critères différents et qui représentent de mêmes données. Les différentes classifications des êtres vivants sont des exemples de telles hiérarchies, indépendantes mais basées sur les mêmes données. La classification cladistique regroupe les êtres vivants selon leurs caractéristiques physiques communes tandis que la classification phénétique les regroupe en fonction de leurs similarités génétiques et hématologiques.

<footnote n="5">⁵Ces idées n'ayant pas été implantées, les problèmes de conflit de nom de champ n'ont pas été étudiés.</footnote>
Là encore, les descriptions multiples de bases doivent être utilisées avec prudence. En effet, elles court-circuitent les vérifications effectuées lors de la construction d'un schéma, qui interdisent la représentation redondante d'informations dans l'environnement. Certaines données de la base peuvent par cette méthode se retrouver plusieurs fois en mémoire dans des objets de classes différentes. Si elles sont exploitées en simple consultation, elles ne sont la source d'aucun problème particulier. Si elles peuvent être modifiées et mises à jour dans la base, des incohérences peuvent être introduites.
# Chapitre 3

## LE SCHÉMA DE CORRESPONDANCES
## 3.1 Deux modèles de données différents

Les modèles relationnel et objet présentent, dans leur richesse d'expression, une différence essentielle qu'il est utile de souligner.

Nous allons examiner dans un premier temps le modèle relationnel puis le modèle objet que supporte `DRIVER`; nous verrons ensuite comment on peut les rapprocher afin d'être capable d'exprimer ou de traduire des données dans l'un comme dans l'autre.

### 3.1.1 Le modèle relationnel

Le modèle *relationnel* est dû à E. F. Codd [Cod70]. Les structures de données sont des *relations* (au sens mathématique) qu'on peut représenter par des tables à un instant donné. Les colonnes, caractérisées par un nom et un type simple, sont appelées des *attributs*, et les lignes, enregistrements successifs et occurrences de relations, sont appelées des *n-uplets*. Les types d'attributs sont toujours atomiques; les plus communément disponibles sont *entier*, *réel* et *chaîne de caractères*, mais il en existe d'autres, comme les types *date*, *argent* qui, cette fois, ne sont pas fournis par tous les SGBD.

Toute relation possède au moins une clé constituée d'un ou plusieurs attributs. La valeur d'une clé de n-uplet l'identifie de manière unique dans l'ensemble des n-uplets de la relation considérée.

<table><caption>Figure 3.1 : Structures relationnelles : emp, table des employés</caption><thead><tr><td>Attribut</td><td>type</td><td>longueur</td><td>Description de l'attribut</td></tr></thead><tbody><tr><td>empno</td><td>entier</td><td>2</td><td>Numéro de l'employé, clé de la relation emp</td></tr><tr><td>ename</td><td>caractères</td><td>20</td><td>Nom de l'employé</td></tr><tr><td>fname</td><td>caractères</td><td>20</td><td>Prénom</td></tr><tr><td>job</td><td>caractères</td><td>20</td><td>Emploi dans l'entreprise</td></tr><tr><td>mgr</td><td>entier</td><td>2</td><td>Numéro du chef de l'employé</td></tr><tr><td>sal</td><td>réel</td><td>8</td><td>Salaire</td></tr><tr><td>com</td><td>réel</td><td>8</td><td>Commission, pour certains employés</td></tr><tr><td>deptn</td><td>entier</td><td>2</td><td>Numéro du département de l'employé</td></tr></tbody></table>

La figure 3.1 présente un exemple de structure de table relationnelle, composée de différents attributs. La figure 3.2 montre et permet de comprendre aisément comment les données sont organisées sous forme de tableaux.
<table><caption>Figure 3.2 : Données relationnelles : contenu de la table emp</caption><thead><tr><th>empno</th><th>ename</th><th>fname</th><th>job</th><th>mgr</th><th>sal</th><th>com</th><th>deptn</th></tr></thead><tbody><tr><td>7839</td><td>king</td><td>paul</td><td>president</td><td></td><td>5000.</td><td></td><td>10</td></tr><tr><td>7566</td><td>jones</td><td>eric</td><td>manager</td><td>7839</td><td>2975.</td><td></td><td>20</td></tr><tr><td>7698</td><td>blake</td><td>harold</td><td>manager</td><td>7839</td><td>2850.</td><td></td><td>30</td></tr><tr><td>7782</td><td>clark</td><td>john</td><td>manager</td><td>7839</td><td>2450.</td><td></td><td>10</td></tr><tr><td>7902</td><td>ford</td><td>john</td><td>analyst</td><td>7566</td><td>3000.</td><td></td><td>20</td></tr><tr><td>7788</td><td>scott</td><td>pit</td><td>analyst</td><td>7566</td><td>3000.</td><td></td><td>20</td></tr><tr><td>7499</td><td>allen</td><td>jack</td><td>salesman</td><td>7698</td><td>1600.</td><td>300.</td><td>30</td></tr><tr><td>7521</td><td>ward</td><td>peter</td><td>salesman</td><td>7698</td><td>1250.</td><td>1400.</td><td>30</td></tr><tr><td>7654</td><td>martin</td><td>georges</td><td>salesman</td><td>7698</td><td>1250.</td><td>1400.</td><td>30</td></tr><tr><td>7655</td><td>james</td><td>peter</td><td>salesman</td><td>7698</td><td>1350.</td><td>1000.</td><td>30</td></tr><tr><td>7934</td><td>miller</td><td>paul</td><td>clerk</td><td>7782</td><td>1300.</td><td></td><td>10</td></tr><tr><td>7329</td><td>smith</td><td>john</td><td>clerk</td><td>7902</td><td>800.</td><td></td><td>20</td></tr></tbody></table>

L'intérêt du modèle relationnel provient de ses bases théoriques solides. Il est aussi de proposer des structures de données simples qui se construisent à partir de la théorie des ensembles.

Un certain nombre d'opérateurs s'appliquant aux relations permettent de définir des données, de les mettre à jour et de les rechercher. Ils constituent *l'algèbre relationnelle*. Chaque opérateur s'applique à des relations et produit comme résultat une relation. Ils comprennent les opérateurs classiques de la théorie des ensembles, comme l'*union*, l'*intersection* et la *différence*, plus d'autres permettant de composer des sous-ensembles de relations, comme la *projection*, la *restriction*, la *jointure* et la *division*.

Des langages construits sur l'algèbre relationnelle sont proposés pour définir et manipuler les données (DDL et DML<fnref n="1" />). Basés sur la logique des prédicats, ces langages de requêtes permettent d'exploiter les données relationnelles en se désintéressant complètement de leur implantation. Il existe un standard qui est SQL<fnref n="2" /> [ANSI86, Mir90]. Ce langage a d'ailleurs fortement contribué au succès du modèle relationnel. Un des points forts de SQL est de fournir une interface uniforme aux administrateurs, programmeurs d'applications et utilisateurs finaux pour la définition, le contrôle et la manipulation des données.

<footnote n="1">¹Data Definition Language, Data Manipulation Language</footnote>

<footnote n="2">²Structured Query Language.</footnote>
## 3.1.2 Le modèle objet de DRIVER

### 3.1.2.1 Caractéristiques générales

Le modèle objet proposé par DRIVER est classique en IA.

Un objet, dans le sens objet complexe, désigne un ensemble de données structuré, identifié par une référence unique. L'objet est instance d'une classe, la classe étant la description purement statique d'un ensemble possible d'objets.

Les classes s'organisent en hiérarchies où les racines, c'est-à-dire les classes n'étant pas issues de classes plus générales, sont appelées les *classes principales*. Il n'y a pas nécessairement de classe principale unique. Une classe est définie par un ensemble de champs, qui sont les champs de la classe mère dont elle hérite, plus les champs locaux qui lui sont propres (Figures 3.3 et 3.4).

Dans l'implantation actuelle du système, l'héritage est simple, c'est-à-dire qu'une classe ne peut hériter directement que d'une seule autre classe.

<figure><img src="image_10.png" /><figcaption>Figure 3.3 : Exemples de classes</figcaption></figure>

Un champ, selon son type, est structuré en un certain nombre de facettes précisant la nature de la valeur qu'il contient; par exemple, le nom du champ, son type, la classe de l'objet dans le cas d'un champ de type objet, la définition des éventuelles contraintes,
<table><thead><tr><th colspan="2">Objet de classe Employé</th></tr></thead><tbody><tr><td>nom</td><td>millor</td></tr><tr><td>prénom</td><td>paul</td></tr><tr><td>num-ss</td><td>787309</td></tr><tr><td>qualif</td><td>clerk</td></tr><tr><td>chef</td><td>Object("clark")</td></tr><tr><td>salaire</td><td>(1300.)</td></tr><tr><td>grade</td><td>2</td></tr><tr><td>dpt</td><td>Object("accounting")</td></tr><tr><td>adresse</td><td>Object("miller-addr")</td></tr></tbody></table>

<table><thead><tr><th colspan="2">Objet de classe Vendeur</th></tr></thead><tbody><tr><td>nom</td><td>ward</td></tr><tr><td>prénom</td><td>peter</td></tr><tr><td>num-ss</td><td>673595</td></tr><tr><td>qualif</td><td>salesman</td></tr><tr><td>chef</td><td>Object("blake")</td></tr><tr><td>salaire</td><td>1250.</td></tr><tr><td>grade</td><td>2</td></tr><tr><td>dpt</td><td>Object("sales")</td></tr><tr><td>adresse</td><td>Object("ward-addr")</td></tr><tr><td>commission</td><td>1400.</td></tr><tr><td>sal-total</td><td>2650.</td></tr></tbody></table>

<table><caption>Figure 3.4 : Exemples d'objets</caption><thead><tr><th colspan="2">Objet de classe Président</th></tr></thead><tbody><tr><td>nom</td><td>king</td></tr><tr><td>prénom</td><td>paul</td></tr><tr><td>num-ss</td><td>786259</td></tr><tr><td>qualif</td><td>president</td></tr><tr><td>chef</td><td>()</td></tr><tr><td>salaire</td><td>5000.</td></tr><tr><td>grade</td><td>5</td></tr><tr><td>dpt</td><td>Object("accounting")</td></tr><tr><td>adresse</td><td>Object("king-addr")</td></tr><tr><td>secrétaire</td><td>Object("bush")</td></tr><tr><td>subordonnés</td><td>(Object("jones") Object("blake") Object("clark"))</td></tr></tbody></table>

etc...

Des contraintes de toutes natures (cf. §3.1.3.2) peuvent être posées sur les champs atomiques et sont définies sous forme de prédicats dont la valeur de vérité indique si la contrainte est respectée ou non. L'application de ces prédicats aux valeurs candidates pour le champ permet de déterminer si elles vérifient ou non la contrainte. Ces contraintes peuvent être redéfinies au niveau des sous-classes et permettent de préciser de plus en plus strictement les valeurs autorisées pour chaque champ.

### 3.1.2.2 Persistance des objets

En fait, parler *DU* modèle objet est en réalité un peu inexact puisqu'en fait, *DRIVER* ne définit et n'offre pas réellement un nouveau et n-ième modèle objet. Il ajoute aux modèles existants, en particulier au modèle du client (cf. §3.1.2.3), une nouvelle caractéristique qui leur est complètement orthogonale, à savoir la persistance.

Cependant, cette persistance n'est accordée qu'à un certain nombre de propriétés du
modèle client qui correspondent aux concepts reconnus par `DRIVER`.

Bien entendu, il n'est pas possible de reconnaître l'ensemble des caractéristiques de tous les modèles objet. Un choix a été effectué. Afin de préserver la réflexivité du système et la symétrie dans l'utilisation de `DRIVER` (attribution de la persistance à des classes ou définition de classes destinées à la représentation de tables relationnelles), il n'est exploité des modèles objet que les propriétés qui peuvent avoir un sens vis-à-vis du modèle relationnel et de son rapprochement du monde objet.

Il pourra être possible de gérer d'autres caractéristiques, notamment parmi les données structurelles, mais cela devra être fait explicitement en définissant un schéma de correspondances entre elles et de nouvelles tables "système" à définir.

On parle malgré tout du modèle objet de `DRIVER`, car la description des informations concernant les classes à rendre persistantes ou à associer à des tables relationnelles s'effectue par le biais d'un langage à objets particulier à `DRIVER`, doté d'un certain nombre de caractéristiques, d'un formalisme et d'une syntaxe qui lui sont propres.

### 3.1.2.3 Une interface fonctionnelle objet

`DRIVER` s'utilise simplement avec différents modèles objet. Il effectue toutes les opérations sur les objets au travers d'une interface fonctionnelle qui invoque des méthodes sur des objets modèles. Pour avoir accès à un nouveau modèle, il suffit donc de définir un nouveau jeu de méthodes pour ce modèle.

Les envois de messages sont effectués sur des objets de classe `Driver-Object-model`. Prendre en compte un modèle objet peut consister à redéfinir les méthodes de cette classe. On peut aussi, et c'est préférable, créer des sous-classes à la classe `Driver-Object-model` et redéfinir les méthodes pour chacune d'entre elles (figure 3.5). De cette façon, différents modèles peuvent être utilisés dans un même environnement. Un mécanisme de modèle courant permet de déterminer à tout moment celui à utiliser.

<figure><img src="image_11.png" /><figcaption>Figure 3.5 : Hiérarchies de classes de modèles objet</figcaption></figure>

Pour le moment, `DRIVER` a été implanté en Le_Lisp v15 et, de ce fait, ne permet la prise en compte que des modèles objet disponibles dans cet environnement. Nous verrons cependant qu'aucun trait du système ne dépend spécifiquement de LISP et de son caractère interprété. `DRIVER` pourra par exemple être très rapidement porté en C++ pour
pouvoir être exploité par les utilisateurs de ce langage.

Le modèle objet *MicroCeyx* est disponible dans l'environnement *DRIVER* de base. La classe *MicroCeyx*, sous-classe de *Driver-Object-model*, et son jeu de méthodes associé y ont en effet été définis. MicroCeyx est le modèle objet utilisé par défaut. Les méthodes de la classe *Driver-Object-model* appellent en effet explicitement celle de la classe *MicroCeyx*.

Le modèle objet de SMECI est également disponible. La classe Smeci-object-model et ses méthodes ont été décrites. À titre d'exemple, les méthodes définies pour les modèles objet MicroCeyx et SMECI sont données en annexe B.4.2.

L'ensemble des méthodes permettent à DRIVER d'accéder à un certain nombre de fonctionnalités de base communes à tous les modèles objet.

Ces méthodes peuvent être facilement définies pour tout modèle, même si tel ou tel modèle objet n'est pas forcément capable d'exploiter la totalité des propriétés du modèle proposé par DRIVER.

Par exemple, le modèle objet de DRIVER gère la pose de contraintes sur les champs. MicroCeyx ne permet pas de le faire. Les contraintes ne peuvent donc pas être déclarées et exploitées en MicroCeyx, même si, dans le même temps, DRIVER en tire complètement profit.

Les méthodes composant l'interface fonctionnelle objet d'un modèle sont les suivantes :

* **Création d'une instance de classe.** Cette méthode a pour but de permettre à DRIVER de créer une instance de classe dans le modèle objet client et de récupérer cette instance.

Comme pour chaque méthode de l'interface fonctionnelle objet, le premier argument est l'objet "modèle objet" auquel est adressé l'envoi de message de création d'instance de classe. Les suivants sont spécifiques à chaque méthode. Pour celle-ci, il n'y en a qu'un. Il s'agit de l'objet qui représente la classe en question en interne à DRIVER. Il contient toutes les informations la concernant. La classe de cet objet "classe", de nom *Driver-class*, est décrite en §3.3.2.3.

* **Lecture de la valeur d'un champ.** Cette méthode a pour but de permettre à DRIVER d'accéder à la valeur du champ d'un objet.

Les arguments de cette méthode sont l'objet "modèle objet", le nom du champ et l'objet duquel on souhaite extraire l'information. Bien entendu, la méthode doit retourner la valeur du champ.

* **Affectation d'une valeur à un champ.** En complément de la fonction précédente, DRIVER a besoin de pouvoir affecter une (nouvelle) valeur au champ d'un objet. Il le fait par cette méthode.

Ses arguments sont, en plus de ceux de la méthode précédente, la valeur à affecter au champ désigné.
* **Envoi de message.** Cette méthode permet à `DRIVER` d'envoyer un message à un objet.

Ses arguments sont l'objet "modèle objet", le nom du message, l'objet auquel s'adresse le message, et la liste des éventuels arguments du message.

* **Test de l'existence d'une classe.** `DRIVER` a besoin de pouvoir tester si une classe a déjà été définie dans le modèle objet client.

Les deux arguments de cette méthode sont l'objet "modèle objet" et l'objet système `DRIVER` représentant cette classe.

Le résultat retourné doit être booléen.

* **Test de l'appartenance d'une instance à une classe.** `DRIVER` a également besoin de pouvoir tester si un objet est instance d'une classe au sens large, c'est-à-dire instance de cette classe ou de l'une de ses sous-classes. Cette méthode doit pouvoir le renseigner à ce sujet.

Ses arguments sont l'objet "modèle objet", l'objet testé et l'objet `DRIVER` représentant la classe.

Encore une fois, le résultat attendu est booléen.

Trois autres méthodes complètent l'interface fonctionnelle objet. Permettant à `DRIVER` d'utiliser des fonctionnalités de modèle objet beaucoup moins répandues que les précédentes, elles risquent de ne pas pouvoir être exprimées pour certains modèles objet.

* **Création de classe.** Cette méthode doit permettre à `DRIVER` de créer une classe dans le modèle objet client.

Outre l'objet "modèle objet", elle nécessite en argument l'objet représentant la classe considérée. Le corps de la fonction doit consister à reformuler sous forme d'appels aux primitives du modèle client la création de classe, ses champs, les contraintes qui y sont associées, toutes données communes à ses futures instances.

Cette méthode ne peut pas être définie pour la plupart des modèles objet des langages compilés. Ce n'est en fait pas très important car elle est invoquée uniquement si l'utilisateur souhaite utiliser les déclarations de classes en `DRIVER` pour créer, par la même opération, ces mêmes classes dans le modèle objet.

* **Mutation de classe.** Celle-ci doit permettre à `DRIVER` de muter un objet d'une classe vers une autre.

Ses arguments sont l'objet "modèle objet", l'objet `DRIVER` représentant la future classe de l'objet à muter, et l'objet en question.

Les modèles objet de Le_Lisp v15 offrant la mutation de classe, nous avons utilisé cette fonctionnalité pour optimiser le chargement et le traitement des objets. Certains algorithmes devront donc être revus lorsque nous implanterons `DRIVER` dans un environnement dont les modèles objet n'en disposent pas.
En fait, la mutation de classe est très utile quand on souhaite partager des informations de la base relationnelle sous-jacente entre plusieurs utilisateurs qui sont autorisés à la modifier, et donc à faire évoluer les classes des objets. Ceci permet par exemple d'utiliser la base pour pouvoir échanger ou partager certains objets entre systèmes indépendants.

Elle est également très pratique pour la gestion des défauts d'objet. Grâce à elle, le chargement d'un objet représenté par un défaut d'objet commence simplement par la mutation de cet objet de la classe des défauts d'objet vers sa classe principale dans la base. Cette opération est bien sûr suivie de la construction des valeurs des champs de l'objet.

* **Instances d'une classe.** La dernière méthode est utilisée par `DRIVER` pour récupérer l'ensemble des instances d'une classe donnée. Instances doit être pris au sens large puisque ce terme désigne également les instances des sous-classes.

Elle a deux arguments qui sont l'objet "modèle objet" et l'objet `DRIVER` représentant la classe.

Cette méthode n'est pas essentielle pour le fonctionnement du système.

Elle n'est utilisée que dans le module `DRIVER` dédié à l'écriture des objets persistants dans la base de données, pour traiter un cas particulier de violation de contraintes d'intégrité sur les clés. Elle permet de déterminer l'ensemble des instances de la classe vérifiant correctement ces contraintes, et proposer à l'utilisateur de choisir parmi elles l'objet qui remplacera celui qui provoque l'incohérence.

Ce cas de figure, extrêmement rare, n'apparaît que dans certaines formes exceptionnelles de schéma de correspondances et peut être souvent évité en modifiant le schéma à l'origine du problème. Il est examiné en détail en §4.2.2.2.

Quand la fonction n'est pas exprimable avec le modèle objet client utilisé, il suffit de la définir de telle manière qu'elle provoque explicitement une erreur expliquant l'incohérence des données. `DRIVER` détecte le déclenchement de l'erreur et annule toute modification de la base depuis la dernière mise à jour validée, évitant ainsi de laisser la base dans un état indéterminé et inconsistent.

L'utilisateur n'a plus alors qu'à corriger l'objet pris en défaut avant de re-provoquer une nouvelle mise à jour dans la base.

Un exemple de jeu de méthodes est donné en annexe §B.8; il permet d'utiliser `DRIVER` avec le modèle objet de `SMECI`.

### 3.1.3 Descriptions relationnelle et objet en `DRIVER`

Ces descriptions n'ont pas pour but de créer, d'un côté les tables correspondantes dans la base de données, de l'autre, les classes dans le modèle objet client, même si des primitives `DRIVER` permettent par ailleurs de le faire. Elles ont plutôt valeur de déclarations
d'existence pour permettre ensuite le rapprochement des structures relationnelles et objet décrites.

Ainsi, il convient de ne décrire que les relations et les classes qui présentent un intérêt pour la couche objet virtuelle persistante.

Il n'est pas non plus utile de décrire complètement telle ou telle relation dont on sait pertinemment que l'on n'en exploitera qu'une partie. De la même manière, il est sans intérêt de décrire en `DRIVER` les champs d'une classe auxquels on n'a pas l'intention d'accorder la persistance, toujours dans le cas, est-il besoin de le répéter, où ces déclarations ne sont pas utilisées pour créer les structures dans les mondes correspondants.

Il appartient au concepteur de sélectionner les informations dont il a besoin et d'établir ensuite les correspondances qui lui paraissent appropriées.

### 3.1.3.1 Description du schéma relationnel en `DRIVER`

La description d'un schéma relationnel se fait simplement en `DRIVER` en explicitant les tables en faisant partie, leurs attributs, types et contraintes d'intégrité sémantiques.

L'exemple suivant (listing 1) illustre les déclarations de trois tables d'un même schéma, `emp` décrite dans la figure 3.1, `person` contenant des renseignements personnels sur les individus, et `dept` la table des départements.

<table><caption>Listing 1. Description de relations en `DRIVER`</caption><thead><tr</tr></thead><tbody><tr><td>(driver-deftable emp</td></tr><tr><td>(empno integer 2 keypart)</td></tr><tr><td>(ename string 20 not-null)</td></tr><tr><td>(fname string 20 ())</td></tr><tr><td>(job string 20 ())</td></tr><tr><td>(mgr integer 2 ())</td></tr><tr><td>(sal float 8 ())</td></tr><tr><td>(com float 8 ())</td></tr><tr><td>(deptn integer 2 ()))</td></tr><tr><td>(driver-deftable person</td></tr><tr><td>(fname string 20 keypart)</td></tr><tr><td>(fname string 20 keypart)</td></tr><tr><td>(ssnum string 15 unique)</td></tr><tr><td>(position string 10 ())</td></tr><tr><td>(phone string 12 ())</td></tr><tr><td>(car integer 2 ()))</td></tr><tr><td>(driver-deftable dept</td></tr><tr><td>(deptno integer 2 keypart)</td></tr><tr><td>(dname string 20 ()))</td></tr></tbody></table>
`emp`, `person` et `dept` comprennent (au moins) les huit, six et deux attributs énoncés.

Chaque attribut est décrit par son nom, son type, sa taille dans le type, plus un mot réservé exprimant les éventuelles contraintes d'intégrité auquel il est éventuellement soumis (l'absence de valeur définie est représentée par le symbole `()`).

Par défaut, la liste des types disponibles est `integer`, `float` et `string`. Cette liste peut être complétée en fonction des besoins et du SGBD utilisé.

Les contraintes d'intégrité imposables aux attributs sont classiques et standard :

* La première identifie l'attribut comme faisant partie de la clé de la relation. Elle se définit à l'aide du mot réservé `keypart`.

* La seconde interdit l'absence de valeur pour l'attribut en question dans un n-uplet. Elle se définit à l'aide du mot réservé `not-null`.

* La dernière interdit qu'un attribut ait même valeur dans deux n-uplets différents. Elle se définit à l'aide du mot réservé `unique`.

Dans l'exemple précédent, la clé de la relation `emp` est un attribut `empno`, celle de la relation `person` est constituée des deux attributs `fname` et `lname`. Une contrainte a été posée sur `emp.ename` précisant qu'un n-uplet de la relation doit forcément avoir un nom spécifié, une autre sur `person.ssnum` imposant cette fois que deux personnes ne peuvent avoir le même numéro de sécurité sociale.

### 3.1.3.2 Le langage objet de `DRIVER`

Décrire une classe consiste en `DRIVER` à :

* la nommer,

* la positionner dans les arbres de classes en précisant le nom de sa classe mère,

* expliciter les champs qui lui sont associés.

L'exemple suivant (listing 2) illustre la déclaration des classes `Employé`, `Vendeur` et `Département`, présentées dans la figure 3.3.

<table><caption>Listing 2. Description de classes en `DRIVER`</caption><thead><tr><th colspan="3">(driver-defclass Employe ()</th></tr></thead><tbody><tr><td>(nom</td><td>atom</td><td>symbol (localp ()))</td></tr><tr><td>(prenom</td><td>atom</td><td>symbol)</td></tr><tr><td>(num-ss</td><td>atom</td><td>symbol)</td></tr><tr><td>(qualif</td><td>atom</td><td>symbol)</td></tr><tr><td>(chef</td><td>object</td><td>Employe)</td></tr></tbody></table>
(responsable-de object2)
(salaire atom float
(constraint (lambda (s) (and (>= s 700.) (<= s 9999.))))
(grade atom fix (initval 0))
dpt object Departement)
(adresse object Adresse)
(tel-prive atom symbol)
(vehicule object Vehicule))

(driver-defclass Vendeur Employe
(qualif (constraint (lambda (qual) (eq qual 'salesman))))
(situ-famille atom symbol)
(commission atom float)
(sal-total monotexpr float)))

(driver-defclass Departement ()
(nom atom symbol (localp ()))
(proDUits object2set)
(sites atomset symbol)
(chefs ordobjset Cadre)
(personnel objectset Employe)
(moy-sal multitexpr float)))

Le nom de la classe mère est précisé après le nom de la classe. S'il n'y a pas de classe mère, le symbole () exprime cette absence. Employé et Département n'héritent d'aucune autre classe connue de DRIVER. En tant que sommets d'arbres de classes, elles sont dites classes principales. Vendeur par contre est une sous-classe d'Employé.

La déclaration en DRIVER d'une classe n'héritant d'aucune classe mère n'implique pas que ce soit forcément le cas dans le modèle objet client. Cela exprime juste le fait que la classe en question n'hérite, dans le modèle objet client, d'aucune classe qui soit persistante du fait du même schéma de correspondances.

Les champs déclarés appartiennent en général à la classe correspondante<fnref n="3" />. Ils sont décrits par leurs noms, leurs types, certaines informations complémentaires qui varient avec le type du champ, plus un certain nombre de paramètres optionnels possédant, par défaut, une valeur déterminée.

Un certain nombre de types de champ sont proposés :

* **Champ à valeur atomique.** Désigné par le mot réservé `atom`, ce type regroupe les champs destinés à contenir une information élémentaire. Outre le type `DRIVER` du champ (`atom`) doit être précisé le type des futures valeurs contenues :

<footnote n="3">³Ce n'est pas toujours le cas, voir l'option localp.</footnote>
- **symbol**. Le champ contiendra des symboles. Ce type est principalement intéressant pour une utilisation de `DRIVER` dans un environnement `LISP`. La première version de `DRIVER` a été écrite en Le_Lisp [Cha89];

- **string**. Contenu de type "chaîne de caractères";

- **fix** ou **integer**. Contenu de type entier;

- **float**. Contenu de type réel;

- (). Type du contenu inconnu ou "autre".

La gestion de l'absence de type telle qu'elle est faite ci-dessus peut être insuffisante lors de l'utilisation d'un modèle objet client fortement typé. De nouveaux types de valeur peuvent être rajoutés en fonction des besoins et du modèle objet client utilisé, à l'aide de la primitive `DRIVER` prévue à cet effet.

Les exemples de déclaration de champs atomiques (`nom`, `salaire`, `grade d'employé`, etc...) abondent dans la description des trois classes précédentes. Certains sont précisés d'informations optionnelles qui sont explicitées ci-après.

* **Champ ayant pour valeur un ensemble de valeurs atomiques.** Ce type de champ impose à chacun des éléments de l'ensemble d'être d'un même type à préciser. `DRIVER` ne gère en effet pas directement les ensembles hétérogènes. Nous verrons qu'il est malgré tout possible de le faire de façon artificielle en utilisant les méthodes de filtrage de champ (cf. §3.2.5.3). Ce type de champ est désigné par le mot réservé `atomset`.

Le champ `sites` de la classe `Département` qui contient l'ensemble des noms des sites où le département a une antenne est un exemple de tel champ.

* **Champ ayant pour valeur un ensemble ordonné de valeurs atomiques.** Comme pour le type précédent, chaque élément de l'ensemble est d'un même type à préciser. Ici, en plus, l'ordre des éléments est important et doit être persistant lui aussi. Ce type de champ est désigné par le mot réservé `ordatomset`.

Les classes proposées dans l'exemple ci-dessus ne comprennent pas de champ de ce type. On pourra néanmoins facilement déduire des exemples présentés une illustration de ce type de champ. Ainsi, le champ `sites` de la classe `Département` aurait pu être défini dans ce type, si l'ordre des éléments avait présenté un intérêt pour l'application. La déclaration de ce champ aurait alors été :

```
(sites ordatomset symbol)
```

* **Champ à objet de classe connue (persistante).** Ce type caractérise les champs destinés à référencer un objet dont la classe, ou au moins la classe principale, est connue. Bien entendu, le nom de cette classe, voire de la classe principale, doit être précisé. Le champ peut contenir toute instance de la classe en question ou de l'une de ses sous-classes. Le type est désigné par le mot réservé `object`.

Les champs `chef` et `dpt` de la classe `Employé` sont des exemples de champ objet à classe connue.
* **Champ à objet de classe quelconque (mais persistante).** Ce type caractérise les champs destinés à contenir un objet dont la classe ne peut être précisée. Il est désigné par le mot réservé `object2`.

Le champ `responsable-de` d'Employé est un champ de ce type. Un employé peut en effet être dans notre application responsable d'un département (objet instance de la classe principale Département), d'un projet (objet instance de la classe principale Projet) ou d'un produit (objet pouvant, selon sa nature, être instance de telle ou telle classe principale).

* **Champ ayant pour valeur un ensemble d'objets de même classe (persistante).** Ce type caractérise les champs destinés à contenir un ensemble d'objets dont la classe, ou au moins la classe principale, est commune et connue. Il doit également ici être précisé le nom de la classe des objets qui y seront référencés ou, à défaut, le nom de leur classe principale. Les objets pourront être instances de la classe précisée, ou de l'une de ses sous-classes. Ce type est désigné par le mot réservé `objectset`.

Un champ de ce type est défini dans la classe Département. Il s'agit en l'occurrence du champ personnel, destiné à recueillir l'ensemble des employés de chaque département.

* **Champ ayant pour valeur un ensemble ordonné d'objets de même classe (persistante).** Comme pour le type précédent, chaque objet élément de l'ensemble est (au moins) instance d'une même classe à préciser. Ici, en plus, l'ordre des objets est important et doit également être persistant. Ce type de champ est désigné par le mot réservé `ordobjset`.

Le champ chefs de la classe Département est un exemple de champ de ce type.

* **Champ ayant pour valeur un ensemble d'objets de classes quelconques (mais persistantes).** Ce type caractérise les champs destinés à contenir un ensemble d'objets de classes différentes et indépendantes, c'est-à-dire héritant de classes principales différentes. Ce type de champ est désigné par le mot réservé `object2set`.

Le champ produits de la classe Département correspond à ce type. Il est destiné à recueillir l'ensemble des produits dont le département est responsable.

* **Champ ayant pour valeur un ensemble ordonné d'objets de classes quelconques (mais persistantes).** Comme le type précédent, ce dernier caractérise les champs destinés à contenir un ensemble d'objets de classes différentes et indépendantes. Ici, en plus, l'ordre des objets est important et doit également être persistant. Ce type de champ est désigné par le mot réservé `ordobj2set`.

* **Champ à valeur atomique destiné à recueillir le fruit d'un calcul sur la base de données.** Cette catégorie de champ ne présente en fait un intérêt que dans la perspective où l'on souhaite rapprocher la classe qui le contient avec des structures relationnelles. Comme pour les champs atomiques doit être précisé le type des futures
valeurs contenues : `symbol`, `string`, `fix`, `float` ou `()` (voir description ci-dessus du champ atomique).

Bien évidemment, cette catégorie de champs ne peut être consultée qu'en lecture. Les calculs sur la base qu'on leur associe sont décrits lors de la phase de définition des correspondances.

On distingue en fait deux types de champs, selon la nature du calcul qu'on leur associe. Ces deux types découlent de deux types de calculs possibles :

- Le premier permet d'effectuer le calcul sur un seul n-uplet relationnel, généralement la correspondance principale de l'objet considéré, celui dont le champ calculé dont on parle sera affecté. Somme ou différence d'attributs du n-uplet sont des exemples d'expressions possibles. Ce type de champ est désigné par le mot réservé `monotexpr`.

Un champ de ce type a été défini dans la classe `Vendeur`. Il s'agit de `sal-total`, destiné à recueillir le calcul de la somme `salaire+commission` (attributs `emp.sal` + `emp.com`) pour chaque vendeur (listing 3).

- Le second permet d'effectuer le calcul sur un ensemble de n-uplets, correspondant généralement à un ensemble d'objets. Ce genre de calcul permet de réaliser par exemple une moyenne ou la somme d'un ensemble de valeurs d'un attribut. Ce type de champ est désigné par le mot réservé `multitexpr`.

Ce type de champ est également illustré, cette fois au sein de la classe `Département` par l'intermédiaire du champ `moy-sal`. Ce champ est destiné à recueillir le calcul de la moyenne des salaires des employés du département.

On a vu en §2.3.4 qu'on peut accéder à la valeur du champ d'un objet de deux façons différentes depuis la couche objet virtuelle. On peut faire la remarque que l'existence de ces deux possibilités présente un intérêt particulier pour l'exploitation des champs calculés :

- **Accès du premier type.** Le champ est calculé lors de la construction de l'objet jumeau et le résultat y est stocké. Par la suite, à chaque nouvel accès du premier type, la valeur est lue dans l'objet et n'est pas recalculée.

- **Accès du second type.** La valeur du champ est recalculée à partir des données contenues dans la base. Si le jumeau de l'objet relationnel a déjà été construit et si le réflexe de mise-à-jour est actif (cf. §2.3.4.2), le champ du jumeau est actualisé avec le résultat du dernier calcul.

Le second type d'accès est évidemment plus coûteux puisqu'il se traduit par l'envoi d'une requête au SGBD à chaque consultation du champ. Il a en retour l'avantage de renvoyer des valeurs actualisées, ce qui est intéressant quand la base est partagée entre plusieurs utilisateurs.
Lors de l“instanciation” de l’interface fonctionnelle objet, on pourra définir les accesseurs aux champs du modèle client de telle manière qu’ils génèrent par défaut un accès du premier type, et pour les champs calculés, un accès du second.

### Les paramètres optionnels de déclaration de champ

Les déclarations des champs peuvent être complétées à l'aide de paramètres optionnels. Une option se présente dans la syntaxe de notre langage sous la forme d'une liste contenant au moins deux éléments dont le premier identifie l'option et le ou les suivants donnent son expression ou sa valeur.

* **Définition de la valeur initiale d'un champ.** La valeur initiale affectée à un champ lors de la création d'un objet peut être spécifiée.

Dans notre implantation, l'expression permettant de calculer la valeur initiale du champ est une forme Lisp qui est évaluée lors de la création de chaque instance. Elle est introduite par le mot réservé `initval`.

Cette option est illustrée, dans notre application, au sein de la classe `Employé`, par la définition d'une valeur initiale pour le champ `grade`. Toute nouvelle instance d'employé se verra affecter automatiquement un grade par défaut de valeur 0.

* **Contrainte d'un champ atomique.** `DRIVER` ne permet actuellement de contraindre que les champs atomiques non calculés.

Dans notre implantation, l'expression de cette contrainte est une lambda-expression à un argument qui a valeur de prédicat. Elle est appliquée à toute valeur candidate du champ; si le résultat est vrai (au sens Lisp), la contrainte est vérifiée; elle ne l'est pas dans le cas contraire. L'expression est introduite par le mot réservé `constraint`.

Deux contraintes ont été définies dans l'application. La première, posée sur le champ `salaire` de la classe `Employé` impose à chaque employé d'avoir un salaire compris entre $700 et $9999. La seconde contraint les instances de la classe `Vendeur`. Elle leur impose d'avoir "salesman" comme valeur du champ `qualif` qui est hérité de la classe `Employé`. On remarquera que les objets qui sont précisément instances de la classe `Employé` ont un champ `qualif` sur lequel n'est posée aucune contrainte.

* **Déclaration d'un champ délocalisé.** Il est possible, par cette option, d'apporter la persistance à un champ défini au niveau d'une classe ancêtre d'une classe persistante. Sans cette facilité, il serait impossible de rendre persistantes des informations contenues dans des instances de classes persistantes, mais stockées dans des champs hérités de classes ancêtres non persistantes.
Dans les modèles objet où toute classe hérite d'une classe système unique, "Objet" par exemple, il arrive que soient accessibles dans cette classe racine des champs destinés à recueillir des informations communes à tous les objets, notamment leurs noms. Dans la perspective de vouloir associer par la suite une classe principale à une table relationnelle, il peut être utile de faire correspondre le champ nom de cette classe, hérité de la classe racine, à l'attribut nom de la relation.

On pourrait bien entendu décider de rendre directement persistante la classe racine. Il ne faut cependant pas oublier qu'une classe principale est associée à une relation principale (cf. §3.2.2) et que toute sous-classe de cette classe est associée à la même relation principale qu'elle. Toutes les classes persistantes devraient alors être associées à la même et unique relation ce qui ne manquerait pas d'être lourd et rapidement sans intérêt.

Cette option est identifiée par le mot réservé localp. La valeur par défaut est vrai (champ local).

Dans notre application, les champs nom des classes Employé et Département sont déclarés non locaux, ceci indiquant qu'ils sont hérités d'une classe mère dans le modèle objet client. Toute création d'une de ces deux classes dans ce modèle client déclenchée par la primitive DRIVER adéquate se traduira donc par l'absence de création de champ nom local.

## 3.2 Mise en correspondance des deux modèles

L'intérêt de définir des correspondances entre le modèle relationnel et le modèle objet canonique défini par DRIVER réside, rappelons-le, dans le fait, d'une part, de permettre la manipulation de données relationnelles sous forme d'objets dans un environnement désigné, et, d'autre part, de pouvoir décrire des objets de ce même environnement sous forme de données relationnelles pour les sauver dans une base de données relationnelle.

La définition de correspondances permet d'établir un pont entre les deux formalismes. Les différents concepts propres à chaque modèle ont donc été rapprochés le plus "naturelement" possible conformément à leurs sémantiques.

### 3.2.1 D'importantes différences conceptuelles

La mise en correspondance du modèle relationnel et d'un modèle objet n'est pas tout-à-fait immédiate car le modèle objet est beaucoup plus riche que son homologue relationnel. Il implique principalement en plus les notions de hiérarchie, d'héritage et d'envoi de message.

Le modèle relationnel ne permet d'établir explicitement aucune hiérarchie entre les relations d'un même schéma. Il ne sait gérer aucune sorte de lien d'héritage et il n'est pas
possible de définir une relation fille d'une autre et héritant de ses attributs. L'envoi de message est inexistant.

Concernant l'information contenue dans les structures, les possibilités de poses de contraintes sur les attributs sont relativement réduites.

Enfin, l'une des plus importantes restrictions imposées par le modèle relationnel est l'absence de types complexes pour les attributs, par exemple les types *ensemble* et *objet*.

### 3.2.2 Classe et relation, objet et n-uplet

La notion de relation s'apparente de façon assez naturelle à celle de classe. Nous les associations. En fait, nous rapprochons plus précisément une relation et une hiérarchie de classes, car nous imposons à toutes les sous-classes d'une classe dont on a défini la correspondance d'être associées à la même relation qu'elle (figure 3.6). La classe la plus générale associée à une relation est appelée *classe principale*. Sa relation associée est également qualifiée de *relation principale*.

<table><caption>Figure 3.6 : Associations de classes et de relations</caption><thead><tr><td>Classe</td><td>Relation</td></tr></thead><tbody><tr><td>Employé</td><td>emp</td></tr><tr><td>Vendeur</td><td>emp</td></tr><tr><td>Département</td><td>dept</td></tr></tbody></table>

De la même façon, nous rapprochons les notions de n-uplet relationnel et d'objet (figures 3.2, 3.4 et 3.7). L'un comme l'autre sont les données, instances, occurrences de leurs structures respectives, la relation pour l'un, la classe pour l'autre. Ainsi, en *DRIVER*, tout n-uplet existant dans une relation est un candidat objet, instance de la classe associée à cette relation.

Ce candidat objet est un objet à part entière, élément de la couche objet virtuelle, quand sa valeur est compatible avec les contraintes imposées aux instances de la classe. Ces contraintes, traduites en contraintes sur la relation grâce au schéma de correspondances, constituent une sorte de filtre qui détermine quels n-uplets sont des objets potentiels, *relationnels* (cf. §2.3.3) et lesquels ne le sont pas. Ceux qui ne sont pas retenus sont simplement ignorés par *DRIVER*. Tout se passe comme s'ils n'existaient pas.

Quand une relation est associée à une hiérarchie de classes, ses n-uplets qui vérifient au moins les contraintes imposées par la classe principale sont les correspondants d'objets relationnels instances de l'une d'entre elles. Un n-uplet de relation principale détermine précisément la classe de l'objet qui le représente en fonction des contraintes imposées par chaque classe qu'il respecte ou non. Cette classe devant être identifiée de manière unique,
<table><caption>Figure 3.7 : Correspondances objet - n-uplet relationnel</caption><thead><tr><td>Objet, classe</td><td>Relation(n-uplet)</td></tr></thead><tbody><tr><td>Paul Miller, Employé</td><td>↔ emp(7934,miller,paul,clerk,7782,1300.,10)</td></tr><tr><td>Peter Ward, Vendeur</td><td>↔ emp(7521,ward,peter,salesman,7698,1250.,1400.,30)</td></tr><tr><td>Paul King, Président</td><td>↔ emp(7839,king,paul,président,,5000.,10)</td></tr></tbody></table>

toutes les sous-classes d'une même classe doivent pouvoir être différenciées entre elles, au moins par des contraintes différentes de façon qu'un n-uplet ne puisse vérifier au plus que celles de l'une d'entre elles. Une classe se différencie de sa mère par des contraintes plus astreignantes imposées aux objets, outre la définition de nouveaux champs qui s'ajoutent à ceux dont elle hérite.

Lors d'un filtrage sur la base de données, `DRIVER` classe automatiquement les objets relationnels choisis et leur attribue la classe la plus précise en fonction de leurs valeurs. Les contraintes s'organisant en arbre, l'appartenance à une classe est ainsi fonction de leur satisfaction ou non tout au long de la hiérarchie considérée.

Un objet relationnel détermine de manière unique le n-uplet qu'il représente dans la couche virtuelle grâce à la définition d'un *identificateur relationnel*, formé d'un nom de table principale et d'une valeur de clé dans cette table. Cet identificateur ne comporte pas d'information sur le SGBD et la base concernés, car chaque représentant d'objet relationnel dans l'environnement (défaut d'objet ou objet jumeau) connaît le schéma de correspondances dont il dépend, qui, lui-même, connaît le SGBD et la base pour l'exploitation desquels il a été défini.

### 3.2.3 Correspondances relationnelles des différents types de champs

Examinons maintenant comment établir des correspondances entre les différents types de champ du modèle objet et les structures relationnelles.

#### 3.2.3.1 Correspondance du champ atomique

Il nous a semblé naturel d'associer attributs et champs atomiques.

Les attributs de la relation principale sont en général les correspondances de la plupart des champs atomiques de la classe principale associée (figure 3.8).

La plupart seulement, car il est en effet possible d'associer un champ atomique à un attribut d'une autre relation que la relation principale. Cette autre relation est alors appelée *relation secondaire*, associée à la classe en question. Une relation secondaire est liée à la relation principale au moyen d'une *jointure*.

De façon générale, une jointure se construit en liant par comparaison (égalité, inégalité, etc...) un ou plusieurs attributs de la relation principale à un ou plusieurs attributs de la relation secondaire, soit directement, soit par l'intermédaire d'autres tables qui deviennent
<table><caption>Figure 3.8 : Associations champ atomique - attribut de relation principale</caption><thead><tr><th>Classe, champ</th><th>Relationattribut</th></tr></thead><tbody><tr><td>Employé, nom</td><td>emp.ename</td></tr><tr><td>Employé, prénom</td><td>emp.fname</td></tr><tr><td>Employé, qualif</td><td>emp.job</td></tr><tr><td>Employé, salaire</td><td>emp.sal</td></tr><tr><td>Vendeur, commission</td><td>emp.com</td></tr><tr><td>Département, nom</td><td>dept.dname</td></tr></tbody></table>

nent également alors relations secondaires de la classe correspondante. Les comparaisons d'attributs peuvent être liées par et ou ou. En DRIVER, les jointures sont orientées; elles sont définies pour accéder, depuis une table origine, à une table destination.

Dans notre application, nous avons choisi de faire de la relation **person** (cf. §3.1.3.1) une relation secondaire de la classe **Employé**. Cela nous permettra de trouver au sein des instances d'**Employé** certaines informations issues dans cette table. Pour pouvoir y accé-

<figure><img src="image_12.png" /><figcaption>Figure 3.9 : Jointure de la table emp vers la table person</figcaption></figure>

der depuis la relation emp, nous avons défini une jointure qui rapproche les deux tables (figure 3.9). Cette jointure exprime le fait qu'un employé (n-uplet de emp) et une personne (n-uplet de person) référencent le même individu s'ils ont même nom et même prénom. Son expression est :

emp.ename = person.pname and emp.fname = person.fname

Appelons-la J emp→person . Sa définition permet l'utilisation d'attributs de la table person pour la correspondance de champs d'Employé, notamment des champs atomiques (figure 3.10).

Cette jointure associe à chaque n-uplet de la relation principale un unique n-uplet de la relation secondaire. Elle est appelée jointure élémentaire . Une jointure élémentaire n'est pas autorisée à échouer : elle représente une liaison dite forte .
<table><caption>Figure 3.10 : Associations champ atomique - attribut de relation secondaire</caption><thead><tr><th>Classe, champ</th><th>Relationattribut</th><th>Jointures</th></tr></thead><tbody><tr><td>Employé, num-ss</td><td>person.ssnum,</td><td>(J emp→person])</td></tr><tr><td>Employé, tél-privé</td><td>person PHONE,</td><td>(J emp→person])</td></tr></tbody></table>

Participant à la définition d'une correspondance de champ atomique, une jointure est nécessairement élémentaire. L'objet relationnel est constitué d'un n-uplet de la relation principale, plus un n-uplet dans chacune des relations secondaires associées à la relation principale. Si une jointure échouait, toute une partie de l'objet deviendrait inaccessible. En fait, l'algorithme d'accès aux données de la base rendrait l'objet complet totalement inaccessible (cf. §4.1.2).

La relation principale d'une classe et les relations secondaires liées par des jointures élémentaires à cette relation principale sont appelées les *relations élémentaires* associées à la classe considérée.

Toute relation élémentaire peut être utilisée à son tour comme base sur laquelle ou à partir de laquelle peuvent être définies de nouvelles correspondances de champs. Ses attributs peuvent être utilisés comme correspondances de champs ou pour définir de nouvelles jointures vers de nouvelles relations.

Chaque correspondance de champ atomique est donc accessible par une *chaîne de jointures* reliant la relation secondaire sur laquelle a été définie la correspondance à la relation principale. Le *chaînage* est simple. Chaque jointure étant orientée, l'ensemble doit en comporter une dont la table origine est la relation principale. La table destination de cette jointure doit être la table origine d'une autre, et ainsi de suite jusqu'à la table contenant l'attribut associé au champ. Une fois la chaîne reconstituée et les éléments ordonnés, toutes les jointures doivent avoir été utilisées. Si ce n'est pas le cas, la chaîne est *incohérente*. Si des éléments manquent, elle est *inconsistante*. Enfin, on peut faire la remarque que, si chaque jointure est élémentaire, la chaîne elle-même est élémentaire.

Une table ne peut pas être à la fois principale et secondaire pour des raisons de maintien de la cohérence entre la couche objet réelle et la base de données. Un objet relationnel est constitué d'un n-uplet dans chaque relation, principale ou secondaire, qui participe à la correspondance de la classe. Si un double rôle pouvait être accordé à une relation, la création d'un objet de la classe l'ayant pour table secondaire provoquerait par effet de bord la création d'un autre objet de classe celle l'ayant comme relation principale.

Une table secondaire pour une classe ne peut pas non plus être table secondaire pour une autre n'héritant pas de la même classe principale. Une table ne peut pas être table principale de deux classes principales différentes.

Pour des raisons similaires, l'usage multiple d'un attribut pour définir différentes cor-
respondances est interdit. Un attribut, qui a déjà été choisi comme correspondance de champ atomique ou qui intervient déjà dans la définition d'une jointure, ne peut plus être utilisé à d'autres fins. Il n'est plus disponible pour définir de nouvelles jointures ou de nouvelles correspondances de champs atomiques. Ceci n'interdit cependant nullement de réutiliser la jointure à laquelle il participe éventuellement dans la définition de nouvelles correspondances.

Les règles qui viennent d'être énoncées peuvent se révéler trop contraignantes pour certains cas particuliers d'application. Il peut par exemple être utile de pouvoir associer un champ atomique à un attribut qui participe également à la définition d'une jointure. Notre application comprend un tel exemple par le biais des attributs `emp.ename` et `emp.fname`. Ces deux attributs interviennent dans la jointure $J[emp→person]$ définie plus haut (figure 3.9), mais nous souhaiterions également les associer à deux champs de la classe `Employé`, les champs `nom` et `prénom` (figure 3.8).

Afin de préserver la cohérence du système, les utilisations et correspondances multiples de relations sont autorisées dans la mesure où seule l'une d'entre elles est habilitée à écrire dans la base. Toutes les autres ne doivent permettre l'accès à l'information qu'en lecture.

Le même principe s'applique aux attributs intervenant à plusieurs reprises dans les définitions de correspondances. Pour des raisons de cohérence, ces attributs ne pourront être mis à jour dans la base que pour l'une de leurs participations.

Un attribut peut participer à une correspondance de deux façons différentes : dans une jointure ou comme correspondance d'un champ atomique. Une jointure est une contrainte qui lie des attributs et qui doit être impérativement vérifiée pour qu'un futur chargement des objets soit possible (cf. §4.2.1.3). Le report dans la base des valeurs des attributs qui la satisfont est nécessaire. Pour cette raison, si un attribut participe, entre autres correspondances, à une jointure, cette dernière étant nécessairement habilitée à écrire dans la base, toutes les autres correspondances ne devront permettre qu'un accès en seule lecture sur cette même base. Si un attribut intervient dans une jointure, toute autre utilisation de cet attribut comme correspondance de champ atomique ne sera possible que comme consultation de la base. Pour la raison évoquée ci-dessus et afin d'éviter les conflits ou les incompatibilités, il est strictement interdit de définir deux jointures comportant un même attribut.

Dans notre application, les attributs `emp.ename` et `emp.fname` pourront donc participer à la jointure $J[emp→person]$ et être les correspondances des champs `nom` et `prénom` de la classe `Employé` si ces deux dernières correspondances ne permettent qu'un accès en lecture sur la base. Il est par ailleurs utile de préciser dès maintenant que si un nouvel objet persistant de la classe `Employé` est créé, son nom et son prénom seront bien sûr reportés dans la base. L'algorithme d'écriture d'un objet résout les contraintes posées sous forme de jointures (cf. §4.2.3.1) et "sait", quand c'est possible, exploiter le contenu des champs pour donner une première valeur aux attributs composant les jointures. Ces valeurs sont ensuite écrites dans la base. Ainsi, les champs `nom` et `prénom` vont être reportés dans la base à leur première validation. Seulement, ils ne pourront plus être modifiés ensuite pour éviter tout échec de la jointure à laquelle ils participent.
### 3.2.3.2 Correspondance du champ "objet"

Un champ de type objet représente un lien orienté d'un objet vers un autre. Quand la classe principale de l'objet référencé est connue, il est naturel d'associer ce lien entre classes à un lien entre les tables principales correspondantes.

Par définition, nous faisons correspondre ce type de champ à une chaîne de jointures qui relie ces tables principales et associe à chaque n-uplet de la première un n-uplet de la seconde (figure 3.12).

La dernière jointure de la chaîne doit avoir comme table initiale une relation élémentaire de la classe possèdant le champ et comme table finale la relation principale de l'autre classe. Elle est appelée *jointure objet*.

Présentons deux exemples de correspondance de champ objet. Le premier exploite l'existence, au sein de la relation *emp*, de l'attribut *deptn* qui contient le numéro du département auquel appartient l'employé. Une jointure entre les deux tables apparaît (figure 3.11) :

$$ emp.deptn = dept.deptno $$

Cette jointure, que nous appelons $J[emp→dept]$, est la correspondance naturelle du champ *dept* de la classe *Employé* (figure 3.12). Il arrive que la chaîne de jointures se réduise comme ici à la seule jointure objet.

<figure><img src="image_13.png" /><figcaption>Figure 3.11 : Jointure de la table *emp* vers la table *dept*</figcaption></figure>

Par ailleurs, nous constatons, dans la relation *person*, l'existence d'un attribut *car* qui contient, pour chaque personne, l'identificateur de son véhicule personnel. Cet identificateur correspond à une valeur de clé contenue dans l'attribut *vnum* de la table *vehicle*. On reconnaît une autre jointure objet possible, cette fois, entre les tables *person* et *vehicle* :

$$ person.car = vehicle.vnum $$

Appelons cette jointure $J[person→vehicle]$. En admettant qu'on ait défini comme correspondance à la table *vehicle* la classe *Voiture*, on constate qu'on peut faire correspondre le champ *vehicule* de la classe *Employé* à la chaîne de jointures ($J[emp→person]$
$J[\mathbf{person} \to \mathbf{vehicle}]$ (figure 3.12). Cette chaîne est cohérente et consistante. Elle relie bien les relations principales des deux classes concernées.

<table><caption>Figure 3.12 : Associations champ objet - chaîne de jointures</caption><thead><tr><th>Classe, champ</th><th></th><th>Jointures</th></tr></thead><tbody><tr><td>Employé, dpt</td><td>↔</td><td>(J[emp→dept])</td></tr><tr><td>Employé, véhicule</td><td>↔</td><td>(J[emp→person] J[person→vehicle])</td></tr></tbody></table>

Les modèles objet autorisent l'absence de valeur dans les champs de type objet des objets. On remarquera au passage que c'est un cas particulier où le champ de type objet a alors une valeur qui n'est pas un objet (!).

Cette caractéristique a été prise en compte. En première analyse, sa correspondance en termes relationnels est l'échec de la jointure objet participant à la correspondance du champ.

Les $N-1$ premières jointures de la chaîne sont élémentaires. Elles lient entre elles des relations élémentaires. Par contre, la dernière (la jointure objet) ne l'est pas. Elle relie une relation élémentaire de la classe considérée à une relation indépendante de cette classe, en l'occurrence, la relation principale de la classe de l'objet contenu. Si le champ objet ne contient pas de référence vers un objet, cette absence traduit ou se traduit par un échec de cette jointure.

En `DRIVER`, on normalise la gestion de l'absence de valeur dans les champs de type objet en imposant un certain nombre de restrictions aux jointures objet. Ces restrictions permettent de spécifier comment ces jointures sont autorisées à échouer. Elles permettent aussi de gérer efficacement ces échecs :

1. Les attributs de la relation destination (la relation principale de la classe de l'objet à référencer) impliqués dans la jointure sont obligatoirement et exclusivement les attributs constituant la clé de la relation.

2. Ces constituants de clé sont comparés à des attributs d'autres relations, appelés attributs référentiels, ou à des valeurs. À chaque fois, le comparateur est nécessairement l'égalité.

3. Toutes ces comparaisons doivent être reliées entre elles et avec le reste du corps de la jointure, s'il y en a un, par un et.

Une quatrième restriction est imposée aux jointures objet définies pour la correspondance de champ de type "objet". Cette restriction n'est donc pas caractéristique de toutes les jointures objet. Celles qui la vérifient sont dites strictes :
4. S'il existe, le reste du corps doit représenter un ensemble de liaisons fortes entre la relation origine de la jointure, relation élémentaire pour la classe possédant le champ, et les relations des attributs référentiels. Autrement dit, les relations des attributs référentiels doivent elles aussi être élémentaires pour la classe possédant le champ.

Par définition,

le champ objet d'une instance ne référence aucun autre objet (champ "vide", absence de valeur) si et seulement si l'un au moins des attributs référentiels vaut NULL<sup>4</sup>.

Cette définition établit très précisément quelle correspondance relationnelle est donnée à l'absence de valeur dans un champ objet. Grâce à elle, le système est en mesure non seulement de détecter un champ objet vide au chargement, mais aussi d'écrire un tel champ vide dans la base. On connaît les attributs qui déterminent l'objet référencé (c'est-à-dire les attributs référentiels) et on sait quelle valeur leur affecter quand le champ est vide.

Prenons l'exemple des deux jointures objet J emp → dept et J person → vehicle que nous avons identifiées. Toutes deux respectent entièrement les restrictions imposées aux jointures objet strictes. Les seuls attributs des relations destinations qui participent sont respectivement dept.deptno et vehicle.vnum, chacun étant la clé de sa relation. Ils sont par ailleurs comparés par l'égalité à deux attributs emp.deptn et person.car issus de deux relations élémentaires de la classe Employé. L'intérêt des restrictions apparaît ici clairement : la consultation de chacun de ces deux attributs référentiels suffit pour déterminer le contenu des champs objet correspondants.

On constate que l'absence de valeur ne coïncide plus tout-à-fait avec l'échec de la jointure objet, comme nous l'avions dit en première analyse. Si tout champ objet vide se traduit effectivement en termes relationnels par un échec de jointure, l'inverse est faux; il est des cas d'échec qui ne correspondent pas à une absence de valeur dans un champ objet.

En effet, si tous les attributs référentiels possèdent une valeur, la référence de l'objet est considérée comme valide. Si, par ailleurs, la valeur de clé ne correspond à aucun n-uplet dans la relation principale jointe (raison d'échec de la jointure), l'existence de l'objet relationnel associé n'est pas remise en cause. Il est alors seulement annoncé manquant lors de toute opération visant à le charger ou à accéder à ses données (cf. §4.1.1.3).

Les restrictions permettent également une plus grande efficacité du système. L'algorithme d'accès aux données dans la base minimise le nombre de requêtes adressées au SGBD car elles sont extrêmement coûteuses en temps. Cet algorithme permet de charger en mémoire un nombre quelconque d'objets d'une même classe principale par l'envoi de

<footnote n="4">⁴NULL est la valeur “système” qui représente l’absence de valeur dans une base de données relationnelle.</footnote>
1 + *N* requêtes au SGBD, *N* étant le nombre de champs de type "ensemble" et "calcul multi-n-uplets" définis dans la classe (cf. §4.1.2). Sans les restrictions imposées aux jointures objet strictes, le coût de chargement serait de 1 + *N* + *M* requêtes, *M* étant le nombre de champs de type "objet" définis dans la classe (cf. §4.1.4.3).

Une requête supplémentaire serait nécessaire par champ objet. En effet, la jointure objet échoue quand le champ est vide. Comme, dans le résultat d'une requête, l'échec d'une jointure se solde par l'absence des n-uplets pour lequel cet échec s'est produit, la requête de chargement d'un groupe d'objets ne pourrait en aucun cas comprendre les objets relationnels ayant un champ objet vide.

Du fait des restrictions, les identités des objets contenus peuvent être obtenues dans le cadre de la requête principale de chargement. L'échec de la jointure objet au sein de cette requête est évité en supprimant la liaison faible qu'elle comprend. Pour connaître l'identité des objets référencés, il n'est plus nécessaire d'interroger le SGBD sur des valeurs de clé à chercher dans la relation principale associée à la classe de ces objets, vérifiant la jointure objet en liaison avec telle autre relation principale correspondant à la classe possédant le champ. On se limite à demander simplement la valeur des attributs référentiels puisqu'ils contiennent directement les références des objets qui nous intéressent. Ces attributs sont en plus toujours accessibles puisqu'ils appartiennent à des relations élémentaires de la classe possédant le champ. La sous-partie de la jointure objet qui les lie à la relation principale n'est pas autorisée à échouer.

Les restrictions imposées limitent objectivement les possibilités de jointure objet. Par exemple, une autre expression pour la jointure $J[person→vehicle]$ comme :

person.car = vehicle.vnum and vehicle.year >= 1990

n'est pas autorisée. Ces restrictions ne gêneront cependant qu'un nombre très restreint d'utilisateurs : la plupart des jointures objet identifiables directement dans une base les respectent de façon naturelle. À l'inverse, elles permettent de définir une correspondance relationnelle claire pour l'absence de valeur dans un champ objet, valable aussi bien en termes de lecture que d'écriture de données dans la base. Elles rendent possible la gestion des objets manquants et offre une efficacité maximale au mécanisme de communication et de dialogue avec le SGBD.

## Des liens entre objets plus ou moins forts

Dans les modèles objet, le champ de type objet permet de représenter un lien entre deux objets. Seulement, cette représentation ne donne aucune information sur la force de ce lien. Par exemple, il n'est pas possible d'imposer qu'un objet référencé dans un champ objet soit la seule valeur possible pour ce champ.