WWW Emi Ac Ma Ntounsi COURS DB Oracle Oracleobjets HTML PDF
WWW Emi Ac Ma Ntounsi COURS DB Oracle Oracleobjets HTML PDF
WWW Emi Ac Ma Ntounsi COURS DB Oracle Oracleobjets HTML PDF
3e année Informatique
Najib Tounsi
Sommaire
En Oracle les objets définis par l'utilisateur peuvent être utilisés principalement pour définir des colonnes de table avec des valeurs non classiques
comme une image, ou des éléments (lignes) de table. Oracle parle de Type au lieu de Classe.
Un type OBJECT est un type de donnée défini par l'utilisateur et qu'on peut utiliser comme les autres types Oracle. Il est défini par la commande
CREATE TYPE.
Une adresse est composée d'un n° dans une rue dans une ville.
On peut munir cet objet de méthodes spécifiques appelées member. On rajoute leur déclaration à la liste des champs.
Ici, on a muni les objets d'une fonction d'accès getNo qui retourne le n° dans la rue et une méthode display pour imprimer une adresse (MAP sera
expliqué plus bas).
SET SERVEROUTPUT ON
DECLARE
maison Adresse;
BEGIN
maison := Adresse(2, 'Atlas', 'Rabat');
dbms_output.put_line('Maison No: '|| TO_CHAR(maison.no));
dbms_output.put_line('Rue: '|| maison.rue);
dbms_output.put_line('Ville: '|| maison.ville);
END;
où on peut noter l'instanciation Adresse(2, 'Atlas', 'Rabat'); avec un appel à une fonction implicite adresse () du même nom que le
type d'objet (c'est un constructeur en fait), et des valeurs énumérées comme dans un insert into de SQL.
Résultat de ce programme :
SQL> /
Maison No: 2
Rue: Atlas
Ville: Rabat
DECLARE
maison Adresse;
BEGIN
maison := NEW Adresse(2, 'Atlas', 'Rabat');
maison.display(); /* Invocation de méthode sur objet */
END;
NB. Pour que ce programme marche, il faut, bien sûr, implémenter la méthode display (voir § ci-après).
SQL> /
Maison No: 2
Rue: Atlas
Ville: Rabat
qui crée le type ou le remplace s'il existe (car sinon, il y a erreur). La commande
Forme simple :
END;
END;
/
Noter l'usage direct des noms de champs de l'objet, appelé SELF en Oracle, sur qui la méthode est appelée. L'écriture no, rue, ville est
équivalente à self.no, self.rue, self.ville .
cette syntaxe permet au paramètre SELF de passer par référence au lieu de passer par valeur (qui est le mode IN OUT par défaut). Ce qui sied à un
objet large utilisé en consultation. Exercice : le vérifier (modifier l'objet dans la procédure). NB. Utiliser la même déclaration dans le BODY (sans
variable paramètre).
Exercices :
Une méthode déclarée MAP sert pour classer et déterminer si deux objets sont les mêmes; en utilisant les valeurs d'attribut des objets par exemple,.
Si on écrit Obj1 >= Obj2 cela revient, dans notre cas, à utiliser la méthode getNo déclarée MAP pour tester si Obj1.getNo() >=
Obj2.getNo() . La valeur de cette fonction dépend des attributs d'un objet, par exemple le n° dans la rue dans notre exemple.
Le programme suivant :
DECLARE
maison Adresse;
maison2 Adresse;
BEGIN
maison := Adresse(2, 'Atlas', 'Rabat');
maison2 := Adresse(3, 'Atlas', 'Casa');
IF (maison > maison2) THEN maison.display();
ELSE maison2.display();
END IF;
END;
/
Une méthode MAP détermine un ordre sur les objets. Elle est appelée automatiquement dans les requêtes qui nécessitent un ordre, comme par
exemple avec DISTINCT, ORDER BY ou GROUP BY de SQL. (cf. exemple plus bas)
Une fonction membre déclarée ORDER permet, quant à elle, de dire si un objet est plus grand, égale ou plus petit qu'un autre, selon un critère
définie par la méthode elle même. Par exemple :
1.3. Constructeurs.
Un constructeur est introduit par le mot clé CONSTRUCTOR. Un constructeur pour le type Adresse serait :
Noter le résultat SELF qui est donc du type de l'objet à construire. Ici, on a choisi une adresse a (déjà créée) comme paramètre pour
l'instanciation du nouvel objet. Voici le corps pour cette méthode :
Exercice : définir un autre constructeur à partir des trois données élémentaires, numéro, rue et ville. Tester.
Instanciation et utilisation :
DECLARE
moi Personne;
BEGIN
moi := Personne (124, 'Untel', Adresse(2, 'Atlas', 'Rabat')); (1)
dbms_output.put_line('Mon nom: ' || moi.nom);
dbms_output.put_line('Ma ville: '|| moi.adr.ville); (3)
moi.display;
END;
/
A ce niveau, on utilise des objets en mémoire comme en programmation Java par exemple. Il n'y a pas encore de conservation de données dans une
BD quelconque. Noter l'instanciation par constructeurs imbriqués (1) et l'accès en cascade (3).
Résultat du programme :
Exercice : Reprendre ce programme un imprimant l'adresse complète (utiliser la méthode display de Adresse.)
En générale les objets Oracle sont à utiliser avec des relations. Soit comme type d'attribut, c'est à dire un objet est une valeur dans une colonne, soit
comme relation-objet, c'est à dire une "ligne" dans une table. La table est alors un ensemble d'objets.
Ici, c'est une table SQL normale. Seul diffère un type de champ qui est non primitif.
Instanciation :
où on constate donc que pour un champ non primitif on fait appel au constructeur.
Consultation :
Résultat:
où la notation SQL est la même (p.adr) mais la valeur adresse apparaît sous forme d'expression constructeur.
(NB. Pour avoir un format de colonnes lisible aisément, utiliser la directive column de SqlPlus :
SQL> column code format 99999
SQL> column nom format a20
SQL> column adr format a50
par exemple)
NOM P.ADR.GETNO()
-------------------- -------------
Untel 56
SELECT p.nom
FROM Personnes p
WHERE p.adr.ville='Rabat';
NOM
--------------------
Untel
Untel2
d) ORDER BY sur l'attribut objet (se rappeler qu'il y a une méthode MAP définie dessus)
où on voit que la fonction MAP getNo() a joué le rôle d'ordre sur les adresses selon le champ NO de l'adresse.
Exercices:
a. Vérifier que ORDER BY p.adr.getNo() donne le même résultat. Même sans la clause MAP (qui, en l'occurrence, permet la syntaxe
SQL normale ORDER BY p.adr ).
b. Même requête, mais en ordre décroissant des noms de rue.
2.3. Objet comme schéma de relation (ou comme ligne dans une table)
On reprend le type d'objets personne précédent (exemple-4 ci-dessus), mais avec une adresse de type simple pour le moment.
Instanciation
Consultation
SELECT p.nom
FROM Personnes p
WHERE p.ville = 'Rabat';
NOM
------------------------------
Untel
Ici, le SELECT est "surchargé" pour s'appliquer à un champ d'objet au lieu d'attribut de table. L'objet p est considéré comme un tuple.
Exercices : Rajouter d'autres tuples (Oups! objets) à cette table. Essayer d'autres requêtes de votre choix.
SELECT VALUE(p)
FROM Personnes p;
Noter le schéma d'affichage du résultat comme type d'objet. Noter aussi la notation constructeur utilisée pour l'affichage des valeurs. (Voir
requête b').
SELECT VALUE(p).getNom()
FROM Personnes p
WHERE p.ville = 'Rabat';
VALUE(P).GETNOM()
----------------------------------
Untel
Ici, on appelle une méthode sur « l'objet-tuple » résultat. Noter encore le schéma du résultat et le type de valeurs afichées.
SELECT *
FROM Personnes p;
DECLARE
moi Personne;
xnom varchar2(20);
BEGIN -- affiche les détails d'une Personne et
SELECT VALUE(p) INTO moi
FROM Personnes p
WHERE VALUE(p).code = 123;
moi.display();
SELECT VALUE(p).nom INTO xnom
FROM Personnes p
WHERE VALUE(p).code = 123;
dbms_output.put_line('Nom = '|| xnom);
END;
/
Code: 123
nom: Untel
Ville: Rabat
Nom = Untel
1. comme une table à colonne unique où chaque ligne est donc un objet personne auquel on peut appliquer les méthodes définies
(requête b), on utilise VALUE() pour cela, ou bien
2. comme une table où chaque colonne est un des composants (code, nom, ville) de l'objet personne, et sur laquelle on applique les
opérations SQL (requête b').
La différence entre (1) la table Personnes définie sur l'objet Personne, et (2) une table définie directement par un schéma (code, nom,
ville), c'est que dans le premier cas on peut appliquer des méthodes utilisateurs display, getNom, aux "tuples" de la première table. Cela
est utile quand on a des applications PL/SQL avec des objets comme ceux de Java, Python ou C++.
La fonction VALUE() convertit une ligne vers une instance d'objet pour accéder aux membres d'un objet : VALUE(ligne).membre.
Exercices : Insérer d'autres lignes. Exécuter les requêtes suivantes et les comparer. Quel est leur résultat?
select *
from personnes p
order by 1 desc;
SELECT VALUE(p)
FROM Personnes p
order by 1 desc;
SELECT VALUE(p).getNom()
FROM Personnes p
order by 1 desc;
SELECT VALUE(p)
FROM Personnes p
order by nom desc;
La deuxième requête donne l'erreur : "ORA-22950: cannot ORDER objects without MAP or ORDER method". Pourquoi?
Comment réparer pour que cette même requête marche?
INSERT INTO Personnes VALUES (Personne (123, 'Untel', Adresse(2, 'Atlas', 'Rabat')));
Consultation
a)
b)
SELECT p.nom
FROM Personnes p
WHERE p.adr.ville = 'Rabat';
NOM
------------------------------
Untel
Un instance de ce type, par exemple un carré qui est une liste de quatre coins, a la forme:
NB. La table de Point n'est pas accessible tel que. D'ailleurs elle n'a pas de nom pour être interrogée. Polygone est un nom de type.
L'intérêt d'un tel objet relation est dans la création de relations imbriquées.
On peut maintenant créer une relation Figures (nom, coins) pour des figures géométriques du type polygonal, avec le nom de figure, carré, ligne,
etc. et la liste des coins; laquelle liste est définie comme relation imbriquée Polygone. Voir figure.
Exemple-7:
nom coins
Carré x y
0 0
0 1
1 1
1 0
x y
ligne 0 0
2 1
... ...
Dans chaque ligne, la valeur de colonne coins est une relation polygone à part.
Cette clause optionnelle NESTED TABLE indique que les différentes (petites) relations imbriquées ne sont pas à stocker individuellement comme
valeur de l'attribut coins, mais en totalité dans une seule relation, appelée à l'occasion TableDePoints. Relation qui ne peut être accessible
telle qu'elle (voir aussi la requête (d) ci-après) . Cette relation stocke donc des objets de type Polygone.
2.4.2. Utilisation
a) Insertion
b) Affichage de la table
SELECT *
FROM Figures;
NOM COINS(X, Y)
--------------- -----------------------------------
ligne POLYGONE(POINT(0, 0), POINT(2, 1))
carre POLYGONE(POINT(0, 0), POINT(0, 1),
POINT(1, 1), POINT(1, 0))
SELECT coins
FROM figures
WHERE nom='ligne';
COINS(X, Y)
-----------------------------------------------------
POLYGONE(POINT(0, 0), POINT(2, 1))
Noter ici que les valeurs du champ coins retournés sont des objets polygones (et non points).
d) Pour accéder aux valeurs x et y donc, il faut préciser la relation imbriquée désirée dans la clause FROM. Le mot clé THE permet cela en
désignant la relation (imbriquée) comme résultat d'une sous-requête SELECT.
SELECT x, y
FROM THE ( SELECT coins
FROM figures
WHERE nom='carre')
Le SELECT interne cherche "la relations ensemble des coins" d'un carré, pour ensuite en afficher les valeurs x et y par le SELECT externe.
X Y
---------- ----------
0 0
0 1
1 1
1 0
Il faut noter ici que le résultat du SELECT interne doit être unique (mono-tuple), c'est à dire une seule "valeur" relation imbriquée. Sans clause
WHERE ici ou si il y avaient plus d'un carré, cela provoquerait une erreur.
La relation imbriquée (coins d'une ligne) est nommée lg pour ensuite être interrogée normalement par le SELECT externe. Résultat :
X Y
---------- ----------
2 1
Résultat :
SELECT x, y
FROM THE ( SELECT coins
FROM figures
WHERE nom='carre');
X Y
---------- ----------
1 0
0 1
1 1
2 0
e) Usage de TABLE()...
A TABLE expression enables you to query a collection in the FROM clause like a table. In effect, you join the nested table with the row
that contains the nested table.
SELECT nom, x, y
FROM figures, TABLE ( figures.coins);
NOM X Y
--------------- ---------- ----------
ligne 0 0
ligne 2 1
carre 0 0
carre 0 1
carre 1 1
carre 1 0
6 rows selected.
La clause FROM contient la relation figures pour chercher le nom, et TABLE(figures.coins) pour avoir la relation imbriquée dans le
même tuple.
TABLE sert à "aplatir" l'attribut coins dans la table figures pour permettre ensuite d'interroger ses données. La table (ou l'alias) figures
doit apparaître à gauche (i.e. avant) de l'expression TABLE. "Left correlation".
SELECT nom, x, y
FROM figures, TABLE ( figures.coins)
WHERE nom='ligne';
NOM X Y
--------------- ---------- ----------
ligne 0 0
ligne 2 1
TABLE sert à "aplatir" l'attribut coins dans la table figures pour permettre ensuite d'interroger ses données. La table (ou l'alias) figures
doit apparaître à gauche (i.e. avant) de l'expression TABLE. "Left correlation".
SELECT nom
FROM figures, TABLE ( figures.coins)
where x = 0 and y = 0;
NOM
---------------
ligne
carre
Devoir à faire: Imaginer un schéma de BD avec des clients qui ont fait des commandes (voir cours 1ère année, relation non normalisée).
1 M
Clients <-------- Fait ----------> Commandes
Considérer deux cas : (1) une table clients avec pour chacun la liste des commandes (sous forme de table imbriquée), ou (2) deux tables
commandes et clients où chaque commande fait référence au client (voir aussi §2.6 ci-après).
Comparer les deux cas en faisant quelques requêtes, telles que : « le nom du client ayant commandé un produit donné », ou « le total des prix de la
commande d'un client donné ».
Il y a aussi la possibilité plus simple que le cas 2.4 où un domaine est de type vecteurs de valeurs (pas forcément scalaires d'ailleurs). Le type
Oracle prédéfini VARRAY permet cela. Par exemple :
Un identifiant d'objet est appelé OID. Chaque objet ligne dans une table d'objets (§ 2.3) comporte un identifiant d'objet (OID) associé. Oracle
attribue par défaut un identifiant unique généré par le système en tant qu'OID, pour chaque objet stocké comme "ligne" dans une table d'objets.
Une valeur de l'attribut chef dans cette table est une référence (pointeur) vers un objet Personne.
Un objet référencé ainsi doit être un tuple dans une relation comme personnes ci-dessus. Et c'est le seul usage possible d'une référence.
Une référence ne peut donc pas pointer vers une colonne objet d'une table. Pas plus que vers un objet indépendant créé à cet effet.
Exemple-9: Pour ajouter une ligne dans la table Departements, on peut écrire:
qui insère le département Informatique avec comme chef une référence à "Untel", calculée par REF(p) où p est la ligne désirée de la table
Personnes.
Après le SELECT imbriqué, le système génère un OID pour la tuple p trouvé, et le met comme valeur dans chef. On peut le constater avec la
requête:
NOM CHEF
---------------- ---------------------------------------------------------------------------
Informatique 00002202080FADD5A1C0614C8B9CD2773D8A7EF04865CA185E26644911AA00793E3AAFE722
Noter enfin, que créer une instance quelconque pour l'affecter à un objet REF , comme dans:
aurait été une erreur, car la référence se fait vers un objet qui n'est pas un tuple dans une table (ORA-00932: types de données
incohérents).
Exercice: faire la même insertion en utilisant UPDATE. (indication: Insérer au préalable le tuple ('informatique', NULL) et ensuite
remplacer la valeur NULL par l'objet qu'il faut.)
Exemple-9-bis: Pour suivre une référence et accéder à un de ses composants (ex. chef.nom), on utilise aussi la notation classique comme si
l'attribut n'était pas une référence (la déréférence est automatique)
NOM CHEF.NOM
-------------------- ------------------------------
Informatique Untel
Par contre, pour l'attribut chef en tant qu'objet, il faut utiliser l'opérateur DEREF pour déréférencer le pointeur (sinon, on obtient l'OID comme
exemple-9 ci-dessus)
Noter le REF(p)!
NOM CHEF
------------------- ---------------------------------------------------------------------------
Informatique 00002202080FADD5A1C0614C8B9CD2773D8A7EF04865CA185E26644911AA00793E3AAFE722
Mecanique 00002202089E09671DCA464C7B902A03146C283AC165CA185E26644911AA00793E3AAFE722
Exemple-11:
SQL> DECLARE
2 moi Personne;
3 ref_moi ref Personne;
4 xnom varchar2(20);
5
6 BEGIN
7 SELECT d.chef.nom INTO xnom
8 FROM Departements d
9 WHERE d.nom ='Mecanique';
10 dbms_output.put_line('Nom = '|| xnom);
11
12 SELECT d.chef INTO ref_moi
13 FROM Departements d
14 WHERE d.nom ='Mecanique';
15
16 SELECT deref(ref_moi) into moi FROM dual;
17 moi.display;
18
19 END;
20 /
Nom = Untel2
Code: 124
nom: Untel2
Ville: Rabat
SQL>
A la ligne 7, on a un SELECT mono-tuple qui suit l'attribut chef sans déréférence. Comme dans l'exemple-9.
A la ligne 12, on a un SELECT mono-tuple sur l'attribut chef pour l'affecter à la variable ref_moi.
Pour appliquer la méthode display à ce chef (ligne 17), il faut déréférencer la variable ref_moi. C'est l'objet du SELECT de la ligne 16, qui
affecte le résultat à la variable moi.
Les vues objets permettent l'utilisation de données relationnelles dans les applications orientées objets. Elles permettent de voir des données
relationnelles existantes dans un modèle objet-relationnel.
Tout comme une vue est une table virtuelle, une vue objet est une table d'objets virtuels.
Exemple-12: On reprend la table Employee ( enum, ename, salary, address, dept) de BD Store (cf
http://www.emi.ac.ma/~ntounsi/COURS/DB/Polys/SQL/SQL-LMD.html ).
On va d'abord créer un type d'objets Employee_type , et ensuite on va définir une table d'objets virtuels de ce type.
a) Création du type et de son implémentation
CREATE OR REPLACE TYPE Employee_type AS OBJECT (
enum varchar (5) ,
ename varchar (20),
salary decimal (9,2),
address varchar(10),
dept varchar (5),
MEMBER PROCEDURE display
);
/
Noter la construction de l'identificateur d'objet sur la base de la clé spécifiée (enum), qui est la clé primaire de la table de base.
c) Interrogation en SQL
SQL> select * from empview;
SQL>
d) Utilisation en PL/SQL
SQL> DECLARE
2 emp employee_type;
3 BEGIN
4 SELECT value(e) INTO emp
5 FROM empview e
6 WHERE e.enum='E6';
7 emp.display;
8 end;
9 /
Num Employee: E6
Nom: Aziz
Salaire: 8500
Adresse: Casa
SQL>
Les objets virtuels ne sont pas forcément composés des mêmes champs que la table de base d'origine. On aurait pu définir le type
Employee_type ci-dessus uniquement sur les champs nom et adresse d'un employé.
3. En Résumé
Résumé de cette approche Objet/Relationnel.
Type created.
SQL>
SQL>
SQL>
SQL> DECLARE
2 maison Adresse; m Adresse;
3 BEGIN
4 maison := Adresse(2, 'Atlas', 'Rabat');
5 maison.display();
6
7 maison.changeRueAdresse ('Rif');
8 maison.display();
9
10 m := Adresse(3, 'Rif', 'Casa');
11 maison.changeAdresse (m);
12 maison.display();
13
14 m.changeRueAdresse('Alpes');
15 maison := Adresse(m); /* vs. maison.Adresse(m) */
16 maison.display();
17 END;
18 /
Maison No: 2
Rue: Atlas
Ville: Rabat
Maison No: 2
Rue: Rif
Ville: Rabat
Maison No: 3
Rue: Rif
Ville: Casa
Maison No: 3
Rue: Alpes
Ville: Casa
SQL>