L’approche objet

L'extension à l'objet du modèle relationnel prend en charge les types abstraits de données qui sont définis à partir d'une structure de données et d'un ensemble d'opérations. La syntaxe de création d’un type d'objet comporte la déclaration de la structure de données, ses méthodes et la partie qui positionne le type dans une hiérarchie d'héritage.

en savoir

CREATE [OR REPLACE] TYPE [SCHEMA.]NOM_TYPE

   {{IS|AS} OBJECT | UNDER [SCHEMA.]SUPERTYPE }

 AS OBJECT

 ( NOM_ATTRIBUT TYPE [,...]

    { {MEMBER|STATIC}

    { PROCEDURE name (NOM_ARGUMENT TYPE [,...])

     | FUNCTION  name (NOM_ARGUMENT TYPE [,...]) RETURN datatype }

     | CONSTRUCTOR FUNCTION NOM_TYPE

         [([SELF IN OUT NOM_TYPE] [,NOM_ARGUMENT datatype[,...]])]

               RETURN SELF AS RESULT})[ [ NOT ] FINAL ];

Lors de la création d'un type, il n'est pas possible de déclarer des attributs de type « BOOLEAN », « ROWID », « LONG », « LONG RAW » ou d'utiliser la directive « %TYPE » pour la définition de leur type.

Il n'est pas possible de définir dynamiquement des types dans des programmes PL/SQL.

SQL> CREATE OR REPLACE TYPE T_TELEPHONE IS OBJECT(

  2        TELEPHONE         VARCHAR2(24),

  3        FAX               VARCHAR2(24),

  4        MAIL              VARCHAR2(24));

  5  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE T_ADRESSE IS OBJECT(

  2        ADRESSE           NVARCHAR2(60),

  3        VILLE             VARCHAR2(30),

  4        CODE_POSTAL       VARCHAR2(10),

  5        PAYS              VARCHAR2(15),

  6        TELEPHONE         T_TELEPHONE);

  7  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE T_PERSONNE IS OBJECT(

  2        NOM               NVARCHAR2(40),

  3        PRENOM            NVARCHAR2(30),

  4        DATE_NAISSANCE    DATE,

  5        ADRESSE           T_ADRESSE);

  6  /

 

Type créé.

 

SQL> DESC T_TELEPHONE

 Nom                                       NULL ?   Type

 ----------------------------------------- -------- --------------

 TELEPHONE                                          VARCHAR2(24)

 FAX                                                VARCHAR2(24)

 MAIL                                               VARCHAR2(24)

 

SQL> DESC T_ADRESSE

 Nom                                       NULL ?   Type

 ----------------------------------------- -------- --------------

 ADRESSE                                            NVARCHAR2(60)

 VILLE                                              VARCHAR2(30)

 CODE_POSTAL                                        VARCHAR2(10)

 PAYS                                               VARCHAR2(15)

 TELEPHONE                                          T_TELEPHONE

 

SQL> DESC T_PERSONNE

 Nom                                       NULL ?   Type

 ----------------------------------------- -------- --------------

 NOM                                                NVARCHAR2(40)

 PRENOM                                             NVARCHAR2(30)

 DATE_NAISSANCE                                     DATE

 ADRESSE                                            T_ADRESSE

Dans l’exemple ci-dessus, vous pouvez voir la création de trois types d’objets « T_PERSONNE », « T_ADRESSE » et « T_TELEPHONE ». La classe « T_PERSONNE » est composée d’une classe adresse « T_ADRESSE » qui à son tour est composée d’une classe « T_TELEPHONE ».

Initialisation d'objets

Comme toute autre variable PL/SQL, un objet est déclaré simplement en le plaçant syntaxiquement après son type dans la section déclarative du bloc, comme cela :

NOM_VARIABLE_INSTANCE NOM_TYPE_OBJET;

Selon les règles du PL/SQL, une instance d'objet déclarée de cette manière est initialisée avec la valeur « NULL », auquel cas l'objet entier est « NULL », mais pas nécessairement ses attributs. Il est toutefois illégal de se référer aux attributs de l'objet.

SQL> DECLARE

  2    personne T_PERSONNE;

  3  BEGIN

  4       DBMS_OUTPUT.PUT_LINE('Le nom est :'||personne.NOM);

  5  END;

  6  /

Le nom est :

 

Procédure PL/SQL terminée avec succès.

 

SQL> DECLARE personne T_PERSONNE; BEGIN personne.NOM := 'BIZOÏ'; END;

  2  /

DECLARE

*

ERREUR à la ligne 1 :

ORA-06530: Référence à un élément composite non initialisé de

ORA-06512: à ligne 4

Instanciation d'objets

Les objets sont instanciés avec un constructeur. Un constructeur est une fonction qui retourne un objet initialisé et reçoit comme arguments les valeurs des attributs de l'objet. Pour chaque type d'objet, Oracle prédéfinit un constructeur par défaut avec le même nom que le type et admet en argument autant de valeurs que le type possède d’attributs.

SQL> DECLARE

  2    personne1 T_PERSONNE;

  3    personne2 T_PERSONNE := T_PERSONNE( 'BIZOÏ','Isabelle',

  4                                         '14/10/1965',NULL);

  5  BEGIN

  6    personne1 := T_PERSONNE( 'BIZOÏ','Razvan','03/02/1965',NULL);

  7    DBMS_OUTPUT.PUT_LINE('Le prénom est :'||personne1.PRENOM);

  8    DBMS_OUTPUT.PUT_LINE('Le prénom est :'||personne2.PRENOM);

  9  END;

 10  /

Le prénom est :Razvan

Le prénom est :Isabelle

Les instances peuvent avoir les mêmes valeurs pour les attributs ne sont pas pour autant le même objet. L'objet est une unité atomique formée de l'union d'un état, d'un comportement et d’une identité.

SQL> DECLARE

  2    personne1 T_PERSONNE;

  3  BEGIN

  4      DECLARE

  5        personne2 T_PERSONNE := T_PERSONNE( 'BIZOÏ','Isabelle',

  6                                             '14/10/1965',NULL);

  7      BEGIN

  8        personne1 := personne2;

  9        personne2.NOM := 'DULUC';

 10        DBMS_OUTPUT.PUT_LINE('Le nom est :'||personne2.NOM);

 11      END;

 12      DBMS_OUTPUT.PUT_LINE('Le nom est :'||personne1.NOM);

 13  END;

 14  /

Le nom est :DULUC

Le nom est :BIZOÏ

Méthodes des types d’objets

Le corps du type d’objet est optionnel. Lorsque vous déclarez le type d’objet, vous ne déclarez aucune procédure ni fonction. Le corps peut être omis.

La syntaxe de création d’un corps du type d’objet est la suivante :

CREATE [OR REPLACE] TYPE BODY NOM_TYPE {IS | AS}

  [Spécifications et corps des modules]

END [NOM_TYPE];

SQL> CREATE OR REPLACE TYPE type_personne AS OBJECT (

  2      NOM                VARCHAR2(20),

  3      PRENOM             VARCHAR2(10),

  4      DATE_NAISSANCE     DATE,

  5      CONSTRUCTOR FUNCTION type_personne( NOM  IN VARCHAR2,

  6                                          DATE_NAISSANCE IN DATE)

  7                           RETURN SELF AS RESULT,

  8      CONSTRUCTOR FUNCTION type_personne( NOM  IN VARCHAR2)

  9                           RETURN SELF AS RESULT,

 10      MEMBER FUNCTION age_pers RETURN NUMBER);

 11  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE BODY type_personne

  2  AS

  3      CONSTRUCTOR FUNCTION type_personne( NOM  IN VARCHAR2,

  4                                          DATE_NAISSANCE IN DATE)

  5      RETURN SELF AS RESULT IS

  6      BEGIN

  7          SELF.NOM := NOM;

  8          SELF.DATE_NAISSANCE := DATE_NAISSANCE;

  9          RETURN;

 10      END;

 11      CONSTRUCTOR FUNCTION type_personne( NOM  IN VARCHAR2)

 12      RETURN SELF AS RESULT IS

 13      BEGIN

 14          SELF.NOM := NOM;   

 15          RETURN;

 16      END;

 17      MEMBER FUNCTION age_pers

 18      RETURN NUMBER IS

 19          age NUMBER(3);

 20      BEGIN

 21          IF SELF.DATE_NAISSANCE IS NOT NULL THEN

 22            age := trunc(( sysdate - SELF.DATE_NAISSANCE) / 365);

 23            RETURN age;

 24          ELSE

 25            RETURN 0;

 26          END IF;

 27      END age_pers;

 28  END;

 29  /

 

