Documente Academic
Documente Profesional
Documente Cultură
Cuprins
Laborator 1 Introducere n obiectele Oracle..................................................................................... 3 Avantajul lucrului cu obiecte Oracle................................................................................................ 3 Tipuri abstracte de date. Atribute i metode ................................................................................... 4 Referine........................................................................................................................................ 5 Folosirea tipurilor n cadrul tabelelor............................................................................................... 6 Laborator 2 Modul de lucru cu obiectele Oracle transmiterea prin valoare..................................... 7 Laborator 3 Modul de lucru cu obiectele Oracle transmiterea prin referin partea I...................11 Laborator 4 Modul de lucru cu obiectele Oracle transmiterea prin referin partea a II-a............16 Laborator 5 Test 1 laborator ...........................................................................................................19 Laborator 6 Referine spre obiecte n PL/SQL ................................................................................20 Pachetul UTL_REF .......................................................................................................................20 Laborator 7 ROWID, OID i REF. ...................................................................................................24 Tipul de dat ROWID....................................................................................................................24 Objects ID (OID) ..........................................................................................................................25 Folosirea lui REF ca operator sau ca modificator de tip .................................................................27 Laborator 8 Definirea tabelelor obiect i a coloanelor de tip tablou imbricat.....................................29 Laborator 9 Metode........................................................................................................................35 Definirea metodelor.......................................................................................................................35 Metode MEMBER .........................................................................................................................36 Metode pentru compararea obiectelor...........................................................................................37 Laborator 10 Metode statice i metode constructor.........................................................................41 Metode statice ..............................................................................................................................41 Metode constructor .......................................................................................................................42 Laborator 11 Test 2 laborator .........................................................................................................44
2 din 44
3 din 44
4 din 44
Exemplu: CREATE OR REPLACE TYPE BarType as OBJECT ( nume CHAR(20), addr CHAR(20) );
Referine
Dac T este un tip, atunci REF T reprezint un pointer ctre un obiect de tip T. n unele documentaii este denumit i object ID, dar fa de identificatorii de obiect care nu sunt vizibili n mod normal, aceste referine sunt vizibile n baza de date.
Exemplu: CREATE OR REPLACE TYPE MenuType as OBJECT ( bar REF BarType, beer REF BeerType, price FLOAT ); /
3.00
Pointer ctre un obiect BarType Pointer ctre un obiect BeerType
5 din 44
Din punct de vedere obiectual, tabela Bars nu este constituit din dou coloane: nume i addr, ci dintr-o singur coloan (relaie unar) care are dou componente: nume i addr. Prin apelarea numelui de tip cu valori, se consider apelarea constructorului implicit: INSERT INTO Bars VALUES( BarType(Corso, str. Lapusneanu) ); /
6 din 44
MEMBER PROCEDURE SetOre(v_ore_lucr INTEGER, v_ore_co INTEGER, v_ore_cm INTEGER) IS BEGIN SELF.ore_lucrate := v_ore_lucr; SELF.ore_co := v_ore_co; SELF.ore_cm := v_ore_cm; END SetOre; END; / 7 din 44
Utilizarea parametrului SELF este opional, dar recomandat pentru lizibilitatea codului sau n cazul operaiilor complexe care necesit i variabile locale (SELF.ore_lucrate este echivalent cu ore_lucrate). Prin sintaxa SELF.ore_lucrate se acceseaz datele stocate de atributul ore_lucrate pentru obiectul curent. Metoda CalculSalariu() este definit fr paranteze deoarece nu accept nici un parametru. Parantezele sunt ns obligatorii pentru apelul metodei. n exemplul de mai jos se construiete un obiect de tip Angajat i se afieaz salariul (acesta se calculeaz pornind de la premisa c pentru concedii medicale se acord un procent de 85% din valoarea salariului tarifar). Apoi se modific numrul de ore lucrate i petrecute n concediu medical prin intermediul metodei SetOre i se afieaz din nou salariul. Construirea unui obiect de un anume tip se realizeaz prin apelarea unui constructor implicit, cruia i sunt furnizate valori pentru fiecare atribut n parte: NEW DenumireTip (val1, val2, , valn) Exemplul de mai jos creeaz un obiect i apeleaz metodele sale ntr+un bloc PL/SQL: DECLARE obj_angaj1 Angajat; BEGIN obj_angaj1 := NEW Angajat(100, Ionescu, Ion, 160, 0, 0, 1000); DBMS_OUTPUT.PUT_LINE(obj_angaj1.nume || || obj_angaj1.prenume || are salar: || obj_angaj1.CalculSalariu() );
-- modific starea obiectului obj_angaj1.SetOre(100, 0, 60); DBMS_OUTPUT.PUT_LINE(obj_angaj1.nume || || obj_angaj1.prenume || are salariul dup modificare ore_cm cu 60: || obj_angaj1.CalculSalariu() ); END; /
Un aspect important n ceea ce privete manipularea obiectelor ntr-un limbaj de programare se refer la modul de transfer al acestora (ntre variabile sau ntre un modul program i altul). n Oracle, obiectele se transmit prin valoare, ca n exemplul de mai jos sau prin referin, prin operatorul REF. 8 din 44
DECLARE obj_angaj1 Angajat; obj_angaj2 Angajat; BEGIN obj_angaj1 := NEW Angajat(100, Ionescu, Ion, 160, 0, 0, 1000); obj_angaj2 := NEW Angajat(100, Popescu, Vasile, 0, 0, 0, 2000); DBMS_OUTPUT.PUT_LINE(Pasul 1: doua obiecte diferite: doua variabile); DBMS_OUTPUT.PUT_LINE(obj_angaj1.nume || || obj_angaj1.prenume || are salar: || obj_angaj1.CalculSalariu() ); DBMS_OUTPUT.PUT_LINE(obj_angaj2.nume || || obj_angaj2.prenume || are salar: || obj_angaj2.CalculSalariu() );
DBMS_OUTPUT.PUT_LINE(Pasul 2: dupa atribuire - doua obiecte identice: doua variabile); DBMS_OUTPUT.PUT_LINE(obj_angaj1.nume || || obj_angaj1.prenume || are salar: || obj_angaj1.CalculSalariu() ); DBMS_OUTPUT.PUT_LINE(obj_angaj2.nume || || obj_angaj2.prenume || are salar: || obj_angaj2.CalculSalariu() );
-- modific doar obiectul 2 obj_angaj2.ore_lucrate := 200; DBMS_OUTPUT.PUT_LINE(Pasul 3: modific doar unul din obiecte
(ore_lucrate = 200), celalalt ramane neschimbat); DBMS_OUTPUT.PUT_LINE(obj_angaj1.nume || || obj_angaj1.prenume || are salar: || obj_angaj1.CalculSalariu() ); DBMS_OUTPUT.PUT_LINE(obj_angaj2.nume || || obj_angaj2.prenume || are salar: || obj_angaj2.CalculSalariu() ); END; / Operaia de atribuire obj_angaj1 := obj_angaj2 va determina construirea unui obiect complet diferit, dar cu stare identic (valorile atributelor sunt identice). 9 din 44
Rezultatul rulrii este: Pasul 1: doua obiecte diferite: doua variabile Ionescu Ion are salar: 160000 Popescu Vasile are salar: 0 Pasul 2: dupa atribuire - doua obiecte identice: doua variabile Popescu Vasile are salar: 0 Popescu Vasile are salar: 0 Pasul 3: modific doar unul din obiecte (ore_lucrate = 200), celalalt ramane neschimbat Popescu Vasile are salar: 0 Popescu Vasile are salar: 400000
10 din 44
Prima linie creeaz o tabel obiectual a crei nregistrri vor fi obiecte de tip Angajat, iar a doua linie de cod definete o tabel relaional n care atributul pers este de tip Angajat i va stoca obiecte. Inserarea n tabela obiectual nu difer de modelul relaional clasic.
INSERT INTO Angajati_ObjTbl VALUES (111, Popescu, Vasile, 0,0,0,2000); sau INSERT INTO Angajati_ObjTbl VALUES (NEW Angajat(112, Popa, Viorel, 0,0,0,1500));
INSERT INTO Angajati_RefTbl VALUES (1, NEW Angajat (111, Popescu, Vasile, 0,0,0,2000)); INSERT INTO Angajati_RefTbl VALUES (2, NEW Angajat (112, Popa, Viorel, 0,0,0,1500));
n cazul tabelei relaionale Angajati_RefTbl, pentru a extrage datele stocate de atributele obiectelor, se va utiliza notaia specific modelului de programare orientatobiect (cu punct). Este obligatorie utilizarea unui alias pentru tabela n cauz, pentru a putea manipula obiectele dup ablonul: aliasTabel.atributTabel.atributObiect. n caz contrar se obine eroarea ORA-00904.
La rularea: SELECT pers.nume FROM Angajati_RefTbl; se va obine mesajul de eroare: SELECT pers.nume FROM Angajati_RefTbl * ERROR at line 1: ORA-00904: "PERS"."NUME": invalid identifier
12 din 44
Actualizarea se face n felul urmtor: UPDATE Angajati_RefTbl A SET A.pers.sal_orar=2400 WHERE A.pers.marca=111;
Pn acum s-au prezentat tehnici de actualizare i interogare a datelor obiectelor (valorile stocate de atribute). Uneori este necesar ca n urma unei interogri s se obin chiar obiecte sau s nlocuim n tabela obiectual un obiect cu un altul. Pentru aceasta se folosete funcia VALUE(alias_tabela_ob) care returneaz obiecte noi, identice cu obiectele din tabela obiectual specificat prin alias_tabela_ob.
Important de reinut: Funcia poate fi folosit numai n fraze SQL i numai pentru tabele obiectuale.
13 din 44
Prin intermediul interogrii de mai sus se obin obiecte i nu valori ale atributelor sale!
DECLARE obj Angajat; BEGIN SELECT VALUE(A) INTO obj FROM Angajati_ObjTbl A WHERE marca=112; obj.nume := 'test'; END; Variabila obj din blocul PL/SQL preia o copie a obiectului cu marca 112 din tabela Angajati_ObjTbl. Tot aici se modific valoarea atributului nume al noului obiect prin linia de comand: obj.nume := 'test'; Rularea comenzii: SELECT VALUE(A) FROM Angajati_ObjTbl A; va avea ca rezultat:
VALUE(A)(MARCA, NUME, PRENUME, ORE_LUCRATE, ORE_CO, ORE_CM, SAL_ORAR) ---------------------------------------------------------------------ANGAJAT(111, 'Popescu', 'Vasile', 0, 0, 0, 2400) ANGAJAT(112, 'Popa', 'Viorel', 0, 0, 0, 1500)
Acest rezultat al interogrii denot faptul c obiectul cu marca 112 din tabela Angajati_ObjTbl a rmas neschimbat.
Cu ajutorul aceleiai funcii VALUE se poate nlocui obiectul pentru care marca este 112 cu un alt obiect: UPDATE Angajati_ObjTbl A SET VALUE(A) = NEW Angajat(115, Vasilescu, Grigore, 0,0,0,1200) WHERE A.marca = 112;
Orice modificare de genul celei de mai sus va lsa intact adresa logic a obiectului.
14 din 44
Pentru oricare din cele dou tabele se poate defini o cheie primar relativ la atributul marca: ALTER TABLE Angajati_ObjTbl ADD PRIMARY KEY (marca); ALTER TABLE Angajati_RefTbl ADD PRIMARY KEY (pers.marca);
15 din 44
Laborator 4 Modul de lucru cu obiectele Oracle transmiterea prin referin partea a II-a
Revenind la exemplul din laboratorul anterior, se va crea o tabel
SPORURI_REFTBL pentru gestionarea orelor lucrate de angajai: CREATE TABLE Sporuri_RefTbl ( ref_angajat REF ANGAJAT REFERENCES Angajati_ObjTbl, an INTEGER, luna INTEGER, oreSporNoapte INTEGER, oreSporNocive INTEGER );
Atributul ref_angajat al tabelei va stoca referine numai spre obiecte de tip Angajat existente n tabela Angajati_ObjTbl, restricie implementat n modelul relaional prin cheia strin (restricia referenial). Obinerea unei referine spre un obiect se realizeaz cu ajutorul funciei REF(obiect), care poate fi folosit numai n fraze SQL. Aspecte eseniale referitoare la tipurile REF: o referin reprezint adresa, identificatorul unic (OID Object IDentifier) generat de sistem la crearea obiectului (este atribuit automat la momentul iniializrii obiectului i este unic n baza de date) prin intermediul referinelor se manipuleaz obiectele folosind notaia cu punct REF(A).nume returneaz datele stocate de atributul nume al obiectelor din tabela Angajati_ObjTbl
16 din 44
Pentru c o referin se poate obine numai prin intermediul unei fraze SELECT, adugarea de nregistrri n tabela Sporuri_RefTbl se face astfel: INSERT INTO Sporuri_RefTbl (SELECT REF(A), 2003, 03, 0, 0 FROM Angajati_ObjTbl A ); Vor fi inserate attea nregistrri cte linii sunt n tabela Angajati_ObjTbl, respectiv n cazul nostru, 2 nregistrri.
Este posibil ca un obiect identificat de o referin s devin la un moment dat indisponibil (de exemplu prin tergerea obiectului sau prin schimbarea privilegiilor). Un astfel de obiect referin se numete izolat. n acest sens, pentru testarea validitii referinelor se folosete predicatul IS DANGLING. Varianta IS NOT DANGLING poate fi utilizat pentru a identifica referinele valide.
17 din 44
CREATE TYPE t_adresa AS OBJECT ( cod_adresa NUMBER, strada nr oras ); VARCHAR2(50), NUMBER, VARCHAR2(40)
INSERT INTO tab_adrese VALUES (1, Dimitrie Mangeron, 53, Iasi); INSERT INTO tab_adrese VALUES (2, Tudor Vladimirescu, 41, Iasi);
INSERT INTO tab_studenti SELECT 101, Popescu, Vasile, REF(a) FROM tab_adrese a WHERE a.cod_adresa = 2; INSERT INTO tab_studenti SELECT 102, Popa, Viorel, REF(a) FROM tab_adrese a WHERE a.cod_adresa = 1;
18 din 44
Iniial, aceast comand nu va returna nici un rezultat deoarece toate legturile sunt corecte. n urma execuiei comenzii: DELETE FROM tab_adrese WHERE cod_adresa = 1; i se execut din nou comanda SELECT de mai sus, va rezulta studentul Popa Viorel deoarece referinele ctre adresa avnd codul 1 au devenit izolate, ele referind aceeai linie obiect de tip t_adresa din tabelul tab_adrese, care a fost ters. Accesarea obiectului adresat de o referin se numete derefereniere i este realizat cu ajutorul funciei DEREF(expresie_referin). Dac argumentul funciei DEREF este o referin ctre un obiect, aceasta returneaz instana obiect referit. Cnd nu se aplic DEREF asupra referinelor selectate de o cerere, sistemul returneaz identificatorul obiect al referinei. SELECT DISTINCT adresa FROM tab_studenti; SELECT DEREF(adresa) FROM tab_studenti; Se observ din rezultate diferena dintre cele dou abordri ale unei referine, fr i cu derefereniere (fizic, respectiv logic). n cel de-al doilea SELECT, unde s-a folosit DEREF, nu se poate specifica operatorul DISTINCT pentru c nu este definit o metod de ordonare pentru tipul t_adresa i atunci sistemul ar returna eroarea ORA-22950 cannot ORDER objects without MAP or ORDER method. Explicaia este c operatorul care face proiecia fr dubluri (DISTINCT) presupune o ordonare a listei de linii rezultat. Dereferenierea referinelor izolate duce la obinerea unui obiect cu valoarea null. Sistemul Oracle asigur dereferenierea implicit a referinelor atunci cnd este necesar. Oracle nu reutilizeaz OID-urile, astfel c la tergerea unui obiect i inserarea imediat a altuia cu aceleai valori, obiectul va primi un alt OID. n schimb, din punct de vedere relaional, linia inserat avnd aceleai valori cu linia tears, este ca i cum nimic nu s-ar fi ntmplat. Aceasta este marea diferen ntre sistemele relaionale i cele orientate obiect. Funcia MAKE_REF creeaz o referin ctre o linie a unui tabel a crei identificator obiect este bazat pe cheia primar.
19 din 44
PL/SQL nu suport navigarea ntre obiecte prin intermediul referinelor, dei permite declararea variabilelor de tip REF care s conin adrese de obiecte. Fie urmtorul bloc PL/SQL: SET SERVEROUTPUT ON; DECLARE angajat_ref REF Angajat; BEGIN SELECT REF(A) INTO angajat_ref FROM Angajati_ObjTbl A WHERE A.marca = 112; DBMS_OUTPUT.PUT_LINE(angajat_ref.nume); END; / La rularea codului de mai sus se obine mesajul de eroare:
DBMS_OUTPUT.PUT_LINE(angajat_ref.nume); *
ORA-06550: line ., column ..: PLS-00536: Navigation through REF variables is not supported in PL/SQL. Oracle ofer posibilitatea de gestionare a referinelor n PL/SQL prin intermediul pachetului UTL_REF. Spre deosebire de comenzile Limbajului de Manipulare a Datelor (LMD) INSERT, UPDATE, DELETE, MERGE, etc, procedurile din pachetul UTL_REF prezint avantajul c nu necesit specificarea tabelului obiect din care face parte obiectul referit. UTL_REF poate fi utilizat att de subprogramele i pachetele PL/SQL stocate pe server, ct i n aplicaiile PL/SQL de la client (Oracle Forms).
20 din 44
Pachetul conine urmtoarele proceduri: 1. UTL_REF.SELECT_OBJECT ( reference IN REF "<typename>", object IN OUT "<typename>");
extrage obiectul adresat de parametrul reference i paseaz valoarea variabilei object. Se va obine un obiect nou, copie identic a obiectului surs. Fraza SQL echivalent este: SELECT VALUE(t) INTO object FROM object_table t WHERE REF(t) = reference;
nlocuiete obiectul de la adresa specificat prin parametrul reference cu unul nou. Adresa OID rmne neschimbat, doar c va fi un alt obiect identificat prin intermediul ei. Fraza SQL echivalent este: UPDATE object_table SET VALUE(t) = object WHERE REF(t) = reference;
3. UTL_REF.DELETE_OBJECT ( reference IN REF "<typename>"); terge obiectul identificat prin referina reference. Fraza SQL echivalent este: DELETE FROM object_table WHERE REF(t) = reference;
21 din 44
Procedura blocheaz un obiect identificat prin referina reference pentru a nu fi modificat de alt tranzacie. Fraza SQL echivalent este: SELECT VALUE(t) INTO object FROM object_table t WHERE REF(t) = reference FOR UPDATE; Dup cum se observ, procedura este suprancrcat astfel nct, opional, obiectul s poat fi reinut ntr-o variabil. Aici se poate vedea cel mai bine avantajul c nu este necesar specificarea tabelului obiect din care face parte obiectul referit. Nu este necesar blocarea unui obiect nainte de a se efectua un UPDATE sau DELETE. Pentru exemplificare, se d urmtoarea procedur: CREATE OR REPLACE PROCEDURE test_utl_ref (v_ref REF Angajat) IS obj_clona_angajat Angajat; BEGIN -- pasul 1 UTL_REF.SELECT_OBJECT (v_ref, obj_clona_angajat); DBMS_OUTPUT.PUT_LINE(pasul1 - || obj_clona_angajat.marca || || obj_clona_angajat.nume);
-- pasul 2 UTL_REF.UPDATE_OBJECT (v_ref, NEW Angajat(120, test2, null, 0,0,0,1200 )); DBMS_OUTPUT.PUT_LINE(pasul2 - || obj_clona_angajat.marca || || obj_clona_angajat.nume);
-- pasul 3 UTL_REF.SELECT_OBJECT (v_ref, obj_clona_angajat); DBMS_OUTPUT.PUT_LINE(pasul3 - || obj_clona_angajat.marca || || obj_clona_angajat.nume); -- pasul 4 UTL_REF.DELETE_OBJECT (v_ref); END; / 22 din 44
Pasul 1 obinerea obiectului. ntr-o variabil local, obj_clona_angajat de tip Angajat, se preia valoarea obiectului a crui referin a fost obinut prin parametrul v_ref Pasul 2 nlocuirea obiectului. Se modific obiectul identificat prin referina v_ref prin nlocuirea lui cu altul complet nou avnd marca 120. nlocuirea de va petrece n tabela surs. Textul afiat demonstreaz c variabila local deine o copie. Pasul 3 obinerea obiectului dup modificare. Se reiniializeaz variabila local cu obiectul nou de la aceeai referin. Pasul 4 tergerea obiectului. Obiectul se terge din tabela surs. Se observ la ultimul SELECT de mai jos c au rmas doar 2 obiecte n tabela Angajati_ObjTbl.
DECLARE ref_a REF Angajat; BEGIN SELECT REF(A) INTO ref_a FROM Angajati_ObjTbl A WHERE A.marca = 119; test_utl_ref(ref_a); END; /
pasul1 - 119 test pasul2 - 119 test pasul3 - 120 test2
23 din 44
FFF
Relative file number
BBBBBB
Block number
RRR
Row Number
Valorile de tip ROWID nu sunt stocate n mod explicit ca valori n coloanele tabelelor la care se refer. O valoare de tip ROWID ocup 10 bytes pe hard disk i este afiat folosind 18 caractere. Data object number este asignat la crearea fiecrui obiect Oracle care stocheaz valori (tabele sau indeci) i este unic n baza de date Relative file number este o valoare unic a fiecrui fiier din care este format tablespace-ul Block number reprezint poziia blocului, n fiier, care conine linia Row number reprezint poziia din header-ul blocului
Intern, data object number ocup 32 bii, relative file number ocup 10 bii, block number ocup 22 bii i row number ocup 16 bii, avnd n total 80 de bii sau 10 bytes. ROWID este afiat folosind o schem de codare base-64 care folosete caracterele: A-Z, a-z, 0-9, +, i / n total 64 caractere. n aceast schem de codare base-64, data object number se afieaz pe 6 poziii, relative file number se afieaz pe 3 poziii, block number se afieaz pe 6 poziii i row number se afieaz pe 3 poziii.
24 din 44
Objects ID (OID)
Relund explicaiile din Laboratorul 3, o referin obiect identific n mod unic un obiect stocat ntr-un tabel sau vizualizare obiect. O valoare de tip referin este caracterizat de: Identificatorul unic al obiectului referit (OID Object IDentifier) Identificatorul unic asociat tabelului obiect referit Identificatorul liniei (ROWID) din tabelul obiect n care este stocat obiectul (identificatorul ROWID este de obicei folosit pentru accesul rapid la obiectele stocate) Dei pn acum doar am vizualizat valori OID, trebuie tiut de la nceput c acestea nu se pot modifica de ctre utilizator sau administratorul bazei de date, fiind gestionate intern de ctre Oracle. Atunci cnd utilizatorul creeaz o tabel de obiecte, Oracle adaug o coloan ascuns care va stoca OID-ul pentru fiecare obiect introdus. OID are urmtoarele proprieti: Opacitatea programele nu folosesc acest OID n mod direct i nu este n mod normal nevoie de a fi folosit n codul unui program; n principiu este unic la nivel global n orice baz de date Oracle instalat. Practic, sunt 2128 valori posibile. n acest fel, chiar pe maini identice pe care este instalat Oracle, nu este posibil a se genera acelai OID. Conservarea valorii dup o operaiune de import/export, ceea ce la ROWID nu se ntmpl. Aceast proprietate permite distribuirea obiectelor ntre baze de date, fa de ROWID care este dependent de baza de date din care face parte. Schimbarea OID-ului pentru un obiect nu se poate face dect prin tergerea lui i recrearea lui cu aceleai valori, moment n care Oracle genereaz un alt OID. Nu toate obiectele au OID. Obiectele stocate n variabile PL/SQL nu au OID i nici obiectele coloan. Un obiect coloan are sens n contextul liniei stocate, iar linia stocat are OID. Practic, un programator trebuie s aleag ntre a introduce obiectul ntr-o coloan sau a-l face refereniabil. Numele coloanei ascunse este SYS_NC_OID$. Aceasta este de lungime de 16 bytes. ROWID este de 10 bytes. Numele altei coloane ascunse este
25 din 44
Nu este necesar folosirea SYS_NC_ROWINFO$ att timp ct Oracle furnizeaz posibilitatea folosirii lui REF( ) i VALUE( ):
26 din 44
Dar i acest mod de lucru prin folosirea lui REF ca un fel de cheie strin poate avea o alternativ: SELECT s.cod_s, s.nume, s.prenume, s.adresa.strada, s.adresa.nr, s.adresa.oras FROM tab_studenti s;
Cele dou instruciuni SELECT de mai sus demonstreaz modul n care Oracle suport navigarea prin obiecte fr folosirea lui REF, ceea ce nu mai merge i n PL/SQL. Este mult mai intuitiv i mai uoar folosirea punctului dect folosirea unui join explicit. De remarcat c cele dou instruciuni SELECT de mai sus nu sunt identice ca i concept de returnare a rezultatelor deoarece prima instruciune execut un equi-join, ceea ce nseamn c dac n tab_studenti am referine izolate sau null (a se vedea explicaiile de la DANGLING), n al doilea SELECT am un outer-join.
27 din 44
Valoarea returnat de REF poate fi stocat ntr-o variabil local declarat ca fiind de tip REF nume_obiect. Aceast variabil local poate primi valoare prin intermediul unui fetch sau dintr-o alt variabil declarat ca fiind de tip REF de acelai nume de obiect, aa cum se poate vedea n exemplul de mai jos: DECLARE ref_obj_angaj1 REF Angajat; ref_obj_angaj2 REF Angajat; BEGIN -- exemplu de atribuire prin fetch SELECT REF(a) INTO ref_obj_angaj1 FROM Angajati_ObjTbl a WHERE... -- exemplu de atribuire directa ref_obj_angaj2 := ref_obj_angaj1;
Nu se poate obine referina la o variabil obiect care exist ntr-un bloc PL/SQL: DECLARE obj_angaj1 Angajat := Angajat (111, 'Popescu ' , 'Vasile', 0, 0, 0, 2000); ref_obj_angaj1 REF Angajat; BEGIN ref_obj_angaj1 := REF(obj_angaj1); -- invalid ..
REF sunt doar pentru OID-uri! Obiectele temporare nu au un asemenea pointer. Dup cum s-a dovedit, REF nu ar fi folositor nici ntr-un astfel de caz.
n programarea avansat Oracle, singura situaie n care REF este folosit este n cazul n care dorim folosirea obiectului ntr-o alt baz de date prin pasarea referinei obiectului ca parametru, deci n cazurile de reutilizare a obiectelor.
28 din 44
Comanda creeaz un tabel obiect n schema curent sau n cea precizat. Clauza OF specific tipul (tip_obiect) corespunztor tabelului obiect. Fiecare linie va conine o instan obiect, creia i se va asocia la inserare cte un identificator obiect generat de sistem (OID). Clauza DEFAULT permite specificarea unei valori implicite. Aceasta va fi atribuit coloanei la execuia unei comenzi de inserare care omite o valoare explicit pentru coloana respectiv. Pentru o coloan de tip definit de ctre utilizator, clauza DEFAULT trebuie s conin o invocare literal a constructorului obiectului sau coleciei respective. Expresia DEFAULT poate include orice funcie SQL care nu returneaz un argument literal, o referin ctre o coloan sau apelul unei funcii imbricat. De asemenea, o expresie implicit nu poate conine referine ctre funcii PL/SQL sau alte coloane, pseudocoloanele LEVEL, PRIOR sau ROWNUM, constante de tip dat calendaristic nespecificate n totalitate.
29 din 44
O invocare literal a unei metode de tip constructor este apelul metodei constructor n care argumentele sunt fie valori, fie la rndul lor alte invocri literale de metode constructor. Nu sunt permise variabile sau funcii. Avnd dat clasele t_artist i t_adresa: CREATE TYPE t_artist AS OBJECT ( nume prenume anul_nasterii anul_mortii nationalitate observatii ); / CREATE TYPE t_adresa AS OBJECT ( cod_adresa strada cod_postal oras judet tara ); / Adaug un atribut adresa de tip t_adresa: ALTER TYPE t_artist ADD ATTRIBUTE adresa t_adresa CASCADE; / Definim un tip tablou imbricat, t_artisti_ti de obiecte de tip t_artist: CREATE TYPE t_artisti_ti AS TABLE OF t_artist; / O invocare literal a constructorului pentru tipul tabel imbricat t_artisti_ti este urmtoarea: NUMBER, VARCHAR2(50), VARCHAR2(7), VARCHAR2(40), VARCHAR2(30), VARCHAR2(40) VARCHAR2(30), VARCHAR2(30), VARCHAR2(4), VARCHAR2(4), VARCHAR2(40), VARCHAR2(2000)
30 din 44
t_artisti_ti ( t_artist(Brancusi, Constantin, 1876, 1957, NULL, NULL, t_adresa(NULL, NULL, NULL, Tg. Jiu, NULL, Romania), t_artist(Dali, Salvador, 1904, 1989, NULL, NULL, NULL), t_artist(Picasso, Pablo, 1881, 1973, NULL, NULL, t_adresa(2, Rue des Ecoles 13, 12345, Paris, Paris, Franta) ); Se mai definete t_polite_ti de tip tablou imbricat i t_dim_v un vector de dimensiune 3. Fiecare linie din tabloul imbricat t_polite_ti este un obiect de tip t_polita_asigurare. CREATE TYPE t_polita_asigurare AS OBJECT ( cod_polita descriere firma valoare data_contract ); / CREATE TYPE t_polite_ti AS TABLE OF t_polita_asigurare; / CREATE TYPE t_dim_v AS VARRAY(3) OF NUMBER; / VARCHAR2(30), VARCHAR2(200), VARCHAR2(50), NUMBER, DATE
Un exemplu de invocare literal de metode constructor pentru a specifica valori implicite ale coloanelor unui tabel relaional care conine informaii despre operele de art:
31 din 44
CREATE TABLE opere_de_arta ( cod_opera tip titlu artist NUMBER, VARCHAR2(40), VARCHAR2(200), t_artist
DEFAULT t_artist (A, A, NULL, NULL, NULL, NULL, NULL), data_crearii data_achizitiei valoare polite dimensiuni DATE, DATE DEFAULT SYSDATE, NUMBER, t_polite_ti DEFAULT t_polite_ti(), t_dim_v DEFAULT t_dim_v(-1, -1, -1) )
t_polite_ti() este invocarea literal a metodei constructor pentru un tablou imbricat vid de acest tip, iar t_dim_v(-1, -1, -1) instaniaz un obiect vector de tip t_dim_v cu elementele implicite -1, -1, -1 alese astfel nct s se indice c nu au fost introduse dimensiunile operei respective. Opiunea clauza_OID are urmtoarea form: OBJECT IDENTIFIER IS { SYSTEM GENERATED | PRIMARY KEY} Clauza permite s se specifice dac identificatorul obiect al tabelului obiectual este generat de sistem sau este bazat pe cheia primar a tabelului. Opiunea implicit este SYSTEM GENERATED. Un identificator cheie primar este unic la nivel local (dar nu n mod necesar i la nivel global). Dac se cere un identificator obiect care s fie unic la nivel global, atunci trebuie verificat faptul c i cheia primar este unic la acest nivel. Restriciile legate de clauza referitoare la identificatorul obiect sunt: Se poate specifica OBJECT IDENTIFIER IS PRIMARY KEY doar dac s-a definit o constrngere de cheie primar; Clauza nu poate fi folosit pentru tablouri imbricate;
Proprietile tabelelor obiect sunt similare celor ale tabelelor relaionale, cu diferena c n loc s se specifice coloane, se specific atribute. Numele atributului trebuie s fie precedat de numele coloanei de tip obiect din care face parte. Clauza de substituibilitate [ NOT ] SUBSTITUTABLE AT ALL LEVELS se utilizeaz pentru a preciza dac pot fi inserate linii obiect corespunztoare 32 din 44
subtipurilor tipului declarat al tabelului obiect asupra cruia se face specificaia. Varianta negat indic faptul c tabelul obiect nu este substituibil. n acest caz, substituia este dezactivat pentru toate atributele obiectelor i elementele coleciilor integrate n cadrul liniei. Opiunea implicit este cea de substituibilitate. Proprietile coloanelor de tip tablou imbricat se pot seta cu ajutorul urmtoarelor opiuni: NESTED TABLE {elem_imbricat | COLUMN_VALUE} [clauza_col_substituibila] STORE AS tabel_stocare [( (propr_obiect) [propr_fizice] [propr_coloana] )] [RETURN AS {LOCATOR | VALUE}] Aceste clauze permit specificarea unor caracteristici de stocare separate pentru un tablou imbricat (de exemplu, definirea unui tablou imbricat ca tabel organizat pe baz de index). Clauza NESTED TABLE este obligatorie atunci cnd se creeaz tabele care conin coloane (propriu-zise sau ascunse) de tip tablou imbricat. Clauzele clauza_col_substituibila, propr_obiect, propr_fizice, propr_coloana din cadrul
opiunilor pentru tablouri imbricate funcioneaz n acelai fel ca i pentru tabelul printe. Atributul elem_imbricat specific numele coloanei de tip tablou imbricat sau al unui atribut tablou imbricat al tipului obiectului asociat tabelului. Cuvntul cheie COLUMN_VALUE este folosit n cazul coleciilor pe mai multe niveluri, atunci cnd tabloul imbricat sau vectorul referit nu are nume. Opiunea STORE AS tabel_stocare specific numele tabelului care va conine liniile coloanei de tip tablou imbricat. Asupra tabelului de stocare exist restricia c nu se pot executa comenzi de interogare sau prelucrare a datelor n mod direct. Clauza RETURN AS specific tipul rezultatului returnat de cererile asupra coloanelor de tip tablou imbricat. Exist dou opiuni: VALUE, care permite returnarea unei copii a tabloului imbricat stocat n cadrul coloanei referite de clauza NESTED TABLE LOCATOR, care permite obinerea unei adrese (locator) ctre valoarea propriu-zis a tabloului imbricat. Locatorul este propriu unei sesiuni i nu poate fi reutilizat de la o sesiune la alta. Exemplu: S se creeze un declanator asupra tabelului obiectual opere_de_arta, care va insera o nou nregistrare n tabelul cumparari pentru fiecare oper de art cumprat. S se insereze o nregistrare i s se constate efectul declanatorului. 33 din 44
Tabela cumparari i tipul t_opera_arta este definit dup cum urmeaz: CREATE TYPE t_opera_arta AS OBJECT ( cod_opera tip titlu artist data_crearii data_achizitiei valoare polite dimensiuni NUMBER, VARCHAR2(10), VARCHAR2(200), REF t_artist, DATE, DATE, NUMBER, t_polite_ti, t_dim_v,
MEMBER FUNCTION get_titlu RETURN VARCHAR2 ); / CREATE TABLE cumparari ( opera t_opera_arta,
CREATE OR REPLACE TRIGGER t_ai_on_opere AFTER INSERT ON opere_de_arta FOR EACH ROW BEGIN INSERT INTO cumparari VALUES (t_opera_arta(:NEW.cod_opera, :NEW.tip, :NEW.titlu, NULL, :NEW.data_crearii, :NEW.data_achizitiei, :NEW.valoare, :NEW.polite, :NEW.dimensiuni), SYSDATE, 100, Matache ); END; / INSERT INTO opere_de_arta (cod_opera, tip, titlu) VALUES (1, tip_fictiv, titlu_fictiv); 34 din 44
Laborator 9 Metode
Metodele sunt funcii sau proceduri declarate n cadrul definiiei unui tip obiect pentru implementarea comportamentului obiectelor de acel tip. O aplicaie interacioneaz cu un obiect prin intermediul metodelor definite de tipul acestuia. Metodele pot fi scrise n PL/SQL sau n alt limbaj de programare acceptat de baza de date Oracle. Cele scrise n PL/SQL sau Java sunt stocate n baza de date, iar cele scrise n alte limbaje de programare, cum ar fi C, sunt stocate extern. n definiia unui tip pot fi declarate metode MEMBER sau statice. Metodele MEMBER pot fi metode obinuite sau funcii de ordonare. De asemenea, pentru fiecare tip obiect, sistemul definete automat o metod constructor. Constructorul unui tip este utilizat pentru crearea obiectelor de tipul respectiv.
Definirea metodelor
Pentru implementarea metodelor unui tip se utilizeaz comanda CREATE TYPE BODY. Dac este creat un tip SQLJ pentru care metodele sunt definite n clasa Java asociat, atunci nu este necesar comanda CREATE TYPE BODY. Acelai lucru este valabil i pentru metodele care sunt declarate ca proceduri sau funcii externe. Pentru a crea sau nlocui corpul unui tip din propria schem sunt necesare privilegiile sistem CREATE TYPE sau CREATE ANY TYPE (pentru a crea corpul unui tip din schema altui utilizator), iar pentru a nlocui corpul unui tip din schema altui utilizator trebuie avut privilegiul sistem DROP ANY TYPE. Comanda are urmtoarea sintax: CREATE [OR REPLACE] TYPE BODY [schema.]nume_tip {IS | AS} {MEMBER | STATIC} {declar_proc | declar_functie | declar_cosntructor} [{MEMBER | STATIC} {declar_proc | declar_functie | declar_cosntructor} ] [ {MAP | ORDER} MEMBER declar_functie] END; n cadrul unui tip se poate defini fie o metod de tip MAP, fie una de tip ORDER. n acest fel este posibil compararea instanelor tipului obiect respectiv, n cadrul comenzilor SQL. Dac nu este declarat nici una din metodele de ordonare, atunci se poate verifica doar egalitatea a dou obiecte de acel tip.
35 din 44
Opiunile MEMBER sau STATIC specific tipul metodei implementate, care poate fi procedur, funcie sau constructor. Sintaxa corespunztoare celor 3 tipuri de metode este: PROCEDURE nume_proc ([ parametru tip_de_date ]) {IS | AS} {bloc_PL/SQL | specificatie_apel}
FUNCTION nume_functie ([ parametru tip_de_date ]) RETURN tip_de_date {IS | AS} {bloc_PL/SQL | specificatie_apel}
[FINAL] [INSTANTIABLE] CONSTRUCTOR FUNCTION tip_obiect [ ( [SELF IN OUT tip_obiect, ] parametru tip_de_date [, parametru tip_de_date ] )] RETURN SELF AS RESULT {IS | AS} {bloc_PL/SQL | specificatie_apel}
Metode MEMBER
Metodele MEMBER reprezint modalitatea prin care o aplicaie acceseaz datele unei instane de tip obiect. n cadrul tipului obiect este posibil implementarea cte unei metode pentru fiecare operaie care poate fi executat asupra obiectelor de tipul respectiv. Exemplu: crearea unei metode get_titlu(), care acceseaz informaiile despre o anumit oper de art i returneaz titlul acesteia:
CREATE OR REPLACE TYPE BODY t_opera_arta AS MEMBER FUNCTION get_titlu RETURN VARCHAR2 IS BEGIN RETURN SELF.titlu; END get_titlu; END; / SELECT c.opera.get_titlu() FROM cumparari c;
36 din 44
Orice metod are un parametru predefinit SELF care identific instana obiect asupra creia este invocat metoda la un moment dat. Pentru simplitate, metodele MEMBER pot defini atributele i metodele parametrului SELF fr calificativ (de exemplu SELF.titlu sau titlu au aceeai valoare). Nu este obligatoriu ca parametrul SELF s fie declarat n mod explicit, dar dac este declarat, el trebuie s fie primul parametru transmis metodei. Dac parametrul SELF nu este declarat, n funciile MEMBER el este considerat n mod implicit IN, iar n procedurile MEMBER de tip IN OUT. O metod se invoc utiliznd notaia cu punct (obiect.metoda()). Prefixul notaiei specific obiectul asupra cruia se invoc metoda. Parantezele sunt obligatorii chiar dac metoda nu are parametri.
O metod de tip MAP permite compararea obiectelor prin asocierea dintre o instan obiect i o valoare scalar (DATE, VARCAHR2, NUMBER, etc) Clauza MAP MEMBER din comenzile CREATE TYPE i CREATE TYPE BODY permite implementarea unei funcii MEMBER de tip MAP care returneaz poziia relativ a unei instane date n domeniul ordonat al tuturor instanelor tipului obiect respectiv. O astfel de metod este apelat n mod implicit de sistem i induce o ordonare a instanelor obiect, utiliznd ordinea predefinit a valorilor scalare asociate. Limbajul PL/SQL utilizeaz metodele MAP pentru a evalua expresiile de tip boolean i pentru comparaii directe (de tip ob1 > ob2) sau indirecte (generate de clauzele DISTINCT, GROUP BY i ORDER BY). Dac tipul va fi referit n cereri care 37 din 44
implic sortri, atunci este obligatorie o funcie MAP. Dac argumentul metodei MAP este null, atunci aceasta returneaz valoarea null i nu mai este invocat. Dac ob1 i ob2 sunt dou obiecte care pot fi comparate utiliznd o metod de mapare denumit map(), atunci comparaia ob1 > ob2 este echivalent cu: ob1. map() > ob2. map() O specificaie a unui tip obiect poate conine o singur metod MAP, care trebuie s fie o funcie cu un singur argument (parametrul implicit SELF). Tipul rezultatului trebuie s fie un tip scalar SQL predefinit. Un subtip poate suprascrie metoda MAP motenit de la supertip.
Exemplu: definirea unei metode de tip MAP, perimetru() care implementeaz un mod de comparare a obiectelor de tip t_dreptunghi, utiliznd perimetrul acestora:
MAP MEMBER FUNCTION perimetru RETURN NUMBER ); / CREATE TYPE BODY t_dreptunghi AS MAP MEMBER FUNCTION perimetru RETURN NUMBER IS BEGIN RETURN 2*( lungime+ latime); END perimetru; END; /
Metodele de tip ORDER permit compararea direct a obiectelor. Spre deosebire de metodele de tip MAP, ele specific dac obiectul asupra cruia se face invocarea este mai mic, egal sau mai mare dect obiectul cu care este comparat, conform unui criteriu prestabilit i implementat de metod. Un obiect poate declara cel mult o metod de ordonare. Clauza ORDER MEMBER specific o funcie membru de tip ORDER care accept instana unui obiect ca argument explicit, SELF ca argument implicit i returneaz un ntreg. 38 din 44
Rezultatul reflect dac argumentul implicit SELF este mai mic (rezultat negativ), egal (rezultat zero) sau mai mare (rezultat pozitiv) dect argumentul explicit. Dac argumentul metodei ORDER este null, atunci metoda returneaz valoarea null i nu mai este invocat. n cadrul unei ierarhii de tipuri, un subtip nu poate nici s declare i nici s suprascrie o metod de tip ORDER. Similar metodelor MAP, o metod ORDER este apelat automat ori de cte ori trebuie comparate obiecte de tipul respectiv. De exemplu, dac o coloan de tip obiect apare ntr-o clauz ORDER BY, atunci se invoc metoda ORDER. Metodele de ordonare sunt necesare atunci cnd semnificaia comparrii obiectelor devine prea complex pentru a putea utiliza metodele de mapare. De exemplu, pentru compararea unor obiecte binare (imagini) se poate crea o metod de ordonare care s ia n considerare luminozitatea i numrul de pixeli.
Problem: s se defineasc o metod de ordonare care compar artitii dup numele lor. n cazul numelor identice, compararea se face dup prenumele acestora i apoi descresctor dup anul naterii. S se verifice modul de funcionare al metodei.
ALTER TYPE t_artist ADD ORDER MEMBER FUNCTION comparare (a t_artist) RETURN INTEGER CASCADE; /
CREATE OR REPLACE TYPE BODY t_artist AS ORDER MEMBER FUNCTION comparare (a t_artist) RETURN INTEGER IS v_nume_complet v_return BEGIN . RETURN v_return; END comparare; END; / VARCHAR2(60); INTEGER;
39 din 44
DECLARE v_artist1 v_artist2 v_rezultat BEGIN SELECT v_artist1.comparare(v_artist2) INTO v_rezultat FROM dual; DBMS_OUTPUT.PUT_LINE(Rezultat : || v_rezultat); DBMS_OUTPUT.PUT_LINE(Rezultat invers: || v_artist2.comparare(v_artist1)); END; t_artist := t_artist(..); t_artist := t_artist(..); INTEGER;
Dac se declar una din metodele de comparare, atunci obiectele pot fi comparate n cadrul instruciunilor SQL sau n comenzi procedurale. Dac nu s-a definit nici una din metodele de ordonare, nu se pot face comparaii dect n SQL, pentru a verifica egalitatea a dou instane (dou obiecte de acelai tip sunt egale dac valorile atributelor lor sunt egale). Atunci cnd se sorteaz sau se interclaseaz un numr mare de obiecte, este preferabil o metod de mapare. Metoda MAP este mai eficient dect una ORDER, care poate compara doar cte dou obiecte la un moment dat. n schimb, o metod de ordonare poate implementa o semantic mult mai complex a lumii reale.
40 din 44
Metode statice
O metod static implementeaz un comportament global la nivel de tip, invariabil relativ la instanele tipului obiect respectiv. Ea nu necesit referine la datele unei instane particulare. Prin urmare, metoda este invocat relativ la tipul obiect care o definete i nu relativ la instanele acestuia. O metod static nu are parametrul SELF. Invocarea se face prin utilizarea notaiei cu punct, prefixnd apelul metodei cu numele tipului obiect asociat (nume_tip.metoda()). Exemplu: metoda static ins a tipului tip_a insereaz o nou nregistrare n tabelul a. nregistrarea este format din dou coloane. Prima reprezint identificatorul dat ca prim parametru, iar a doua are ca valoare o referin la obiectul linie din tabelul b, identificat prin codul dat ca al doilea parametru. Operaia nu depinde de instanele tipului i de aceea a fost declarat static. Care este rezultatul cererii?
CREATE OR REPLACE TYPE tip_a AS OBJECT ( id NUMBER, b REF tip_b, STATIC PROCEDURE ins (p_a_id NUMBER, p_b_id NUMBER) ); /
41 din 44
CREATE OR REPLACE TYPE BODY tip_a AS STATIC PROCEDURE ins (p_a_id NUMBER, p_b_id NUMBER) IS BEGIN INSERT INTO a SELECT p_a_id, REF(alias) FROM b alias WHERE alias.id = p_b_id; END ins; END; / INSERT INTO b VALUES (tip_b(1)); INSERT INTO b VALUES (tip_b(2)); INSERT INTO b VALUES (tip_b(3)); EXEC tip_a.ins(1,2);
Metode constructor
n mod implicit, fiecare tip obiect are o metod constructor definit de sistem, adic o metod prin care se poate crea o instan, setnd valorile atributelor acesteia. Metoda constructor este o funcie care are cte un parametru corespunztor fiecrui atribut al tipului obiect i care returneaz o nou instan creat. Numele metodei constructor este acelai cu numele tipului obiect. Parametrii lui au numele i tipurile de date ale atributelor tipului obiect. Clauza CONSTRUCTOR permite definirea constructorilor utilizator. La definirea unui constructor este obligatorie clauza RETURN SELF AS RESULT. Acest lucru indic faptul c cel mai specific tip al valorii returnate de constructor corespunde celui mai specific tip al argumentului SELF. Funcia constructor trebuie s conin o comand RETURN simpl care nu specific explicit valoarea returnat. n acest fel, sistemul returneaz noua instan SELF definit de constructorul respectiv. 42 din 44
Un constructor definit de utilizator nu poate fi utilizat n cadrul clauzei DEFAULT dintr-o comand CREATE TABLE sau ALTER TABLE, dei metodele constructor implicite sunt permise. n SQL, parantezele sunt necesare chiar i pentru constructorii care nu au argumente, n timp ce n PL/SQL acestea sunt opionale. Pentru a fi n concordan cu limbajele orientate pe obiecte existente, a fost introdus cuvntul cheie NEW. Acesta, ns, nu este obligatoriu. Exemplu: se definete un tip t_organizator care s conin atributele cod, nume, telefon i adresa. Se va implementa un constructor care permite specificarea doar a codului i numelui organizatorului. Se va crea un bloc PL/SQL n care se vor utiliza ambii constructori (implicit i definit de utilizator).
CREATE OR REPLACE TYPE t_organizator AS OBJECT ( cod nume telefon adresa NUMBER, VARCHAR2(20), VARCHAR2(30), t_adresa,
CONSTRUCTOR FUNCTION t_organizator ( p_cod NUMBER, p_nume VARCHAR2) RETURN SELF AS RESULT );
CREATE OR REPLACE TYPE BODY t_organizator AS CONSTRUCTOR FUNCTION t_organizator ( p_cod NUMBER, p_nume VARCHAR2) RETURN SELF AS RESULT IS BEGIN SELF.cod := p_cod; SELF.nume := p_nume; SELF.telefon := 1234567; SELF.adresa := t_adresa(-1, nici o strada, NULL, NULL, NULL, NULL); RETURN; END; END; / 43 din 44
DECLARE v_org1 t_organizator; v_org2 t_organizator; BEGIN -- constructorul definit de utilizator v_org1 := NEW t_organizator(1, ABC); DBMS_OUTPUT.PUT_LINE(v_org1.adresa.strada || to_char(v_org1.cod) || v_org1.nume || v_org1.telefon); -- constructorul implicit v_org2 := t_organizator(1, XYZ, 0808080, NULL); DBMS_OUTPUT.PUT_LINE(v_org2.adresa.strada || to_char(v_org2.cod) || v_org2.nume || v_org2.telefon); END; /
Studiu individual: Tipuri colecie Motenirea tipurilor Motenirea, suprancrcarea i suprascrierea metodelor Substituirea tipurilor n ierarhia de tipuri. Ierarhii de obiecte
44 din 44