Corps de type créé.

 

SQL> DECLARE

  2    personne1 type_personne:=type_personne('BIZOÏ');

  3    personne2 type_personne:=type_personne('BIZOÏ','03/02/1965');

  4    personne3 type_personne:=type_personne('BIZOÏ','Razvan',

  5                                           '03/02/1965');

  6  BEGIN

  7    DBMS_OUTPUT.PUT_LINE(personne1.NOM||' '||personne1.PRENOM||

  8                         ' '||personne1.age_pers());

  9    DBMS_OUTPUT.PUT_LINE(personne2.NOM||' '||personne2.PRENOM||

 10                         ' '||personne2.age_pers());

 11    DBMS_OUTPUT.PUT_LINE(personne3.NOM||' '||personne3.PRENOM||

 12                         ' '||personne3.age_pers());

 13  END;

 14  /

BIZOÏ  0

BIZOÏ  41

BIZOÏ Razvan 41

Dans l’exemple précédent, vous pouvez voir la création d’un type d’objet avec deux constructeurs personnalisés et une fonction qui calcule l’âge d’une personne. Dans le bloc anonyme, on instancie les objets à l’aide de ces deux constructeurs mais également à l’aide du constructeur par défaut fourni automatiquement par Oracle.

SELF

Le mot-clé « SELF » est automatiquement lié à l'objet instancié dans une méthode. Considérons le constructeur type_personne, cette méthode instancie l'objet à partir d’un ensemble d’arguments. Pour rendre plus clair le code PL/SQL, les arguments doivent avoir le même nom que les attributs ainsi le mot-clé « SELF » est nécessaire pour différencier les arguments et les attributs.

« SELF » est le premier argument de chaque méthode membre et peut être déclaré explicitement ou implicitement. Pour les fonctions membres, il est implicitement déclaré comme étant « IN » et, pour les procédures, il est déclaré comme étant « IN OUT ». Le type de « SELF » est le type de l'objet lui-même. Ce serait une erreur de déclarer « SELF » autrement que comme premier argument, puisqu'il est implicitement déclaré comme tel.

SQL> CREATE OR REPLACE TYPE type_personne

...

 12      CONSTRUCTOR FUNCTION type_personne( PERS IN type_personne)

 13                           RETURN SELF AS RESULT,

...

 15  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE BODY type_personne

  2  AS

...

 17      CONSTRUCTOR FUNCTION type_personne( PERS IN type_personne)

 18      RETURN SELF AS RESULT IS

 19      BEGIN

 20          SELF := PERS;

 21          RETURN;

 22      END;

...

 34  END;

 35  /

 

Corps de type créé.

 

SQL> DECLARE

  2    personne1 type_personne:=type_personne('BIZOÏ','Razvan',

  3                                           '03/02/1965');

  4    personne2 type_personne;

  5  BEGIN

  6    personne2 := type_personne(personne1);

  7    DBMS_OUTPUT.PUT_LINE(personne1.NOM||' '||personne1.PRENOM||

  8                         ' '||personne1.age_pers());

  9    DBMS_OUTPUT.PUT_LINE(personne2.NOM||' '||personne2.PRENOM||

 10                         ' '||personne2.age_pers());

 11  END;

 12  /

BIZOÏ Razvan 41

BIZOÏ Razvan 41

L'attribut « %TYPE » ne peut être appliqué directement à un attribut de type d'objet et doit être appliqué à la place à un attribut d'une instance du type d'objet. Il doit être appliqué à une instance et pas à un type d’objet.

SQL> DECLARE

  2    personne type_personne;

  3    v_pers type_personne%TYPE;

  4  BEGIN

  5     NULL;

  6  END;

  7  /

  v_pers type_personne%TYPE;

         *

ERREUR à la ligne 3 :

ORA-06550: Ligne 3, colonne 10 :

PLS-00206: %TYPE doit être appliqué à une variable, une colonne, un champ ou un attribut, mais pas à "TYPE_PERSONNE"

ORA-06550: Ligne 3, colonne 10 :

PL/SQL: Item ignored

 

 

SQL> DECLARE

  2    personne type_personne;

  3    v_pers personne%TYPE;

  4    v_NOM personne.NOM%TYPE;

  5  BEGIN

  6     v_pers :=type_personne('BIZOÏ','Razvan','03/03/1965');

  7     DBMS_OUTPUT.PUT_LINE( v_pers.NOM||' '||v_pers.PRENOM||

  8                                     ' '||v_pers.age_pers());

  9  END;

 10  /

BIZOÏ Razvan 41

Méthode MAP et ORDER

Les types prédéfinis d'Oracle possèdent tous un ordre par défaut. Dans le cas de deux variables « VARCHAR2 », par exemple, vous pouvez déterminer si l'une est inférieure, supérieure ou égale à l'autre. Sans cela, il ne serait pas possible de trier les valeurs du type de données spécifié. En revanche, les types d'objets ne possèdent pas d'ordre implicite, seule l'égalité des objets peut être comparée.

Les méthodes « MAP » et « ORDER » permettent de remédier à cela. En outre, ces méthodes autorisent la comparaison d'objets non seulement dans PL/SQL, mais aussi dans SQL.

SQL> CREATE OR REPLACE TYPE type_personne

...

 15      MAP   MEMBER FUNCTION cle_personne RETURN VARCHAR2);

 16  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE BODY type_personne

  2  AS

...

 35      MAP   MEMBER FUNCTION cle_personne RETURN VARCHAR2

 36      IS

 37      BEGIN

 38        RETURN SELF.NOM||SELF.PRENOM||

 39               to_char(SELF.DATE_NAISSANCE,'YYYYMMDD');

 40      END cle_personne;

 41  END;

 42  /

 

Corps de type créé.

 

SQL> DECLARE

  2    personne1 type_personne:=type_personne('BIZOÏ','Razvan',

  3                                           '03/02/1965');

  4    personne2 type_personne:=type_personne('BIZOÏ','Razvan',

  5                                           '03/03/1965');

  6  BEGIN

  7    DBMS_OUTPUT.PUT_LINE( 'personne1 '||personne1.cle_personne);

  8    DBMS_OUTPUT.PUT_LINE( 'personne2 '||personne2.cle_personne);

  9    IF personne1 > personne1 THEN

 10       DBMS_OUTPUT.PUT_LINE( 'personne1 > personne2');

 11    ELSE

 12       DBMS_OUTPUT.PUT_LINE( 'personne1 < personne2');

 13    END IF;

 14  END;

 15  /

personne1 BIZOÏRazvan19650203

personne2 BIZOÏRazvan19650303

personne1 < personne2

Vous pouvez créer une méthode « ORDER », qui reçoit un argument du même type que l'objet et retourne un résultat numérique contenant l'une des valeurs suivantes :

> 1 si l’argument est supérieur à « SELF ».

< 1 si l’argument est inférieur à « SELF ».

0 si l’argument est égal à « SELF ».

Nous pouvons, par exemple, créer une méthode « ORDER » qui trie les personnes par leur date de naissance, comme illustré ci-dessous.

SQL> CREATE OR REPLACE TYPE type_personne

...

 15      ORDER MEMBER FUNCTION comparaison_personne

 16                       (aSELF IN type_personne ) RETURN NUMBER);

 17  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE BODY type_personne

  2  AS

...

 35      ORDER MEMBER FUNCTION comparaison_personne(aSELF IN type_personne)

 36      RETURN NUMBER                           

 37      IS

 38      BEGIN

 39       CASE

 40          WHEN aSELF.DATE_NAISSANCE =  SELF.DATE_NAISSANCE THEN

 41               RETURN 0;

 42          WHEN aSELF.DATE_NAISSANCE >  SELF.DATE_NAISSANCE THEN

 43               RETURN 1;

 44          ELSE

 45               RETURN -1;

 46       END CASE;

 47      END comparaison_personne;

 48  END;

 49  /

 

Corps de type créé.

 

SQL> DECLARE

  2  personne1 type_personne:=type_personne('BIZOÏ','Razvan','03/02/1965');

  3  personne2 type_personne:=type_personne('BIZOÏ','Razvan','03/03/1965');

  4  BEGIN 

  5    IF personne1 > personne1 THEN                                          

  6       DBMS_OUTPUT.PUT_LINE( 'personne1 > personne2');

  7    ELSE

  8       DBMS_OUTPUT.PUT_LINE( 'personne1 < personne2');

  9    END IF;

 10  END;

 11  /

personne1 < personne2

Méthodes statiques

Une méthode statique est déclarée avec le mot-clé « STATIC » plutôt qu'avec le mot-clé « MEMBER ». Contrairement aux méthodes non statiques, une méthode statique est invoquée sur le type d'objet lui-même, plutôt que sur une instance de ce type.

La syntaxe utilisée pour l'appel d'une méthode de type « STATIC » est :

nom_type_objet.nom_méthode

SQL> CREATE OR REPLACE TYPE type_personne

...

  4      STATIC PROCEDURE ObtenirInfoClasse);

  5  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE BODY type_personne

  2  AS

  3      STATIC PROCEDURE ObtenirInfoClasse IS

  4      BEGIN DBMS_OUTPUT.PUT_LINE( 'La classe est type_personne'); END;

  5  END;       

  6  /   

 

Corps de type créé.

 

SQL> BEGIN type_personne.ObtenirInfoClasse; END;

  2  / 

La classe est type_personne

Une méthode de type « STATIC » peut être invoquée indépendamment de tout objet instancié, elle a un comportement très proche des fonctions et procédures classiques.

Héritage

Les types d’objets enfants héritent des caractéristiques de leurs types d’objets parents ; les attributs et les opérations déclarés dans le type d’objet parent, sont accessibles dans le type d’objet enfant, comme s'ils avaient été déclarés localement.

SQL> CREATE OR REPLACE TYPE    T_ADRESSE IS OBJECT

  2  (     ADRESSE             NVARCHAR2(60),

  3        VILLE               VARCHAR2(30),

  4        CODE_POSTAL         VARCHAR2(10),

  5        PAYS                VARCHAR2(15));

  6  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE     T_PERSONNE IS OBJECT

  2  (     NOM                  NVARCHAR2(40),

  3        PRENOM               NVARCHAR2(30),

  4        DATE_NAISSANCE       DATE,

  5        ADRESSE              T_ADRESSE) NOT INSTANTIABLE NOT FINAL;

  6  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE    T_EMPLOYE UNDER T_PERSONNE

  2  (       NO_EMPLOYE        NUMBER(6),

  3          REND_COMPTE       NUMBER(6),

  4          FONCTION          VARCHAR2(30),

  5          SALAIRE           NUMBER(8,2) )NOT FINAL;

  6  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE    T_MANAGER UNDER T_EMPLOYE

  2  (       DEPARTEMENT       NVARCHAR2(30),

  3          COMMISSION        NUMBER(8,2) )FINAL;

  4  /

 

Type créé.

Le type T_PERSONNE « a un » type T_ADRESSE, la relation entre ces deux types est une composition. Le type T_EMPLOYE « est un » type T_PERSONNE il s’agit dans ce cas d’un héritage ainsi qu’entre le type T_EMPLOYE et le type T_MANAGER.

Le type T_MANAGER « est un » type T_EMPLOYE mais le type T_EMPLOYE « est un » type T_PERSONNE également. Ainsi le type T_MANAGER « est un » type T_PERSONNE.

SQL> DECLARE

  2      manager T_MANAGER := T_MANAGER( 'BIZOÏ', 'Razvan',

  3          '03/02/1965', T_ADRESSE('44, rue Mélanie','Strasbourg',

  4           67200,'FRANCE'), 1, 1, 'Consultant',2000, 'Formation', 3.5);

  5      employe T_EMPLOYE;                 

  6  BEGIN

  7    employe := manager;

  8    DBMS_OUTPUT.PUT_LINE( employe.NOM||' '||employe.PRENOM||

  9        ' '||employe.DATE_NAISSANCE||' '||employe.FONCTION

 10        ||' '||manager.DEPARTEMENT);

 11  END;                        

 12  /

BIZOÏ Razvan 03/02/65 Consultant Formation

Comme on l’a vu précédemment, le type T_MANAGER « est un » type T_EMPLOYE ainsi l’instance employe peut recevoir les informations correspondantes stockées dans l’instance manager.

La relation entre deux types parent enfant est unidirectionnelle le type enfant « est un » type parent. La réciproque n’est pas valable. Ainsi le type T_MANAGER « est un » type T_EMPLOYE mais le type T_EMPLOYE n’est pas un type T_MANAGER.

SQL> DECLARE

  2      employe T_EMPLOYE := T_EMPLOYE( 'BIZOÏ', 'Razvan',

  3          '03/02/1965', T_ADRESSE('44, rue Mélanie','Strasbourg',

  4                    67200,'FRANCE'), 1, 1, 'Consultant',2000);

  5      manager T_MANAGER ;

  6  BEGIN manager := employe; END;

  7  /

  manager := employe;

             *

ERREUR à la ligne 7 :

ORA-06550: Ligne 7, colonne 14 :

PLS-00382: expression du mauvais type

ORA-06550: Ligne 7, colonne 3 :

PL/SQL: Statement ignored

Les directives « FINAL » et « NOT FINAL » permettent de définir si le type peut être l’ancêtre, pour un autre type. Si un type est définit comme « FINAL » il ne peut plus servir dans un héritage. Il faut faire attention car c’est la valeur par défaut.

SQL> CREATE OR REPLACE TYPE T_DIRECTEUR

  2  UNDER T_MANAGER ( SITE  NVARCHAR2(30) ) FINAL;

  3  /

 

Avertissement : Type créé avec erreurs de compilation.

 

SQL> SHOW ERRORS

Erreurs pour TYPE T_DIRECTEUR :

LINE/COL ERROR

-------- ------------------------------------------------------------

1/1      PLS-00590: tentative de création d'un sous-type sous un type FINAL

Les directives « INSTANTIABLE » et « NOT INSTANTIABLE » renseignent la capacité d'instanciation d'un type. Il est possible de définir un type comme un objet générique, comme une classe abstraite, qui va être spécialisée dans ces enfants.

Le type défini « NOT INSTANTIABLE » ne peut pas servir à créer un objet, il est forcement un type dédié à être un ancêtre. Ainsi un type « NOT INSTANTIABLE » ne peut pas être défini « FINAL ».

Deux catégories de types existent, les types « NOT INSTANTIABLE » similaires aux classes abstraites et les types « INSTANTIABLE » qui sont définis pour créer des objets. La syntaxe de déclaration de ces types est :      … [ NOT ] INSTANTIABLE [ NOT ] FINAL ;

SQL> DECLARE

  2      personne T_PERSONNE := T_PERSONNE( 'BIZOÏ', 'Razvan', NULL, NULL);

  3  BEGIN NULL; END;

  4  /

    personne T_PERSONNE := T_PERSONNE( 'BIZOÏ', 'Razvan',

                           *

ERREUR à la ligne 2 :

ORA-06550: Ligne 2, colonne 28 :

PLS-00713: tentative d'instanciation d'un type NOT INSTANTIABLE

ORA-06550: Ligne 2, colonne 14 :

PL/SQL: Item ignored

Le type T_PERSONNE est un type générique, il est déclaré comme « NOT INSTANTIABLE » ainsi il ne peut pas être utilisé pour la déclaration des variables qui sont instanciées.

Redéfinition des méthodes

Il est possible de redéfinir dans un type enfant, une méthode héritée. C’est une forme de surcharge d’une méthode qui permet d’adapter le traitement aux spécificités des enfants.

La méthode redéfinie doit conserver le nom, le nombre et type des arguments dans chaque type enfant où elle apparaît. Dans la définition du type, la directive « OVERRIDING » précède la déclaration de la méthode redéfinie.

SQL> CREATE OR REPLACE TYPE    T_PERSONNE IS OBJECT

  2    (   NOM               NVARCHAR2(40),

  3        PRENOM            NVARCHAR2(30),

  4        DATE_NAISSANCE    DATE,

  5        ADRESSE           T_ADRESSE)NOT INSTANTIABLE NOT FINAL;

  6 

  7  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE    T_EMPLOYE UNDER T_PERSONNE

  2  (     NO_EMPLOYE        NUMBER(6),

  3        REND_COMPTE       NUMBER(6),

  4        FONCTION          VARCHAR2(30),

  5        SALAIRE           NUMBER(8,2),

  6        MEMBER FUNCTION revenu RETURN NUMBER,

  7        MEMBER FUNCTION InfoClasse RETURN VARCHAR2)NOT FINAL;

  8  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE    T_MANAGER UNDER T_EMPLOYE

  2  (     DEPARTEMENT       NVARCHAR2(30),

  3        COMMISSION        NUMBER(8,2),

  4        OVERRIDING MEMBER FUNCTION revenu RETURN NUMBER,

  5        OVERRIDING MEMBER FUNCTION InfoClasse RETURN VARCHAR2)NOT FINAL;

  6  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE    T_DIRECTEUR UNDER T_MANAGER

  2  (     SITE              NVARCHAR2(30) ,

  3        OVERRIDING MEMBER FUNCTION InfoClasse RETURN VARCHAR2)FINAL;

  4  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE BODY T_EMPLOYE

  2  AS

  3      MEMBER FUNCTION revenu RETURN NUMBER IS

  4      BEGIN

  5          RETURN NVL(SALAIRE,0);

  6      END revenu;

  7      MEMBER FUNCTION InfoClasse RETURN VARCHAR2 IS

  8      BEGIN

  9          RETURN 'Le type est T_EMPLOYE';

 10      END InfoClasse;

 11  END;

 12  /

 

Corps de type créé.

 

SQL> CREATE OR REPLACE TYPE BODY T_MANAGER

  2  AS

  3      OVERRIDING MEMBER FUNCTION revenu RETURN NUMBER IS

  4      BEGIN

  5        RETURN NVL(SALAIRE,0)+(NVL(SALAIRE,0)*NVL(COMMISSION,0));

  6      END revenu;

  7      OVERRIDING MEMBER FUNCTION InfoClasse RETURN VARCHAR2 IS

  8      BEGIN

  9          RETURN 'Le type est T_MANAGER';

 10      END InfoClasse;

 11  END;

 12  /

 

Corps de type créé.

 

SQL> CREATE OR REPLACE TYPE BODY T_DIRECTEUR

  2  AS

  3      OVERRIDING MEMBER FUNCTION InfoClasse RETURN VARCHAR2 IS

  4      BEGIN

  5          RETURN 'Le type est T_DIRECTEUR';

  6      END InfoClasse;

  7  END;

  8  /

 

Corps de type créé.

Chaque fois qu’une méthode de l’ancêtre doit être surchargée dans un des enfants, la directive« OVERRIDING » précède la déclaration de la méthode. Autrement tout type objet hérite des méthodes de ses ancêtres sans aucune autre précision.

SQL> DECLARE

  2     TYPE t_emp IS TABLE OF T_EMPLOYE INDEX BY BINARY_INTEGER;

  3     employes t_emp;

  4  BEGIN

  5     employes(1) := T_EMPLOYE('BIZOÏ','Razvan','03/02/1965',

  6                      T_ADRESSE('44, rue Mélanie','Strasbourg',

  7                      67200,'FRANCE'), 1, 1, 'Consultant',2000);

  8                     

  9     employes(2) := T_MANAGER('DULUC','Isabelle','03/02/1965',

 10                      T_ADRESSE('44, rue Mélanie','Strasbourg',

 11                      67200,'FRANCE'), 1, 1, 'Consultant',2000,

 12                      'Formation', 3.5);

 13     employes(3) := T_DIRECTEUR('FABER','Pierre','03/02/1965',

 14                      T_ADRESSE('44, rue Mélanie','Strasbourg',

 15                      67200,'FRANCE'), 1, 1, 'Consultant',4000,

 16                      'Formation', 3.5,'Strasbourg');

 17     FOR indx IN 1..3 LOOP

 18        DBMS_OUTPUT.put_line ( employes(indx).InfoClasse||

 19           ' Revenue de '||employes(indx).NOM||' '||

 20           employes(indx).PRENOM||' = '|| employes(indx).revenu);

 21     END LOOP;

 22  END;

 23  /

Le type est T_EMPLOYE Revenue de BIZOÏ Razvan = 2000

Le type est T_MANAGER Revenue de DULUC Isabelle = 9000

Le type est T_DIRECTEUR Revenue de FABER Pierre = 18000

 

Procédure PL/SQL terminée avec succès.

Les trois instances des objets de type T_EMPLOYE, T_MANAGER, et T_DIRECTEUR sont stockés dans le tableau des objets de type T_EMPLOYE. Chaque instance garde toutes les informations concernant son type d’origine, ainsi à l’exécution la méthode InfoClasse, chaque instance retourne son propre type. Egalement pour le calcul des revenus de l’employé, manager ou directeur, chaque instance exécute sa propre méthode de calcul du revenu.

Stockage d’un type objet

Dans un contexte de bases de données, un type abstrait de données peut être perçu comme :

Une nouvelle gamme de colonnes définie par l'utilisateur qui enrichit celle existante. Les types peuvent se combiner entre eux pour en construire d'autres.

Une structure de données partagée qui permet qu'un type puisse être utilisé par une ou plusieurs tables.

Un type abstrait de données peut être stocké dans une table de deux manières :

Les objets colonne qui sont stockés en tant que colonne structurée dans une table relationnelle.

Les objets enregistrements qui sont stockés en tant que ligne d'une table objet. À ce titre, ils possèdent un identificateur unique appelé OID (Object IDentifier). Ces objets peuvent être indexés et partitionnés.

Vous pouvez créer une table relationnelle et stocker des types objets utilisateur dans une des ces colonnes. La syntaxe de création d'une table relationnelle est la suivante :

CREATE TABLE [SCHEMA.]NOM_TABLE

      ( NOM TYPE_PERSO [DEFAULT EXPRESSION] [,...] ) ...

SQL> CREATE OR REPLACE TYPE type_adresse IS OBJECT (

  2      NORUE              VARCHAR2(60),

  3      VILLE              VARCHAR2(15),

  4      CODE_POSTAL        VARCHAR2(10),

  5      PAYS               VARCHAR2(15))

  6  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE type_personne IS OBJECT(

  2      NOM                VARCHAR2(20),

  3      PRENOM             VARCHAR2(10),

  4      DATE_NAISSANCE     DATE,

  5      adresse            type_adresse)

  6  /

 

Type créé.

 

SQL> CREATE TABLE EMPLOYES(

  2      NO_EMPLOYE         NUMBER(6)    ,

  3      EMPLOYE            type_personne,

  4      FONCTION           VARCHAR2(30) ,

  5      DATE_EMBAUCHE      DATE         ,

  6      SALAIRE            NUMBER(8, 2) )

  7  /

 

Table créée.

 

SQL> INSERT INTO EMPLOYES VALUES ( 1, TYPE_PERSONNE( 'BIZOÏ','Razvan',

  2   '10/12/1964', TYPE_ADRESSE('44,rue Mélanie','STRASBOURG',

  3      '67000','FRANCE')), 'Consultant Oracle', SYSDATE, 2000);         

Vous pouvez interroger la vue du dictionnaire de données « DBA_TYPES » pour récupérer les informations sur les types d’objets de votre base.

SQL>  DESC DBA_TYPES

 Nom                                       NULL ?   Type

 ----------------------------------------- -------- --------------

 OWNER                                              VARCHAR2(30)

 TYPE_NAME                                 NOT NULL VARCHAR2(30)

 TYPE_OID                                  NOT NULL RAW(16)

 TYPECODE                                           VARCHAR2(30)

 ATTRIBUTES                                         NUMBER

 METHODS                                            NUMBER

 PREDEFINED                                         VARCHAR2(3)

 INCOMPLETE                                         VARCHAR2(3)

 FINAL                                              VARCHAR2(3)

 INSTANTIABLE                                       VARCHAR2(3)

 SUPERTYPE_OWNER                                    VARCHAR2(30)

 SUPERTYPE_NAME                                     VARCHAR2(30)

 LOCAL_ATTRIBUTES                                   NUMBER

 LOCAL_METHODS                                      NUMBER

 TYPEID                                             RAW(16)

 

SQL> SELECT TYPE_NAME, ATTRIBUTES FROM DBA_TYPES

  2  WHERE TYPE_NAME LIKE 'TYPE%';

 

TYPE_NAME                      ATTRIBUTES

------------------------------ ----------

TYPE_ADRESSE                            4

TYPE_PERSONNE                           4

 

SQL> DESC TYPE_ADRESSE

 Nom                                       NULL ?   Type

 ----------------------------------------- -------- ----------------

 NORUE                                              VARCHAR2(60)

 VILLE                                              VARCHAR2(15)

 CODE_POSTAL                                        VARCHAR2(10)

 PAYS                                               VARCHAR2(15)

 

SQL> DESC TYPE_PERSONNE

 Nom                                       NULL ?   Type

 ----------------------------------------- -------- ----------------

 NOM                                                VARCHAR2(20)

 PRENOM                                             VARCHAR2(10)

 DATE_NAISSANCE                                     DATE

 ADRESSE                                            TYPE_ADRESSE

Vous pouvez définir pour chaque colonne de type objet utilisateur une valeur par défaut.

SQL> CREATE OR REPLACE TYPE type_personne AS OBJECT (

  2      NOM                VARCHAR2(20),

  3      PRENOM             VARCHAR2(10),

  4      DATE_NAISSANCE     DATE,

  5  CONSTRUCTOR FUNCTION type_personne

  6     ( NOM  IN VARCHAR2, DATE_NAISSANCE IN DATE) RETURN SELF AS RESULT,

  7  CONSTRUCTOR FUNCTION type_personne ( NOM  IN VARCHAR2)  

  8      RETURN SELF AS RESULT,

  9  MEMBER FUNCTION age_pers RETURN NUMBER)

 10  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE BODY type_personne

  2  AS

  3    CONSTRUCTOR FUNCTION type_personne ( NOM  IN VARCHAR2,

  4                                         DATE_NAISSANCE IN DATE)

  5    RETURN SELF AS RESULT IS

  6    BEGIN

  7        SELF.NOM := NOM;

  8        SELF.DATE_NAISSANCE := DATE_NAISSANCE;

  9        RETURN;

 10    END;

 11    CONSTRUCTOR FUNCTION type_personne ( NOM  IN VARCHAR2)

 12        RETURN SELF AS RESULT IS

 13    BEGIN

 14        SELF.NOM := NOM;    RETURN;

 15    END;

 16    MEMBER FUNCTION age_pers RETURN NUMBER IS

 17        age NUMBER(3);

 18    BEGIN

 19        age := trunc( ( sysdate - SELF.DATE_NAISSANCE) / 365);

 20        RETURN age;

 21    END age_pers;

 22  END;

 23  /

 

Corps de type créé.

 

SQL> CREATE TABLE EMPLOYES(

  2      NO_EMPLOYE         NUMBER(6)    ,

  3      EMPLOYE            type_personne

  4           DEFAULT TYPE_PERSONNE( 'BIZOÏ','Razvan','10/12/1964'),

  5      FONCTION           VARCHAR2(30) ,

  6      DATE_EMBAUCHE      DATE         ,

  7      SALAIRE            NUMBER(8, 2) )

  8  /

 

Table créée.

 

SQL> INSERT INTO EMPLOYES

  2   ( NO_EMPLOYE, FONCTION, DATE_EMBAUCHE, SALAIRE)

  3      VALUES ( 1,'Consultant Oracle',SYSDATE, 2000);

 

1 ligne créée.

 

SQL> DESC TYPE_PERSONNE

 Nom                             NULL ?   Type

 ------------------------------- -------- ----------------------

 NOM                                      VARCHAR2(20)

 PRENOM                                   VARCHAR2(10)

 DATE_NAISSANCE                           DATE

 

METHOD

------

 FINAL CONSTRUCTOR FUNCTION TYPE_PERSONNE RETURNS SELF AS RESULT

Nom d'argument             Type                    E/S par défaut ?

 -------------------------- ----------------------- ------ --------

 NOM                        VARCHAR2                IN

 DATE_NAISSANCE             DATE                    IN

 

METHOD

------

 FINAL CONSTRUCTOR FUNCTION TYPE_PERSONNE RETURNS SELF AS RESULT

 Nom d'argument             Type                    E/S par défaut ?

 -------------------------- ----------------------- ------ --------

 NOM                        VARCHAR2                IN

 

METHOD

------

 MEMBER FUNCTION AGE_PERS RETURNS NUMBER

Pour accéder aux attributs des objets dans une instruction SQL, il est obligatoire d’utiliser un alias pour le nom de la table. De même, pour accéder aux méthodes, il faut utiliser un alias pour le nom de la table et utiliser toujours les parenthèses même s’il s’agit d’une fonction sans aucun argument.

SQL> SELECT * FROM EMPLOYES;

 

NO_EMPLOYE

----------

EMPLOYE(NOM, PRENOM, DATE_NAISSANCE)

------------------------------------------------------

FONCTION                       DATE_EMB    SALAIRE

------------------------------ -------- ----------

         1

TYPE_PERSONNE('BIZOÏ', 'Razvan', '10/12/64')

Consultant Oracle              07/07/06       2000

 

SQL> SELECT EMPLOYE.AGE_PERS() FROM EMPLOYES;

SELECT EMPLOYE.AGE_PERS() FROM EMPLOYES

       *

ERREUR à la ligne 1 :

ORA-00904: "EMPLOYE"."AGE_PERS" : identificateur non valide

 

SQL> SELECT EMP.EMPLOYE.AGE_PERS() FROM EMPLOYES EMP;

 

S.EMPLOYE.AGE_PERS()

--------------------

                  41

 

SQL> SELECT EMP.EMPLOYE.DATE_NAISSANCE FROM EMPLOYES EMP;

 

EMPLOYE.

--------

10/12/64

Table objet

La méthode la plus simple de stocker des types objets est de créer une table qui reprend la description d’un objet ainsi chaque enregistrement est un objet de type respectif.

La syntaxe simplifiée de création d'une table relationnelle est la suivante :

CREATE TABLE [SCHEMA.]NOM_TABLE OF [SCHEMA.]NOM_TYPE

    ( NOM TYPE_PERSO [DEFAULT EXPRESSION] [,...] )

[OBJECT IDENTIFIER IS { SYSTEM GENERATED | PRIMARY KEY}]

NESTED TABLE NOM_TABLEAU STORE AS NOM_COLONNE[,...] ...

SQL> CREATE OR REPLACE TYPE type_personne

  2  AS OBJECT (

  3      NO_EMP             NUMBER(2),

  4      NOM                VARCHAR2(20),

  5      PRENOM             VARCHAR2(10),

  6      DATE_NAISSANCE     DATE,

  7  CONSTRUCTOR FUNCTION type_personne ( NO_EMP  NUMBER,

  8                                       NOM  IN VARCHAR2,

  9                                       DATE_NAISSANCE IN DATE)

 10     RETURN SELF AS RESULT,

 11  MEMBER FUNCTION age_pers RETURN NUMBER)

 12  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE BODY type_personne

  2  AS

  3    CONSTRUCTOR FUNCTION type_personne ( NO_EMP  NUMBER,

  4                                         NOM  IN VARCHAR2,

  5                                         DATE_NAISSANCE IN DATE)

  6    RETURN SELF AS RESULT IS

  7    BEGIN

  8        SELF.NOM := NOM;

  9        SELF.DATE_NAISSANCE := DATE_NAISSANCE;

 10        RETURN;

 11    END;

 12    MEMBER FUNCTION age_pers RETURN NUMBER IS

 13        age NUMBER(3);

 14    BEGIN

 15        age := trunc( ( sysdate - SELF.DATE_NAISSANCE) / 365);

 16        RETURN age;

 17    END age_pers;

 18  END;

 19  /

 

Corps de type créé.

 

SQL> CREATE TABLE EMPLOYES OF type_personne ;

 

Table créée.

 

SQL> DESC EMPLOYES

Nom                                       NULL ?   Type

 ----------------------------------------- -------- ----------------

 NO_EMP                                             NUMBER(2)

 NOM                                                VARCHAR2(20)

 PRENOM                                             VARCHAR2(10)

 DATE_NAISSANCE                                     DATE

 

SQL> SELECT OBJECT_ID_TYPE, TABLE_TYPE_OWNER, TABLE_TYPE

  2  FROM DBA_OBJECT_TABLES

  3  WHERE TABLE_NAME LIKE 'EMPLOYES';

 

OBJECT_ID_TYPE   TABLE_TYPE_OWNER               TABLE_TYPE

---------------- ------------------------------ ---------------

SYSTEM GENERATED STAGIAIRE                      TYPE_PERSONNE

Vous pouvez interroger la vue du dictionnaire de données « DBA_OBJECT_TABLES » pour récupérer les informations sur les tables objets.

Il est possible de définir une clé primaire pour gérer les enregistrements de la table. Pour plus de détails sur la syntaxe voir plus loin dans le module.

SQL> CREATE TABLE EMPLOYES OF type_personne

  2  (CONSTRAINT PK_EMPLOYE PRIMARY KEY (NO_EMP))

  3  OBJECT IDENTIFIER IS PRIMARY KEY;

 

Table créée.

 

SQL> SELECT OBJECT_ID_TYPE, TABLE_TYPE_OWNER, TABLE_TYPE

  2  FROM DBA_OBJECT_TABLES

  3  WHERE TABLE_NAME LIKE 'EMPLOYES';

 

OBJECT_ID_TYPE   TABLE_TYPE_OWNER               TABLE_TYPE

---------------- ------------------------------ --------------

USER-DEFINED     STAGIAIRE                      TYPE_PERSONNE

 

SQL> INSERT INTO EMPLOYES VALUES ( 1,'BIZOÏ','Razvan','10/12/1965');

 

1 ligne créée.

 

SQL> INSERT INTO EMPLOYES VALUES ( 1,'BIZOÏ','Razvan','10/12/1965');

INSERT INTO EMPLOYES

*

ERREUR à la ligne 1 :

ORA-00001: violation de contrainte unique (SYS.PK_EMPLOYE)

 

SQL> INSERT INTO EMPLOYES VALUES ( 2,'DULUC','Isabelle','10/12/1965');

 

1 ligne créée.

Si vous voulez insérer des objets qui contiennent des tableaux imbriqués il faut faire attention de prendre en compte la description de chaque tableau imbriqué pour indiquer l’alias de la colonne correspondante.

SQL> CREATE OR REPLACE TYPE T_ADRESSE IS OBJECT (

  2            ADRESSE             NVARCHAR2(60),

  3            VILLE               VARCHAR2(30));

  4  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE T_LISTE_ADRESSES IS TABLE OF T_ADRESSE;

  2  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE T_PERSONNE IS OBJECT(    

  2            NOM                  NVARCHAR2(40),

  3            PRENOM               NVARCHAR2(30),

  4            LISTE_ADRESSES       T_LISTE_ADRESSES)

  5  /

 

Type créé.

 

SQL> CREATE TABLE PERSONNE_OBJ OF T_PERSONNE

  2  NESTED TABLE LISTE_ADRESSES STORE AS ADRESSES;

 

Table créée.

 

SQL> DECLARE

  2   V_PERSONNE1 T_PERSONNE :=

  3           T_PERSONNE( 'BIZOÏ', 'Razvan',

  4                T_LISTE_ADRESSES(

  5                    T_ADRESSE('44, rue Mélanie','Strasbourg'),

  6                    T_ADRESSE('14, rue Claudel','Strasbourg')));

  7   V_PERSONNE2 T_PERSONNE;

  8  BEGIN

  9    V_PERSONNE2 :=

 10           T_PERSONNE( 'DULUC','Isabelle',

 11                T_LISTE_ADRESSES(

 12                    T_ADRESSE('44, rue Mélanie','Strasbourg'),

 13                    T_ADRESSE('14, rue Claudel','Strasbourg')));

 14    INSERT INTO PERSONNE_OBJ VALUES  V_PERSONNE1;

 15    INSERT INTO PERSONNE_OBJ VALUES  V_PERSONNE2;

 16  END;

 17  /

 

Procédure PL/SQL terminée avec succès.

 

SQL> SELECT * FROM PERSONNE_OBJ;

 

NOM

----------------------------------------

PRENOM

------------------------------

LISTE_ADRESSES(ADRESSE, VILLE)

-------------------------------------------------------------

BIZOÏ

Razvan

T_LISTE_ADRESSES(T_ADRESSE('44, rue Mélanie', 'Strasbourg'),

T_ADRESSE('14, rue Claudel', 'Strasbourg'))

 

DULUC

Isabelle

 

NOM

----------------------------------------

PRENOM

------------------------------

LISTE_ADRESSES(ADRESSE, VILLE)

-------------------------------------------------------------

T_LISTE_ADRESSES(T_ADRESSE('44, rue Mélanie', 'Strasbourg'),

T_ADRESSE('14, rue Claudel', 'Strasbourg'))

Opérateurs et prédicats

SQL définit des opérateurs qui peuvent manipuler les objets et les références d'objets. Notez bien que si tous ces opérateurs « VALUE », « REF », « DEREF » et « IS DANGLING » peuvent être spécifiés uniquement dans les instructions SQL, ils ne peuvent l'être dans des instructions PL/SQL.

VALUE

 Vous pouvez utiliser la directive « VALUE » pour récupérer l'objet de l’enregistrement pour la table que représente l’argument de la directive. Attention il s’agit d’un alias de table.

SQL> SELECT VALUE(E), NOM, PRENOM FROM EMPLOYES E;

 

VALUE(E)(NO_EMP, NOM, PRENOM, DATE_NAISSANCE)

--------------------------------------------------

NOM                  PRENOM

-------------------- ----------

TYPE_PERSONNE(1, 'BIZOÏ', 'Razvan', '10/12/65')

BIZOÏ                Razvan

 

TYPE_PERSONNE(2, 'DULUC', 'Isabelle', '10/12/65')

DULUC                Isabelle

La directive « VALUE » peut être utilisée également dans la clause « WHERE » pour effectuer une comparaison avec la valeur de retour d'une sous-requête ou d'une variable PL/SQL

SQL> SELECT VALUE(E), NOM, PRENOM FROM EMPLOYES E

  2  WHERE VALUE(E)=( SELECT VALUE(E1) FROM EMPLOYES E1 WHERE NO_EMP = 2);

 

VALUE(E)(NO_EMP, NOM, PRENOM, DATE_NAISSANCE)

---------------------------------------------------------

NOM                  PRENOM

-------------------- ----------

TYPE_PERSONNE(2, 'DULUC', 'Isabelle', '10/12/65')

DULUC                Isabelle

REF

Vous pouvez utiliser la directive « REF » pour récupérer la référence de chaque objet stocké dans la table. Rappelez-vous que chaque enregistrement est une instance du type d’objet utilisé pour la création de la table. La référence d’un objet peut être utilisée pour des jointures entre les tables au même titre que les contraintes d’intégrités référentielles.

SQL> SELECT REF(E), NOM, PRENOM FROM EMPLOYES E;

 

REF(E)

-------------------------------------------------------------------

NOM                  PRENOM

-------------------- ----------

00004A038A004675B57AFAC9E24692A8576E9B2B92C840000000142601000100010

0290000000000090602002A00078401FE0000000A02C10200000000000000000000

00000000000000000000

BIZOÏ                Razvan

 

00004A038A004675B57AFAC9E24692A8576E9B2B92C840000000142601000100010

0290000000000090602002A00078401FE0000000A02C10300000000000000000000

00000000000000000000

DULUC                Isabelle

DEREF

La directive « DEREF » vous permet de retrouver l'objet de l’enregistrement pour la table correspondant à la référence donné comme argument.

SQL> DECLARE

  2     v_pers TYPE_PERSONNE;

  3  BEGIN

  4    SELECT DEREF(REF(EMP)) INTO v_pers FROM EMPLOYES EMP WHERE NO_EMP=1;

  5    DBMS_OUTPUT.PUT_LINE(v_pers.NOM||' '||v_pers.PRENOM||' '||  

  6                          v_pers.DATE_NAISSANCE);

  7  END;

  8  /

BIZOÏ Razvan 10/12/65

IS DANGLING

 Le prédicat « IS DANGLING » détermine si une référence d'objet pointe ou non vers un objet valide. Si l'objet sur lequel pointe une référence est supprimé, la référence est dite invalide puisqu'elle pointe désormais sur un objet non existant. Il est illicite de supprimer une référence invalide.

Rappelez vous les directives « VALUE », « REF », « DEREF » et le prédicat « IS DANGLING » peuvent être spécifiés uniquement dans les instructions SQL, ils ne peuvent l'être dans des instructions PL/SQL.

SQL> DECLARE

  2     v_pers TYPE_PERSONNE;

  3  BEGIN

  4     SELECT REF(EMP) INTO v_ref FROM EMPLOYES EMP WHERE NO_EMP=1;

  5     DELETE FROM EMPLOYES WHERE NO_EMP = 1;

  6     IF v_ref IS DANGLING THEN

  7        DBMS_OUTPUT.PUT_LINE('1');

  8     ELSE

  9        DBMS_OUTPUT.PUT_LINE('2');

 10     END if;  

 11  END;

 12  /

   IF v_ref IS DANGLING THEN

      *

ERREUR à la ligne 6 :

ORA-06550: Ligne 6, colonne 7 :

PLS-00204: fonction ou pseudo-colonne 'IS DANGLING' peut être

utilisée uniquement dans instruction SQL

 

SQL> DECLARE

  2     v_ref REF TYPE_PERSONNE;

  3     v_result   VARCHAR2(50);

  4  BEGIN

  5     SELECT REF(EMP) INTO v_ref FROM EMPLOYES EMP WHERE NO_EMP=1;

  6     DELETE FROM EMPLOYES WHERE NO_EMP = 1;

  7     SELECT 'Référence est supprimé' INTO v_result FROM DUAL

  8     WHERE v_ref IS DANGLING;

  9     DBMS_OUTPUT.PUT_LINE(v_result);

 10  END;

 11  /

Référence est supprimé

Les tableaux imbriqués

Les objets que vous créez peuvent utiliser des tableaux imbriqués comme attributs et vous pouvez également les stocker dans les tables de votre base de données. Ainsi, comme nous l’avons vu précédemment, vous créez deux tables, la première est la table principale et la deuxième celle du tableau imbriqué. La syntaxe pour la création de la table est la suivante :

CREATE TABLE (  ... nom_colonne  type_tableau, ... )

NESTED TABLE nom_colonne STORE AS nom_table_tableau;

Dans l’exemple suivant vous pouvez voir le stockage d’un tableau imbriqué dans une table et la syntaxe d’alimentation de cette table, en utilisant la commande « COLLECT » pour concevoir le tableau à partir d’une requête SQL.

SQL> CREATE OR REPLACE TYPE obj_adresse IS OBJECT

  2  (  ADRESSE              NVARCHAR2(60),

  3     VILLE                VARCHAR2(30) ,

  4     CODE_POSTAL          VARCHAR2(10) ,

  5     PAYS                 VARCHAR2(25) );

  6  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE tab_adresses

  2         IS TABLE OF obj_adresse;

  3  /

 

Type créé.

 

SQL> CREATE TABLE PERSONNES(

  2     NO_PERSONNE          NUMBER(6)

  3           CONSTRAINT PERSONNES_PK PRIMARY KEY

  4           USING INDEX TABLESPACE ITB_TRAN LOGGING,

  5     NOM                  NVARCHAR2(40)     NOT NULL,

  6     PRENOM               NVARCHAR2(30)     NOT NULL,

  7     adresses             tab_adresses )

  9  NESTED TABLE ADRESSES STORE AS PERSTAB_ADRESSES

 10  TABLESPACE DTB_TRAN LOGGING COMPRESS FOR OLTP;

 

Table créée.

 

SQL> INSERT INTO PERSONNES

  2  SELECT NO_EMPLOYE, NOM, PRENOM,

  3     ( SELECT CAST( COLLECT(

  4                  obj_adresse(ADRESSE,VILLE,CODE_POSTAL,PAYS))

  5                         AS tab_adresses)

  6       FROM PERSONNES_ADRESSES WHERE ID = NO_EMPLOYE)

  7  FROM EMPLOYES

  8  WHERE PAYS IS NOT NULL;

 

92 ligne(s) créée(s).

 

SQL> SELECT NO_PERSONNE ID,NOM,PRENOM,ADRESSES FROM PERSONNES;

 

  ID NOM          PRENOM       ADRESSES(ADRESSE, VILLE, CODE_POSTAL,

---- ------------ ------------ -------------------------------------

  70 Berlioz      Jacques      TAB_ADRESSES(OBJ_ADRESSE('37 North Ai

  71 Nocella      Guy          TAB_ADRESSES(OBJ_ADRESSE('17 South Sa

  72 Herve        Didier       TAB_ADRESSES(OBJ_ADRESSE('107 West Ar

  73 Mangeard     Jocelyne     TAB_ADRESSES(OBJ_ADRESSE('27 West Str

  74 Cazade       Anne-Claire  TAB_ADRESSES()

  76 Peacock      Margaret     TAB_ADRESSES(OBJ_ADRESSE('57 East Bla

...

Dans le cas où vous utilisez le tableau imbriqué comme attribut d’une colonne de type objet, il faut faire attention à la déclaration du stockage du tableau imbriqué. En effet, la syntaxe de déclaration pour le stockage du tableau imbriqué accepte uniquement le nom d’une variable de type tableau imbriqué.

SQL> CREATE OR REPLACE TYPE otab_adresses IS OBJECT

  2  (  a_adresses            tab_adresses,

  3     CONSTRUCTOR FUNCTION otab_adresses

  4           ( ADRESSE     IN NVARCHAR2,

  5             VILLE       IN VARCHAR2,

  6             CODE_POSTAL IN VARCHAR2 ,

  7             PAYS        IN VARCHAR2) RETURN SELF AS RESULT);

  8  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE BODY otab_adresses

  2  AS

  3     CONSTRUCTOR FUNCTION otab_adresses

  4        ( ADRESSE     IN NVARCHAR2,

  5          VILLE       IN VARCHAR2,

  6          CODE_POSTAL IN VARCHAR2 ,

  7          PAYS        IN VARCHAR2)

  8     RETURN SELF AS RESULT IS

  9     BEGIN

 10         SELF.a_adresses := tab_adresses(

 11           obj_adresse( ADRESSE,VILLE,CODE_POSTAL,PAYS));

 12         RETURN;

 13     END;

 14  END;

 15  /

 

Corps de type créé.

 

SQL> CREATE TABLE PERSONNES(

  2     NO_PERSONNE          NUMBER(6)

  3           CONSTRAINT PERSONNES_PK PRIMARY KEY

  4           USING INDEX TABLESPACE ITB_TRAN LOGGING,

  5     NOM                  NVARCHAR2(40)     NOT NULL,

  6     PRENOM               NVARCHAR2(30)     NOT NULL,

  7     adresses             otab_adresses  )

  8  NESTED TABLE ADRESSES.a_adresses STORE AS PERSTAB_ADRESSES;

 

Table créée.

Il faut également faire attention à la syntaxe de stockage des tableaux imbriqués à plusieurs niveaux. La syntaxe de mise en œuvre est la suivante :

CREATE TABLE (  ... nom_colonne  type_tableau, ... )

NESTED TABLE nom_colonne STORE AS nom_table_tableau

     ( NESTED TABLE nom_colonne  STORE AS nom_table_tableau

           ( NESTED TABLE nom_colonne STORE AS nom_table_tableau) );

Dans l’exemple suivant, la table FOURNISSEURS stocke pour chaque fournisseur une liste d’adresses dans un tableau imbriqué. Pour chaque adresse, une liste de plusieurs contacts est définie et stockée dans un deuxième tableau imbriqué. Pour chaque contact, une liste de coordonnées est stockée dans un troisième tableau imbriqué.

 SQL> CREATE OR REPLACE TYPE r_coordonee IS OBJECT

  2         ( TELEPHONE VARCHAR2(90) , TYPE  NUMBER(1));

  3  /  

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE t_coordonees IS TABLE OF r_coordonee;

  2   /     

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE r_contact IS OBJECT

  2  (   NOM             NVARCHAR2(60)     ,

  3      PRENOM          NVARCHAR2(60)     ,

  4      TITRE           VARCHAR2(5)       ,

  5      DATE_NAISSANCE  DATE              ,

  6      MAIL            NVARCHAR2(90)     ,

  7      COORDONEES      t_coordonees  );

  8  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE t_contacts IS TABLE OF r_contact;

  2  /     

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE r_adresse IS OBJECT

  2  (  ADRESSE          NVARCHAR2(80)    ,

  3     VILLE            NVARCHAR2(80)    ,

  4     CODE_POSTAL      NVARCHAR2(20)    ,

  5     PROVINCE         NVARCHAR2(40)    ,

  6     PAYS             NVARCHAR2(60)    ,

  7     CONTACTS         t_contacts       );

  8  /

 

Type créé.

 

SQL> CREATE OR REPLACE TYPE t_adresses IS TABLE OF r_adresse;

  2  /     

 

Type créé.

 

SQL> CREATE TABLE T_FOURNISSEURS  (

  2     NO_FOURNISSEUR       NUMBER(6)     NOT NULL,

  3     SOCIETE              NVARCHAR2(80) NOT NULL,

  4     ADRESSES             t_adresses    ,

  5     CONSTRAINT T_FOURNISSEURS_PK PRIMARY KEY (NO_FOURNISSEUR)

  6           USING INDEX TABLESPACE ITB_TRAN LOGGING)

  7  NESTED TABLE ADRESSES STORE AS T_ADRESSES_FOUR

  8     ( NESTED TABLE CONTACTS STORE AS T_CONTACTS_FOUR

  9          ( NESTED TABLE COORDONEES STORE AS T_COORDONEES_FOUR ))

 10  TABLESPACE DTB_TRAN;

 11 

 

Table créée.

 

SQL> DECLARE

  2     V_ADRESSES   T_ADRESSES   := T_ADRESSES();

  3     V_CONTACTS   T_CONTACTS   := T_CONTACTS();

  4     V_COORDONEES T_COORDONEES := T_COORDONEES();

  5     C_ADR SIMPLE_INTEGER := 1;

  6     C_CTC SIMPLE_INTEGER := 1;

  7     C_CRD SIMPLE_INTEGER := 1;

  8  BEGIN

  9     FOR FOUR IN ( SELECT * FROM FOURNISSEURS)

 10     LOOP

 11        V_ADRESSES.DELETE;

 12        C_ADR := 1;

 13        FOR ADR IN ( SELECT * FROM TAB_ADRESSES_FOURNISSEURS

 14                     WHERE NO_FOURNISSEUR = FOUR.NO_FOURNISSEUR)

 15        LOOP

 16           V_CONTACTS.DELETE;

 17           C_CTC := 1;

 18           FOR CTC IN ( SELECT * FROM TAB_CONTACTS_FOURNISSEURS

 19                        WHERE NO_FOURNISSEUR = FOUR.NO_FOURNISSEUR

 20                          AND NO_ADRESSE     = ADR.NO_ADRESSE)

 21           LOOP

 22              V_COORDONEES.DELETE;

 23              C_CRD := 1;

 24              FOR CRD IN ( SELECT * FROM TAB_COORDONEES_FOURNISSEURS

 25                           WHERE NO_FOURNISSEUR = FOUR.NO_FOURNISSEUR

 26                             AND NO_ADRESSE     = ADR.NO_ADRESSE

 27                             AND NO_PERSONNE    = CTC.NO_PERSONNE)

 28             LOOP

 29               V_COORDONEES.EXTEND;

 30               V_COORDONEES(C_CRD):=R_COORDONEE(CRD.TELEPHONE,CRD.TYPE);

 31               C_CRD := C_CRD + 1;

 32              END LOOP;

 33              V_CONTACTS.EXTEND;

 34              V_CONTACTS(C_CTC):=R_CONTACT(CTC.NOM,CTC.PRENOM,CTC.TITRE,

 35                             CTC.DATE_NAISSANCE,CTC.MAIL,V_COORDONEES);

 36              C_CTC := C_CTC + 1;

 37           END LOOP;

 38           V_ADRESSES.EXTEND;

 39           V_ADRESSES(C_ADR) := R_ADRESSE(ADR.ADRESSE,ADR.VILLE,

 40                    ADR.CODE_POSTAL,ADR.PROVINCE,ADR.PAYS,V_CONTACTS);

 41           C_ADR := C_ADR + 1;

 42        END LOOP;

 43        INSERT INTO T_FOURNISSEURS VALUES (FOUR.NO_FOURNISSEUR,

 44              FOUR.SOCIETE,V_ADRESSES);

 45     END LOOP;

 46     COMMIT;

 47  END;

 48  /

 

Procédure PL/SQL terminée avec succès.

 

SQL> SELECT ADRESSE,VILLE,CODE_POSTAL,PROVINCE,PAYS FROM TABLE(

  2    SELECT ADRESSES FROM T_FOURNISSEURS WHERE NO_FOURNISSEUR = 2 );

 

ADRESSE                      VILLE   CODE_P PROVINCE        PAYS

---------------------------- ------- ------ --------------- ----------

67 Packard Avenue            Almere  53574  Flevopolder     Italie

67 South Coshocton Avenue    Sydney  63488  New South Wales Australie

 

SQL> SELECT NOM,PRENOM,TITRE,DATE_NAISSANCE,MAIL FROM TABLE

  2  (SELECT CONTACTS FROM TABLE( SELECT ADRESSES FROM T_FOURNISSEURS

  3   WHERE NO_FOURNISSEUR = 2 ) ADRESSE WHERE PAYS = 'Italie');

 

NOM          PRENOM       TITRE DATE_NAISS MAIL

------------ ------------ ----- ---------- ----------------------------

Sager        Yvette       M.    13/02/1979 Cette adresse e-mail est protégée contre les robots spammeurs. Vous devez activer le JavaScript pour la visualiser.

Barron       Zandra       Mme   14/02/1955 Cette adresse e-mail est protégée contre les robots spammeurs. Vous devez activer le JavaScript pour la visualiser.

Kimball      Ralph        M.    15/06/1939 Cette adresse e-mail est protégée contre les robots spammeurs. Vous devez activer le JavaScript pour la visualiser.

Whitehead    Benita       Mme   14/06/1952 Cette adresse e-mail est protégée contre les robots spammeurs. Vous devez activer le JavaScript pour la visualiser.

 

SQL> SELECT TELEPHONE FROM TABLE(SELECT COORDONEES FROM TABLE

  2  (SELECT CONTACTS FROM TABLE( SELECT ADRESSES FROM T_FOURNISSEURS

  3         WHERE NO_FOURNISSEUR = 2 )

  4            WHERE PAYS = 'Italie')  

  5                WHERE NOM = 'Kimball');

 

TELEPHONE

------------------------------------------------------------------------

229-112-7083

166-407-8056

650-238-1338

629-706-6107

513-187-5523

 

SQL> UPDATE TABLE

  2  (SELECT COORDONEES FROM TABLE

  3  (SELECT CONTACTS FROM TABLE( SELECT ADRESSES

  4    FROM T_FOURNISSEURS WHERE NO_FOURNISSEUR = 2 ) ADRESSE

  5     WHERE PAYS = 'Italie')   WHERE NOM = 'Kimball') TEL

  6  SET VALUE(TEL) = r_coordonee

  7    ('(39)'||REPLACE(VALUE(TEL).TELEPHONE,'-'),VALUE(TEL).TYPE);

 

5 lignes mises à jour.

 

SQL> SELECT TELEPHONE FROM TABLE(SELECT COORDONEES FROM TABLE

  2  (SELECT CONTACTS FROM TABLE( SELECT ADRESSES

  3    FROM T_FOURNISSEURS WHERE NO_FOURNISSEUR = 2 ) ADRESSE

  4     WHERE PAYS = 'Italie')   WHERE NOM = 'Kimball');

 

TELEPHONE

------------------------------------------------------------------------

(39)2291127083

(39)1664078056

(39)6502381338

(39)6297066107

(39)5131875523