Objektorientierte PL/SQL-Programmierung

Werbung
Objektorientierte PL/SQL-Programmierung
Andriy Terletskyy
Berenberg Bank
Hamburg
Schlüsselworte:
Aufbau eines virtuellen ODBMS auf der Basis einer RDBMS, Objektorientierter ROWTYPE,
Typehierarchie
Zusammenfassung
Es wird pro Tabelle ein objektorientierter ROWTYPE (OO-ROWTYPE) erzeugt, der einen
einfachen Zugriff auf einzelne Datensätze erlaubt. Dieser OO-ROWTYPE beinhaltet
Methoden und Konstruktoren, die die üblichen DML-Anweisungen abbilden.
Auf der Basis vom SCOTT-Schema werden OO-ROWTYPEs gebaut und die Stärke der
objektorientierten PL/SQL-Programmierung hervorgehoben.
Folgende Schritte sind notwendig, um die PL/SQL-Programmierung auf dem relationalen
Datenbankmodell objektorieniert (OO) zu verwirklichen:
• Einführung des Object Type TYPE_OBJECT, analog zur Object Klasse in Java. Dies
ist die Basisklasse für alle abgeleiteten Object Types.
• Einführung von OO-ROWTYPEs, die vom TYPE_OBJECT abgeleitet sind.
• Aufbau von Business Object Types (BO-TYPEs), die Objekte der realen Welt
abbilden.
Einleitung
Für den traditionellen Einsatz der objektorientierte PL/SQL-Programmierung bietet Oracle ein
ODBMS. Bei diesem Ansatz werden erst die Oracle Object Types definiert, auf denen
objektorientierte Tabellen erzeugt werden. Für die Datennormalisierung verwendet man dort
die OROWID. Um unter der Verwendung von OROWID den Inhalt des referenzierten
Objektes zu bekommen oder eine Methode aufzurufen, verwendet man eine DEREF –
Funktion. Für die Datenmanipulation sind wie früher die üblichen DML-Anweisungen
verfügbar.
Im Gegensatz zur OO-PL/SQL-Programmierung für ODBMS, werden die hier vorgestellten
Objekt Typen auf der Basis eines bestehenden relationalen DB-Models gebaut. Damit ist ein
objektorientierter Zugriff auf die Tabelleninhalte und auf die in PL/SQL realisierten
Prozeduren möglich. Die erstellten Object Types sind ein Analogon zum Oracle
<TABLE_NAME>%ROWTYPE. Sie sind zusätzlichen mit Konstruktoren und Methoden
ausgestattet, die den üblichen DML-Anweisungen wie SELECT, INSERT, UPDATE,
DELETE, bzw. MERGE entsprechen. Somit werden die Datenmanipulationen vereinfacht. Es
stehen Konstruktoren für Primary Key und Unique Key zur Verfügung, um basierend auf
einer Id des RDBMS einen referenzierten Datensatz zu erhalten. Die TO_STRING – Methode
gibt die Representation des Types aus. Die Ausgabe von TO_STRING erfolgt über die
DBMS_OUTPUT Methode.
BO-TYPES
OO-ROWTYPES
RDBMS
Abb. 1: Schichtenmodel der Object Types für RDBMS
Mit den genannten Funktionalitäten können die Daten wie üblich auf der Basis eines
relationalen Datenbankmodells in dem RDBMS gespeichert werden. Über das virtuelle
objektorientierte Modell (OO-Schicht) sind die Zugriffe auf Tabellenspalten und die
Ausführung der sämtlichen DML-Anweisungen vereinfacht.
Auf Grund dieser OO-ROWTYPEs kann man mittels der Vererbung und der Komposition,
die so genannten Business-Objekte (BO) aufbauen, die die reale Welt abbilden. Diese BOTypes können durch Ableitung und Kombination von mehreren OO-ROWTYPEs realisiert
werden.
Realisierung
1. Einführung des Object Type TYPE_OBJECT
Oracle gibt keinerlei Ursprungs Objekthierarchie vor, d.h. es gibt kein Objekt, der Basisklasse
für alle abgeleiteten Object Types ist. Daher wird zunächst ein TYPE_OBJECT erstellt:
CREATE
CREATE OR REPLACE
REPLACE TYPE
TYPE SCOTT.TYPE_OBJECT
SCOTT.TYPE_OBJECT AS OBJECT(
OBJECT(
-- attributes
attributes
object_type_name
object_type_name VARCHAR2(100)
VARCHAR2(100)
-- member
member functions
functions and
and procedures
procedures
, MEMBER
MEMBER PROCEDURE
PROCEDURE DBMS_OUTPUT
DBMS_OUTPUT
, MEMBER
MEMBER FUNCTION
FUNCTION TO_STRING
TO_STRING RETURN
RETURN VARCHAR
VARCHAR2
, MEMBER
MEMBER FUNCTION
FUNCTION COMPARE(in_type1
COMPARE(in_type1 TYPE_OBJECT,
TYPE_OBJECT, in_type2
in_type2 TYPE_OBJECT
TYPE_OBJECT
) RETURN
RETURN INTEGER
INTEGER
, ORDER
MEMBER
ORDER
MEMBER FUNCTION
FUNCTION COMPARE2(in_other
COMPARE2(in_other TYPE_OBJECT)
TYPE_OBJECT) RETURN
RETURN INTEGER
INTEGER
) NOT
NOT FINAL
FINAL NOT
NOT INSTANTIABLE
INSTANTIABLE
Hiermit werden folgende Vorteile erzielt:
- Ein standardisiertes Interface für alle abgeleiteten Objekte, wie TO_STRING oder
DBMS_OUTPUT;
- Einfache Übergabe abgeleiteter Typen als Parameter in beliebige Prozeduren oder
Funktionen (Ursprungshierarchie);
- Definition der ORDER Funktion, die wegen der Überschreibung der CompareMethode das Vergleich die Instanzen beliebigen Typen ermöglicht.
2. Einführung von OO-ROWTYPE
Für die SCOTT-Schema Tabellen EMP und DEPT hätten die oben genannten OOROWTYPE in der ODBMS folgende Umsetzung:
CREATE
CREATE OR REPLACE
REPLACE TYPE
TYPE SCOTT.ROW_DEPT
SCOTT.ROW_DEPT UNDER
UNDER SCOTT.TYPE_OBJECT(
SCOTT.TYPE_OBJECT(
-- attributes
attributes
deptno
deptno NUMBER(2)
NUMBER(2)
, dname
VARCHAR2(14)
dname
VARCHAR2(14)
, loc
VARCHAR2(13)
loc
VARCHAR2(13)
-- define
define constructors
constructors
, CONSTRUCTOR
CONSTRUCTOR FUNCTION
FUNCTION ROW_DEPT
ROW_DEPT RETURN
RETURN SELF
SELF AS RESULT
RESULT
, CONSTRUCTOR
CONSTRUCTOR FUNCTION
FUNCTION ROW_DEPT(
ROW_DEPT( in_deptno
in_deptno NUMBER,
UMBER, in_dname
in_dname VARCHAR2
VARCHAR2
, in_loc
in_loc VARCHAR2)
VARCHAR2) RETURN
RETURN SELF
SELF AS RESULT
RESULT
, CONSTRUCTOR
CONSTRUCTOR FUNCTION
FUNCTION ROW_DEPT(in_deptno
ROW_DEPT(in_deptno NUMBER)
NUMBER) RETURN
RETURN SELF
SELF AS RESULT
RESULT
-- member
member functions
functions
, MEMBER
MEMBER FUNCTION
FUNCTION ROW_EXISTS(in_deptno
ROW_EXISTS(in_deptno NUMBER)
NUMBER) RETURN
RETURN BOOLEAN
BOOLEAN
, OVERRIDING
OVERRIDING MEMBER
MEMBER FUNCTION
FUNCTION COMPARE
COMPARE(
RE( in_type1
in_type1 GLOBAL.TYPE_OBJECT
GLOBAL.TYPE_OBJECT
, in_type2
in_type2 GLOBAL.TYPE_OBJECT
GLOBAL.TYPE_OBJECT
) RETURN
RETURN INTEGER
INTEGER
-- member
member procedures
procedures
, MEMBER
MEMBER PROCEDURE
PROCEDURE ROW_INSERT
ROW_INSERT
, MEMBER
MEMBER PROCEDURE
PROCEDURE ROW_UPDATE
ROW_UPDATE
, MEMBER
MEMBER PROCEDURE
PROCEDURE ROW_MERGE
ROW_MERGE
, MEMBER
MEMBER PROCEDURE
PROCEDURE ROW_SAVE
ROW_SAVE
, MEMBER
MEMBER PROCEDURE
PROCEDURE ROW_DELETE
ROW_DELETE
, MEMBER
MEMBER PROCEDURE
PROCEDURE ROW_SELECT(in_deptno
ROW_SELECT(in_deptno NUMBER)
NUMBER)
, MEMBE
MEMBER
R PROCEDURE
PROCEDURE ROW_DEFAULT
ROW_DEFAULT
) NOT
NOT FINAL
FINAL
CREATE
CREATE OR REPLACE
REPLACE TYPE
TYPE SCOTT.ROW_EMP
SCOTT.ROW_EMP UNDER
UNDER SCOTT.TYPE_OBJECT(
SCOTT.TYPE_OBJECT(
-- attributes
attributes
empno
NUMBER(4)
empno
NUMBER(4)
, ename
VARCHAR2(10)
ename
VARCHAR2(10)
, job
VARCHAR2(9)
job
VARCHAR2(9)
, mgr
NUMBER(4)
mgr
NUMBER(4)
, hiredat
hiredate
date DATE
DATE
, sal
NUMBER(7,2)
sal
NUMBER(7,2)
, comm
NUMBER(7,2)
comm
NUMBER(7,2)
, deptno
NUMBER(2)
deptno
NUMBER(2)
-- constructors
constructors
, CONSTRUCTOR
CONSTRUCTOR FUNCTION
FUNCTION ROW_EMP
ROW_EMP RETURN
RETURN SELF
SELF AS RESULT
RESULT
, CONSTRUCTOR
CONSTRUCTOR FUNCTION
FUNCTION ROW_EMP(
ROW_EMP( in_empno
in_empno NUMBER,
NUMBER, in_ename
in_ename VARCHAR2
VARCHAR2
, in_job
in_job VARCHAR2,
VARCHAR2, in_mgr
in_mgr NUMBER
NUMBER
, in_hiredate
in_hiredate DATE
DATE,
TE, in_sal
in_sal NUMBER
NUMBER
, in_comm
in_comm NUMBER,
NUMBER, in_deptno
in_deptno NUMBER
NUMBER
) RETURN
RETURN SELF
SELF AS RESULT
RESULT
, CONSTRUCTOR
CONSTRUCTOR FUNCTION
FUNCTION ROW_EMP(in_empno
ROW_EMP(in_empno NUMBER)
NUMBER) RETURN
RETURN SELF
SELF AS RESULT
RESULT
-- member
member functions
functions
, MEMBER
MEMBER FUNCTION
FUNCTION ROW_EXISTS(in_empno
ROW_EXISTS(in_empno NUMBER)
NUMBER) RETURN
RETURN BOOLEAN
BOOLEAN
, OVERRIDING
OVERRIDING MEMBER
MEMBER FUNCTIO
FUNCTION
ION compare(
compare( in_type1
in_type1 GLOBAL.TYPE_OBJECT
GLOBAL.TYPE_OBJECT
, in_type2
in_type2 GLOBAL.TYPE_OBJECT
GLOBAL.TYPE_OBJECT
) RETURN
RETURN INTEGER
INTEGER
-- member
member procedures
procedures
, MEMBER
MEMBER PROCEDURE
PROCEDURE ROW_INSERT
ROW_INSERT
, MEMBER
MEMBER PROCEDURE
PROCEDURE ROW_UPDATE
ROW_UPDATE
, MEMBER
MEMBER PROCEDURE
PROCEDURE ROW_MERGE
ROW_MERGE
, MEMBER
MEMBER PROCEDURE
PROCEDURE ROW_SAVE
ROW_SAVE
, MEMBER
MEMBER PROCEDURE
PROCEDURE ROW_DELETE
ROW_DELETE
, MEMBER
MEMBER PROCEDURE
PROCEDURE ROW_SELECT(in_empno
ROW_SELECT(in_empno NUMBER)
NUMBER)
, MEMBER
MEMBER PROCEDURE
PROCEDURE ROW_DEFAULT
ROW_DEFAULT
) NOT
NOT FINAL
FINAL
und die dazu gehörigen Container Typen und Packages:
CREATE
CREATE OR REPLACE
REPLACE TYPE
TYPE
CREATE
CREATE OR REPLACE
REPLACE TYPE
TYPE
SCOTT.ROW_DEPT;
COTT.ROW_DEPT;
TABLE_EMP
TABLE_EMP AS TABLE
TABLE OF SCOTT.ROW_EMP;
SCOTT.ROW_EMP;
TABLE_DEPT
TABLE_DEPT AS TABLE
TABLE OF
CREATE
CREATE OR REPLACE
REPLACE PACKAGE
PACKAGE PA_DEPT
PA_DEPT IS
FUNCTION
FUNCTION FU_SELECT
FU_SELECT RETURN
RETURN
TABLE_DEPT;
TABLE_DEPT;
END;
END;
CREATE
CREATE OR REPLACE
REPLACE PACKAGE
PACKAGE PA_EMP
PA_EMP IS
FUNCTION
FUNCTION FU_SELECT
FU_SELECT RETURN
RETURN
TABLE_EMP;
TABLE_EMP;
FUNCTION
FUNCTION FS_DEPTNO(IN_DEPTNO
FS_DEPTNO(IN_DEPTNO IN EMP.DEPTNO%TYPE)
EMP.DEPTNO%TYPE) RETURN
RETURN
TABLE_EMP;
TABLE_EMP; FUNCTION
FUNCTION FS_MGR(IN_MG
FS_MGR(IN_MGR
MGR IN EMP.MGR%TYPE)
EMP.MGR%TYPE) RETURN
RETURN
TABLE_EMP;
TABLE_EMP;
END;
END;
Man erkennt, dass die gebauten Typen vergleichbar zu den <TABLE_NAME>%ROWTYPE
sind, aber weitere Funktionalitäten abbilden.
Zu diesen gehören :
- Konstruktoren für Primary- und Unique Keys;
- Funktionen:
o ROW_EXIST - prüft ob die Datensatz mit angegeben Primary oder Unique
Key in der Datenbank schon vorhanden ist;
o COMPARE – vergleicht zweier Instanzen.
- Prozeduren:
o ROW_INSERT,
ROW_UPDATE,
ROW_DELETE, ROW_MERGE,
ROW_SELECT – die entsprechende DML-Anweisungen ausüben;
o ROW_DEFAULT – setzt aus der Tabellendefinition die Defaultwerte in die
dazu gehörige Attribute;
o ROW_SAVE – ersetzt die MERGE- Anweisung, und enthält im Gegensatz
zum Oracle-MERGE eine RETURNING Clause.
Zusätzlich wird für jeden OO-ROWTYPE ein Container Type und eine oder mehrere DataCartridges für die Foreign Keys und die gesamte Tabelle erzeugt. Mit Data-Cartridge ist eine
Funktionen gemeint, welche die Container Tabelle zurück liefert. Leider erlaubt Oracle nicht,
im Type eigene Container Types zu verwenden. Deswegen sind diese Data-Cartridges in
einem seperaten Package hingelegt.
Die manuelle Erstellung die OO-ROWTYPEs ist sehr aufwendig und fehlerträchtig, hier hilft
ein in Java selbstgeschriebener Generator, der die Oracle Dictionary Definitionen ausliest und
diese Arbeit erledigt. Der Generator ist in das DBMS eingebunden und wird über ein Wrapper
Skript aufgerufen.
3. Aufbau der Business Object Types (BO-TYPEs)
Auf der Basis des generierten Sources, der Vererbung und Komposition wird über so
genannte Busines Objekte, der Aufbau der realen Welt abgebildet.
Dies ist im vorliegende Fall:
-
TYPE_MANAGER – ist vom ROW_EMP vererbt und beinhaltet alle untergeordneten
Mitarbeiter, die er leitet.
CREATE
OR
REPLACE
TYPE
TYPE_MANAGER
UNDER
CREATE
REPLACE
TYPE
TYPE_MANAGER
UNDER
ROW_EMP(
ROW_EMP(
-- attributes
attributes
EMPLOYEES
EMPLOYEES TABLE_EMP
TABLE_EMP
-- constructors
constructors
, CONSTRUCTOR
AS
CONSTRUCTOR FUNCTION
FUNCTION TYPE_MANAGER
TYPE_MANAGER RETURN
RETURN SELF
SELF
RESULT
RESULT
, CONSTRUCTOR
CONSTRUCTOR FUNCTION
FUNCTION TYPE_MANAGER(IN_EMPNO
TYPE_MANAGER(IN_EMPNO NUMBER)
NUMBER) RETURN
RETURN
RESULT
RESULT
) NOT
NOT FINAL
FINAL
-
SELF
SELF
AS
TYPE_DEPARTMENT – ist vom ROW_DEPT abgeleitet und beinhaltet alle Mitarbeiter
die in der Abteilung tätig sind, so wie den leitenden Manager.
CREATE
OR
REPLACE
TYPE
TYPE_DEPARTMENT
UNDER
CREATE
REPLACE
TYPE
TYPE_DEPARTMENT
UNDER
ROW_DEPT(
ROW_DEPT(
-- attributes
attributes
EMPLOYEES
EMPLOYEES TABLE_EMP
TABLE_EMP
-- constructor
constructors
, CONSTRUCTOR
AS
CONSTRUCTOR FUNCTION
FUNCTION TYPE_DEPARTMENT
TYPE_DEPARTMENT RETURN
RETURN SELF
SELF
RESULT
RESULT
, CONSTRUCTOR
CONSTRUCTOR FUNCTION
FUNCTION TYPE_DEPARTMENT(in_deptno
TYPE_DEPARTMENT(in_deptno NUMBER)
NUMBER) RETURN
RETURN SELF
SELF
AS
RESULT
RESULT
-- member
member functions
functions
,
MEMBER
FUNCTION
MEMBER
FUNCTION
TYPE_MANAGER
TYPE_MANAGER
) NOT
NOT FINAL
FINAL
GET_MANAGER
GET_MANAGER
RETURN
RETURN
-
TYPE_ENTERPRISE – ist vom TYPE_OBJECT
vererbt und besteht
Unternehmungsname, Vorstandvorsitzendem, Abteilungen und Mitarbeitern.
aus
CREATE
UNDER
R TYPE_OBJECT(
CREATE OR REPLACE
REPLACE TYPE
TYPE TYPE_ENTERPRISE
TYPE_ENTERPRISE UNDE
TYPE_OBJECT(
-- attributes
attributes
NAME
VARCHAR2(100)
NAME
VARCHAR2(100)
, PRESIDENT
TYPE_MANAGER
PRESIDENT
TYPE_MANAGER
, DEPARTMENT
DEPARTMENTS TABLE_DEPT
TABLE_DEPT
, EMPLOYEES
TABLE_EMP
EMPLOYEES
TABLE_EMP
-- constructors
constructors
, CONSTRUCTOR
CONSTRUCTOR FUNCTION
FUNCTION TYPE_ENTERPRISE
TYPE_ENTERPRISE RETURN
RETURN SELF
SELF AS RESULT
RESULT
) NOT
NOT FINAL
FINAL
Die erzeugten Objekt-Typen und Container lassen sich einfach mit SELECT –Anweisungen
aufrufen:
SQL>
SQL>
SELECT
SELECT TYPE_DEPARTMENT(20)
TYPE_DEPARTMENT(20) FROM
FROM
SQL>
SQL>
SELECT
SELECT VALUE(e)
VALUE(e) FROM
FROM
DUAL;
DUAL;
TYPE_ENTERPRISE()(OBJECT_TYPE_NAME, DEPTNO, DNAME, LOC, NAME, PRESIDENT(OBJECT_T
------------------------------------------------------------------------------------------------------------------TYPE_DEPARTMENT('TYPE_DEPARTMENT', 20, 'RESEARCH', 'DALLAS',
TABLE_EMP(
ROW_EMP('ROW_EMP', 7369, 'SMITH', 'CLERK', 7902, '17.12.80', 800, NULL, 20),
ROW_EMP('ROW_EMP', 7566, 'JONES', 'MANAGER', 7839, '02.04.81', 2975, NULL, 20),
ROW_EMP('ROW_EMP', 7788, 'SCOTT', 'ANALYST', 7566, '19.04.87', 3000, NULL, 20),
ROW_EMP('ROW_EMP', 7876, 'ADAMS', 'CLERK', 7788, '23.05.87', 1100, NULL, 20),
ROW_EMP('ROW_EMP', 7902, 'FORD', 'ANALYST', 7566, '03.12.81', 3000, NULL, 20)
)
)
TABLE
TABLE
(TYPE_MANAGER(7698).EMPLOYEES)
(TYPE_MANAGER(7698).EMPLOYEES) e;
VALUE(E)(OBJECT_TYPE_NAME, EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
------------------------------------------------------------------------------------------------------------------ROW_EMP('ROW_EMP', 7499, 'ALLEN', 'SALESMAN', 7698, '20.02.81', 1600, 300, 30)
ROW_EMP('ROW_EMP', 7521, 'WARD', 'SALESMAN', 7698, '22.02.81', 1250, 500, 30)
ROW_EMP('ROW_EMP', 7654, 'MARTIN', 'SALESMAN', 7698, '28.09.81', 1250, 1400, 30)
ROW_EMP('ROW_EMP', 7844, 'TURNER', 'SALESMAN', 7698, '08.09.81', 1500, 0, 30)
ROW_EMP('ROW_EMP', 7900, 'JAMES', 'CLERK', 7698, '03.12.81', 950, NULL, 30)
Oder mit PL/SQL-Skript:
DECLARE
e TYPE_ENTERPRISE := TYPE_ENTERPRISE();
BEGIN
e.dbms_output;
END;
Entsprechendes DBMS Output:
SCOTT.TYPE_ENTERPRISE
(
OBJECT_TYPE_NAME = TYPE_ENTERPRISE
NAME
= King Corporation
PRESIDENT
= SCOTT.TYPE_MANAGER
(
OBJECT_TYPE_NAME =
EMPNO
=
ENAME
=
JOB
=
MGR
=
HIREDATE
=
SAL
=
COMM
=
DEPTNO
=
EMPLOYEES
=
TYPE_MANAGER
7839
KING
PRESIDENT
17.11.1981 00:00:00
5000
10
< SCOTT.TABLE_EMP >
)
)
DEPARTMENTS
= < SCOTT.TABLE_DEPT >
EMPLOYEES
= <
SCOTT.TABLE_EMP >
Die PL/SQL-Programmierung würde jetzt so ähnlich aussehen:
DECLARE
m TYPE_MANAGER;
BEGIN
m := TYPE_MANAGER(7839);
m.sal := m.sal * 2;
m.row_update;
m.dbms_output;
END;
Entsprechendes DBMS Output:
SCOTT.TYPE_MAN
AGER (
OBJECT_TYPE_NAME =
TYPE_MANAGER EMPNO
7839
ENAME
=
JOB
=
MGR
=
HIREDATE
=
SAL
=
COMM
=
DEPTNO
=
EMPLOYEES
=
)
=
KING
PRESIDENT
17.11.1981 00:00:00
10000
10
< SCOTT.TABLE_EMP >
Hiermit gelingt es, eine virtuelle ODBMS auf dem Basis eines RDBMS aufzubauen
und
PL/SQL –Programmierung für RBDMS objectorientiert zu machen.
Das gebaute Model hat folgende Vorteile :
- keine Funktionalitätsverluste gegeüber der bestehenden ODBMS Lösung von Oracle
- Das Objektmodel entspricht dem relationalen Datenmodel und kann ohne Änderung
am relationalen Datenmodel erzeugt und verwendet werden
- Vereinfachung der DML-Anweisungen
- Zugriffe auf manuell geschriebenen Business Objekte sind unabhängig von
der unterliegenden Datenquelle
- Auf die erstellten Objekte und deren Methoden lassen sich über Generatoren (wie
z.B.
JPublisher oder selbst geschriebene) die passenden Zugriffsklassen für z.B. Java
erstellen
Zur Nachbesserung von Architekturnachteilen und Bugs, wären gewünscht:
- die Oracle Object-Typen unterstützen nicht ROWID, %TYPE und INDEX BY TABLE
- ein Type kann nicht Container von eigenen Instanzen referenzieren
- ein SUPER –Attribut, wie es z.B. in Java existiert, fehlt
- die entsprechendes Gegenstück der z.B. in Java existierenden Interfaces fehlt
(deswegen beinhaltet TYPE_OBJECT das überflüssige Attribute
OBJECT_TYPE_NAME)
- ein Type, der von anderen Type vererbt ist, oder von Container referenziert ist,
kann mittels REPLACE nicht mehr ersetzt werden (nur ALTER TYPE
Anweisung ist für
solche Änderungen vorgesehen). Momentan ist nur ein Umweg über DROP TYPE
force möglich.
Vollständige Sourcen:
CREATE OR REPLACE TYPE TYPE_OBJECT AS OBJECT(
-- Attributes
object_type_name VARCHAR2(100)
-- Member functions and procedures
, MEMBER PROCEDURE DBMS_OUTPUT
, MEMBER FUNCTION TO_STRING RETURN VARCHAR2
, MEMBER FUNCTION COMPARE(in_type1 TYPE_OBJECT, in_type2 TYPE_OBJECT
JECT)
CT)
INTEGER
, ORDER
MEMBER FUNCTION COMPARE2(in_other TYPE_OBJECT) RETURN INTEGER
) NOT FINAL NOT INSTANTIABLE
/
CREATE OR REPLACE TYPE BODY TYPE_OBJECT
IS
MEMBER PROCEDURE DBMS_OUTPUT
IS
str
VARCHAR2(32767) := SELF.TO_STRING();
sub
VARCHAR2(32767);
len
PLS_INTEGER
:= LENGTH(str);
von
PLS_INTEGER
:= 1;
bis
PLS_INTEGER
:= INSTR(str,CHR(10),von);
BEGIN
LOOP
IF bis = 0 THEN bis := len+1; END IF;
EXIT WHEN von>bis;
sub := SUBSTR(str,von,bis-von);
FOR j IN 0..(LENGTH(sub)/255) LOOP
SYS.DBMS_OUTPUT.PUT_LINE(SUBSTR(sub,j*255+1,255));
END LOOP;
von := bis+1;
bis := INSTR(str,CHR(10),von);
END LOOP;
END;
-----------------------------------------MEMBER FUNCTION TO_STRING RETURN VARCHAR2
IS
res
VARCHAR2(32767);
TYPE ANYTYPE_INFO IS RECORD (prec PLS_INTEGER,
scale PLS_INTEGER,
len PLS_INTEGER,
csid PLS_INTEGER,
csfrm PLS_INTEGER,
schema_name VARCHAR2(35),
type_name VARCHAR2(35),
version varchar2(10)
10),
count PLS_INTEGER);
TYPE ATTRIBUTE_INFO IS RECORD (pos PLS_INTEGER,
prec PLS_INTEGER,
scale PLS_INTEGER,
len PLS_INTEGER,
csid PLS_INTEGER,
csfrm PLS_INTEGER,
attr_elt_type ANYTYPE,
aname VARCHAR2(50));
t_info
ANYTYPE_INFO;
o_info
ANYTYPE_INFO;
t_att
ATTRIBUTE_INFO;
v_AnyData
SYS.ANYDATA;
v_AnyType
SYS.ANYTYPE;
v_TypeCode
PLS_INTEGER;
v_ObjCode
PLS_INTEGER;
v_maxAttSize
PLS_INTEGER := 0;
v_char
CHAR(4000);
v_varchar
VARCHAR(4000);
v_raw
RAW(4000);
v_varchar2
VARCHAR2(4000);
v_number
NUMBER;
v_date
DATE;
RETURN
v_object
TYPE_OBJECT;
v_value
VARCHAR2(4000);
v_clob
CLOB;
v_blob
BLOB;
BEGIN
v_AnyData := AnyData.ConvertObject(SELF);
v_TypeCode := v_AnyData.Gettype(v_AnyType);
v_TypeCode
:=
v_anytype.GetInfo(t_info.prec,
t_info.scale,
t_info.len,
t_info.csid, t_info.csfrm, t_info.schema_name, t_info.type_name, t_info.version,
t_info.count);
res := v_AnyData.GetTypeName();
res := res||CHR(10)||'('||CHR(10);
-- max Attribute size übermitteln
FOR pos IN 1..t_info.COUNT LOOP
v_TypeCode
:=
v_anytype.GetAttrElemInfo(pos,
t_att.prec,
t_att.scale,
t_att.len, t_att.csid, t_att.csfrm, t_att.attr_elt_type, t_att.aname);
v_maxAttSize := GREATEST(v_maxAttSize,LENGTH(t_att.aname));
END LOOP;
--sets the mode of access of the current data value to be an attribute at a time
v_AnyData.PieceWise;
-- Attribute Name übermitteln
FOR pos IN 1..t_info.COUNT LOOP
v_TypeCode
:=
v_anytype.GetAttrElemInfo(pos,
t_att.prec,
t_att.scale,
t_att.len, t_att.csid, t_att.csfrm, t_att.attr_elt_type, t_att.aname);
BEGIN
-- Attribute Value übermitteln
CASE v_TypeCode
WHEN DBMS_TYPES.TYPECODE_VARCHAR2 THEN -- 2archar2
IF v_AnyData.GetVarchar2(v_varchar2) = dbms_types.success THEN
v_value := v_varchar2;
END IF;
WHEN DBMS_TYPES.TYPECODE_CHAR THEN -- Char
IF v_AnyData.GetChar(v_Char) = dbms_types.success THEN
v_value := RTRIM(v_Char);
END IF;
WHEN DBMS_TYPES.TYPECODE_RAW THEN -- Raw
Raw
IF v_AnyData.GetRaw(v_Raw) = dbms_types.success THEN
v_value := v_Raw;
END IF;
WHEN DBMS_TYPES.TYPECODE_VARCHAR THEN -- Varchar
IF v_AnyData.GetVarchar(v_Varchar) = dbms_types.success THEN
v_value := v_Varchar;
END IF;
WHEN DBMS_TYPES.TYPECODE_NUMBER THEN -- Number
IF v_AnyData.GetNumber(v_number) = dbms_types.success THEN
v_value := TO_CHAR(v_number);
END IF;
WHEN DBMS_TYPES.TYPECODE_DATE THEN -- Date
IF v_AnyData.GetDate(v_date) = dbms_types.success THEN
v_value := TO_CHAR(v_date,'DD.MM.YYYY HH24:MI:SS');
END IF;
WHEN DBMS_TYPES.TYPECODE_OBJECT THEN -- Object
IF v_AnyData.GetObject(v_Object) = dbms_types.success THEN
v_value
:=
REPLACE(v_Object.TO_STRING(),CHR(10),RPAD(CHR(10),v_maxAttSize+5));
END IF;
WHEN DBMS_TYPES.TYPECODE_CLOB THEN -- CLO
CLOB
IF v_AnyData.GetCLOB(v_clob) = dbms_types.success THEN
v_value := '<CLOB> size = '||DBMS_LOB.Getlength(v_clob);
END IF;
WHEN DBMS_TYPES.TYPECODE_BLOB THEN -- BLO
BLOB
IF v_AnyData.GetBLOB(v_blob) = dbms
dbms_
ms_types.success THEN
v_value := '<BLOB> size = '||DBMS_LOB.Getlength(v_blob);
END IF;
WHEN DBMS_TYPES.TYPECODE_TABLE THEN -- TABLE
v_value := '<NESTED TABLE>';
WHEN DBMS_TYPES.TYPECODE_VARRAY THEN -- VAR
VARRAY
v_value := '<VARRAY>';
WHEN DBMS_TYPES.TYPECODE_NAMEDCOLLECTION THEN -- NAMEDCOLLECTION
v_ObjCode :=
t_att.attr_elt_type.GetInfo(o_info.prec, o_info.scale,
o_info.len,
o_info.csid,
o_info.csfrm,
o_info.schema_name,
o_info.type_name,
o_info.version, o_info.count);
v_value := '< '||o_info.schema_name||'.'||o_info.type_name||' > ';
-- v_AnyData.GetCollection Engpass !!! Abstract Collection
Interface
mangelt
ELSE -- others
v_value := '!!! UNBEKANNTE DATEN
TYP !!! DBM
DBMS_TYPES.TYPECODE = '||
v_TypeCode;
END CASE;
EXCEPTION
WHEN OTHERS THEN
v_value := ' ERROR:'||SQLERRM;
END;
IF
LENGTH(res||'
'||RPAD(t_att.aname,v_maxAttSize)||'
=
'||v_value||CHR(10))< 32700 THEN
res := res||' '||RPAD(t_att.aname,v_maxAttSize)||' = '||v_value||CHR(10);
ELSE
res := res||CHR(10)||'ERROR : Maximale Größe VARCHAR2(32767) erreicht.';
END IF;
END LOOP;
res := res||')';
RETURN res;
EXCEPTION
WHEN OTHERS THEN
RETURN 'ERROR : '|| SQLERRM;
END;
-----------------------------------------MEMBER FUNCTION
compare(in_type1 TYPE_OBJECT, in_type2 TYPE_OBJECT) RETURN
INTEGER
IS
BEGIN
RETURN 1; -- default immer ungleich
END;
-----------------------------------------ORDER MEMBER FUNCTION compare2(in_other TYPE_OBJECT)
RETURN INTEGER
IS
BEGIN
RETURN compare(SELF,in_other);
END;
END;
/
CREATE OR REPLACE TYPE ROW_DEPT UNDER TYPE_OBJECT(
-- attributes
deptno NUMBER(2)
, dname
VARCHAR2(14)
, loc
VARCHAR2(13)
-- constructors
, CONSTRUCTOR FUNCTION ROW_DEPT RETURN SELF AS RESULT
, CONSTRUCTOR FUNCTION ROW_DEPT(in_deptno NUMBER, in_dname VARCHAR2, in_loc
VARCHAR2) RETURN SELF AS RESULT
, CONSTRUCTOR FUNCTION ROW_DEPT(in_deptno NUMBER) RETURN SELF AS RESULT
-- member functions
, MEMBER FUNCTION ROW_EXISTS(in_deptno NUMBER) RETURN BOOLEAN
, OVERRIDING MEMBER FUNCTION compare(in_type1 TYPE_OBJECT,in_type2
TYPE_
YPE_O
E_OBJECT)
RETURN INTEGER
-- member procedures
, MEMBER PROCEDURE ROW_INSERT
, MEMBER PROCEDURE ROW_UPDATE
, MEMBER PROCEDURE ROW_MERGE
, MEMBER PROCEDURE ROW_SAVE
, MEMBER PROCEDURE ROW_DELETE
, MEMBER PROCEDURE ROW_SELECT(in_deptno NUMBER)
, MEMBER PROCEDURE ROW_DEFAULT
) NOT FINAL
/
CREATE OR REPLACE TYPE BODY ROW_DEPT IS
-- constructors
CONSTRUCTOR FUNCTION ROW_DEPT RETURN SELF AS RESULT
IS
BEGIN
SELF.OBJECT_TYPE_NAME := 'ROW_DEPT';
ROW_DEFAULT();
RETURN;
END;
----------------------------------------------------------------------------------CONSTRUCTOR FUNCTION ROW_DEPT(in_deptno NUMBER, in_dname VARCHAR2, in_loc
VARCHAR2) RETURN SELF AS RESULT
IS
BEGIN
SELF.OBJECT_TYPE_NAME := 'ROW_DEPT';
SELF.DEPTNO := IN_DEPTNO;
SELF.DNAME := IN_DNAME;
SELF.LOC
:= IN_LOC;
RETURN;
END;
----------------------------------------------------------------------------------CONSTRUCTOR FUNCTION ROW_DEPT(in_deptno NUMBER) RETURN SELF AS RESULT
IS
BEGIN
SELF.OBJECT_TYPE_NAME := 'ROW_DEPT';
ROW_SELECT(IN_DEPTNO => IN_DEPTNO);
RETURN;
END;
----------------------------------------------------------------------------------MEMBER FUNCTION ROW_EXISTS(in_deptno NUMBER) RETURN BOOLEAN
IS
v_count PLS_INTEGER;
BEGIN
SELECT COUNT(*)
INTO v_count
FROM SCOTT.DEPT
WHERE DEPTNO = IN_DEPTNO;
RETURN (v_count <> 0);
END;
------------------------------------------------------------------------------------ member functions
OVERRIDING MEMBER FUNCTION compare(in_type1 TYPE_OBJECT,in_type2 TYPE_OBJECT)
RETURN INTEGER
IS
type1
ROW_DEPT := TREAT(in_type1 AS ROW_DEPT);
type2
ROW_DEPT := TREAT(in_type2 AS ROW_DEPT);
BEGIN
IF
type1.DEPTNO = type2.DEPTNO
AND ( type1.DNAME = type2.DNAME OR (type1.DNAME IS NULL AND type2.DNAME
IS NULL) )
AND ( type1.LOC
= type2.LOC
OR (type1.LOC
IS NULL AND type2.LOC
IS NULL) )
THEN
RETURN 0; --gleich
ELSE
RETURN 1; --ungleich
END IF;
END;
------------------------------------------------------------------------------------ member procedures
MEMBER PROCEDURE ROW_INSERT
IS
BEGIN
INSERT INTO SCOTT.DEPT ( DEPTNO
,DNAME
,LOC
) VALUES ( SELF.DEPTNO
,SELF.DNAME
,SELF.LOC
) RETURNING
DEPTNO
,DNAME
,LOC
INTO
SELF.DEPTNO
,SELF.DNAME
,SELF.LOC
;
END;
----------------------------------------------------------------------------------MEMBER PROCEDURE ROW_UPDATE
IS
BEGIN
UPDATE SCOTT.DEPT
SET DNAME
= SELF.DNAME
,LOC
= SELF.LOC
WHERE DEPTNO = SELF.DEPTNO
RETURNING DEPTNO
,DNAME
,LOC
INTO SELF.DEPTNO
,SELF.DNAME
,SELF.LOC
;
IF SQL%ROWCOUNT <> 1 THEN
RAISE NO_DATA_FOUND;
END IF;
END;
----------------------------------------------------------------------------------MEMBER PROCEDURE ROW_MERGE
IS
BEGIN
MERGE INTO SCOTT.DEPT A
USING ( SELECT SELF.DEPTNO AS DEPTNO
,SELF.DNAME AS DNAME
,SELF.LOC
AS LOC
FROM DUAL ) B
ON (
A.DEPTNO = B.DEPTNO)
WHEN MATCHED THEN UPDATE SET
DNAME
= B.DNAME
,LOC
= B.LOC
WHEN NOT MATCHED THEN INSERT ( DEPTNO
,DNAME
,LOC
) VALUES ( B.DEPTNO
,B.DNAME
,B.LOC
);
END;
----------------------------------------------------------------------------------MEMBER PROCEDURE ROW_SAVE
IS
BEGIN
IF ROW_EXISTS(IN_DEPTNO => SELF.DEPTNO) THEN
ROW_UPDATE;
ELSE
ROW_INSERT;
END IF;
END;
----------------------------------------------------------------------------------MEMBER PROCEDURE ROW_DELETE
IS
BEGIN
DELETE FROM SCOTT.DEPT
WHERE DEPTNO = SELF.DEPTNO;
IF SQL%ROWCOUNT <> 1 THEN
RAISE NO_DATA_FOUND;
END IF;
END;
----------------------------------------------------------------------------------MEMBER PROCEDURE ROW_SELECT(in_deptno NUMBER)
IS
BEGIN
SELECT DEPTNO
,DNAME
,LOC
INTO SELF.DEPTNO
,SELF.DNAME
,SELF.LOC
FROM SCOTT.DEPT
WHERE DEPTNO = IN_DEPTNO;
END;
-------------------------------------------------------------- MEMBER PROCEDURE ROW_DEFAULT
IS
BEGIN
NULL;
END;
END;
/
CREATE OR REPLACE TYPE ROW_EMP UNDER SCOTT.TYPE_OBJECT(
-- attributes
empno
NUMBER(4)
, ename
VARCHAR2(10)
, job
VARCHAR2(9)
, mgr
NUMBER(4)
, hiredate DATE
, sal
NUMBER(7,2)
, comm
NUMBER(7,2)
, deptno
NUMBER(2)
-- constructors
, CONSTRUCTOR FUNCTION ROW_EMP RETURN SELF AS RESULT
, CONSTRUCTOR FUNCTION ROW_EMP(in_empno NUMBER, in_ename VARCHAR2, in_job
VARCHAR2,
in_mgr NUMBER, in_hiredate DATE, in_sal NUMBER, in_comm NUMBER, in_deptno
NUMBER)
RETURN SELF AS RESULT
, CONSTRUCTOR FUNCTION ROW_EMP(in_empno NUMBER) RETURN SELF AS RESULT
-- member functions
, MEMBER FUNCTION ROW_EXISTS(in_empno NUMBER) RETURN BOOLEAN
, OVERRIDING MEMBER FUNCTION compare(in_type1 TYPE_OBJECT,in_type2
TYPE_OBJECT)
RETURN INTEGER
-- member procedures
, MEMBER PROCEDURE ROW_INSERT
, MEMBER PROCEDURE ROW_UPDATE
, MEMBER PROCEDURE ROW_MERGE
, MEMBER PROCEDURE ROW_SAVE
, MEMBER PROCEDURE ROW_DELETE
, MEMBER PROCEDURE ROW_SELECT(in_empno NUMBER)
, MEMBER PROCEDURE ROW_DEFAULT
) NOT FINAL
/
CREATE OR REPLACE TYPE BODY ROW_EMP IS
-- constructors
CONSTRUCTOR FUNCTION ROW_EMP RETURN SELF AS RESULT
IS
BEGIN
SELF.OBJECT_TYPE_NAME := 'ROW_EMP';
ROW_DEFAULT();
RETURN;
END;
----------------------------------------------------------------------------------CONSTRUCTOR FUNCTION ROW_EMP(in_empno NUMBER, in_ename VARCHAR2, in_job
VARCHAR2,
in_mgr NUMBER, in_hiredate DATE, in_sal NUMBER, in_comm NUMBER, in_deptno
NUMBER)
RETURN SELF AS RESULT
IS
BEGIN
SELF.OBJECT_TYPE_NAME := 'ROW_EMP';
SELF.EMPNO
:= EMPNO;
SELF.ENAME
:= ENAME;
SELF.JOB
:= JOB;
SELF.MGR
:= MGR;
SELF.HIREDATE := HIREDATE;
SELF.SAL
:= SAL;
SELF.COMM
:= COMM;
SELF.DEPTNO
:= DEPTNO;
RETURN;
END;
----------------------------------------------------------------------------------CONSTRUCTOR FUNCTION ROW_EMP(in_empno NUMBER) RETURN SELF AS RESULT
IS
BEGIN
SELF.OBJECT_TYPE_NAME := 'ROW_EMP';
ROW_SELECT(IN_EMPNO =>
IN_EMPNO); RETURN;
END;
----------------------------------------------------------------------------------MEMBER FUNCTION ROW_EXISTS(in_empno NUMBER) RETURN BOOLEAN
IS
v_count PLS_INTEGER;
BEGIN
SELECT COUNT(*)
INTO v_count
FROM SCOTT.EMP
WHERE EMPNO = IN_EMPNO;
RETURN (v_count <> 0);
END;
------------------------------------------------------------------------------------ member functions
OVERRIDING MEMBER FUNCTION compare(in_type1 TYPE_OBJECT,in_type2 TYPE_OBJECT
JECT)
CT)
RETURN INTEGER
IS
type1
ROW_EMP := TREAT(in_type1 AS ROW_EMP);
type2
ROW_EMP := TREAT(in_type2 AS ROW_EMP);
BEGIN
IF
type1.EMPNO
= type2.EMPNO
AND ( type1.ENAME
= type2.ENAME
OR (type1.ENAME
IS NULL AND
type2.ENAME
IS NULL) )
AND ( type1.JOB
= type2.JOB
OR (type1.JOB
IS NULL AND
type2.JOB
IS NULL) )
AND ( type1.MGR
= type2.MGR
OR (type1.MGR
IS NULL AND
type2.MGR
IS NULL) )
AND
( type1.HIREDATE = type2.HIREDATE OR (type1.HIREDATE IS NULL
AND
type2.HIREDATE IS NULL) )
AND ( type1.SAL
= type2.SAL
OR (type1.SAL
IS NULL AND
type2.SAL
IS NULL) )
AND ( type1.COMM
= type2.COMM
OR (type1.COMM
IS NULL AND
type2.COMM
IS NULL) )
AND ( type1.DEPTNO
= type2.DEPTNO
OR (type1.DEPTNO
IS NULL AND
type2.DEPTNO
IS NULL) )
THEN
RETURN 0; --gleich
ELSE
RETURN 1; --ungleich
END IF;
END;
------------------------------------------------------------------------------------ member procedures
MEMBER PROCEDURE ROW_INSERT
IS
BEGIN
INSERT INTO SCOTT.EMP ( EMPNO
,ENAME
,JOB
,MGR
,HIREDATE
,SAL
,COMM
,DEPTNO
) VALUES ( SELF.EMPNO
,SELF.ENAME
,SELF.JOB
,SELF.MGR
,SELF.HIREDATE
,SELF.SAL
,SELF.COMM
,SELF.DEPTNO
) RETURNING
EMPNO
,ENAME
,JOB
,MGR
,HIREDATE
,SAL
INTO
,COMM
,DEPTNO
SELF.EMPNO
,SELF.ENAME
,SELF.JOB
,SELF.MGR
,SELF.HIREDATE
,SELF.SAL
,SELF.COMM
,SELF.DEPTNO
;
END;
----------------------------------------------------------------------------------MEMBER PROCEDURE ROW_UPDATE
IS
BEGIN
UPDATE SCOTT.EMP
SET ENAME
= SELF.ENAME
,JOB
= SELF.JOB
,MGR
= SELF.MGR
,HIREDATE = SELF.HIREDATE
,SAL
= SELF.SAL
,COMM
= SELF.COMM
,DEPTNO
= SELF.DEPTNO
WHERE EMPNO = SELF.EMPNO
RETURNING EMPNO
,ENAME
,JOB
,MGR
,HIREDATE
,SAL
,COMM
,DEPTNO
INTO SELF.EMPNO
,SELF.ENAME
,SELF.JOB
,SELF.MGR
,SELF.HIREDATE
,SELF.SAL
,SELF.COMM
,SELF.DEPTNO
;
IF SQL%ROWCOUNT <> 1 THEN
RAISE NO_DATA_FOUND;
END IF;
END;
----------------------------------------------------------------------------------MEMBER PROCEDURE ROW_MERGE
IS
BEGIN
MERGE INTO SCOTT.EMP A
USING ( SELECT SELF.EMPNO
AS EMPNO
,SELF.ENAME
AS ENAME
,SELF.JOB
AS JOB
,SELF.MGR
AS MGR
,SELF.HIREDATE AS HIREDATE
,SELF.SAL
AS SAL
,SELF.COMM
AS COMM
,SELF.DEPTNO
AS DEPTNO
FROM DUAL ) B
ON (
A.EMPNO = B.EMPNO)
WHEN MATCHED THEN UPDATE SET
ENAME
= B.ENAME
,JOB
= B.JOB
,MGR
= B.MGR
MGR
,HIREDATE = B.HIREDATE
,SAL
= B.SAL
,COMM
= B.COMM
,DEPTNO
= B.DEPTNO
WHEN NOT MATCHED THEN INSERT ( EMPNO
,ENAME
,JOB
,MGR
,HIREDATE
,SAL
,COMM
,DEPTNO
) VALUES ( B.EMPNO
,B.ENAME
,B.JOB
,B.MGR
,B.HIREDATE
,B.SAL
,B.COMM
,B.DEPTNO
);
END;
----------------------------------------------------------------------------------MEMBER PROCEDURE ROW_SAVE
IS
BEGIN
IF ROW_EXISTS(IN_EMPNO => SELF.EMPNO) THEN
ROW_UPDATE;
ELSE
ROW_INSERT;
END IF;
END;
----------------------------------------------------------------------------------MEMBER PROCEDURE ROW_DELETE
IS
BEGIN
DELETE FROM SCOTT.EMP
WHERE EMPNO = SELF.EMPNO;
IF SQL%ROWCOUNT <> 1 THEN
RAISE NO_DATA_FOUND;
END IF;
END;
----------------------------------------------------------------------------------MEMBER PROCEDURE ROW_SELECT(in_empno NUMBER)
IS
BEGIN
SELECT EMPNO
,ENAME
,JOB
,MGR
,HIREDATE
,SAL
,COMM
,DEPTNO
INTO SELF.EMPNO
,SELF.ENAME
,SELF.JOB
,SELF.MGR
,SELF.HIREDATE
,SELF.SAL
,SELF.COMM
,SELF.DEPTNO
FROM SCOTT.EMP
WHERE EMPNO = IN_EMPNO;
END;
-------------------------------------------------------------------------------------MEMBER PROCEDURE ROW_DEFAULT
IS
BEGIN
NULL;
END;
END;
/
CREATE OR REPLACE TYPE TABLE_DEPT AS TABLE OF SCOTT.ROW_DEPT;
/
CREATE OR REPLACE TYPE TABLE_EMP AS TABLE OF SCOTT.ROW_EMP;
/
CREATE OR REPLACE PACKAGE PA_EMP IS
FUNCTION FU_SELECT RETURN
TABLE_EMP;
FUNCTION FS_DEPTNO(IN_DEPTNO IN EMP.DEPTNO%TYPE) RETURN
TABLE_EMP; FUNCTION FS_MGR(IN_MGR IN EMP.MGR%TYPE) RETURN
TABLE_EMP;
END;
/
CREATE OR REPLACE PACKAGE BODY PA_EMP IS
FUNCTION FU_SELECT RETURN TABLE_EMP
IS
result TABLE_EMP;
BEGIN
SELECT ROW_EMP('ROW_EMP'
,EMPNO
,ENAME
,JOB
,MGR
,HIREDATE
,SAL
,COMM
,DEPTNO
)
BULK COLLECT INTO result
FROM SCOTT.EMP;
RETURN result;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN TABLE_EMP();
WHEN OTHERS THEN
RAISE;
END;
----------------------------------------------------------------FUNCTION FS_DEPTNO(IN_DEPTNO IN EMP.DEPTNO%TYPE) RETURN TABLE_EMP
IS
result TABLE_EMP;
BEGIN
SELECT ROW_EMP('ROW_EMP'
,EMPNO
,ENAME
,JOB
,MGR
,HIREDATE
,SAL
,COMM
,DEPTNO
)
BULK COLLECT INTO result
FROM SCOTT.EMP
WHERE (DEPTNO = IN_DEPTNO OR (DEPTNO IS NULL AND IN_DEPTNO IS NULL));
RETURN result;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN TABLE_EMP();
WHEN OTHERS THEN
RAISE;
END;
----------------------------------------------------------------FUNCTION FS_MGR(IN_MGR IN EMP.MGR%TYPE) RETURN TABLE_EMP
IS
result TABLE_EMP;
BEGIN
SELECT ROW_EMP('ROW_EMP'
,EMPNO
,ENAME
,JOB
,MGR
,HIREDATE
,SAL
,COMM
,DEPTNO
)
BULK COLLECT INTO result
FROM SCOTT.EMP
WHERE (MGR = IN_MGR OR (MGR IS NULL AND IN_MGR IS NULL));
RETURN result;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN TABLE_EMP();
WHEN OTHERS THEN
RAISE;
END;
END;
/
CREATE OR REPLACE PACKAGE PA_DEPT IS
FUNCTION FU_SELECT RETURN TABLE_DEPT;
END;
/
CREATE OR REPLACE PACKAGE BODY PA_DEPT IS
FUNCTION FU_SELECT RETURN TABLE_DEPT
IS
result TABLE_DEPT;
BEGIN
SELECT ROW_DEPT('ROW_DEPT'
,DEPTNO
,DNAME
,LOC
)
BULK COLLECT INTO result
FROM SCOTT.DEPT;
RETURN result;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN TABLE_DEPT();
WHEN OTHERS THEN
RAISE;
END;
END;
/
CREATE OR REPLACE TYPE TYPE_MANAGER UNDER ROW_EMP(
-- attributes
EMPLOYEES TABLE_EMP
-- constructors
, CONSTRUCTOR FUNCTION TYPE_MANAGER RETURN SELF AS RESULT
, CONSTRUCTOR FUNCTION TYPE_MANAGER(IN_EMPNO NUMBER) RETURN SELF AS RESULT
) NOT FINAL
/
CREATE OR REPLACE TYPE BODY TYPE_MANAGER IS
-- constructors
CONSTRUCTOR FUNCTION TYPE_MANAGER RETURN SELF AS RESULT
IS
BEGIN
SELF.OBJECT_TYPE_NAME := 'TYPE_MANAGER';
SELF.ROW_DEFAULT();
SELF.EMPLOYEES := TABLE_EMP();
RETURN;
END;
---------------------------------------------------------------------------------------------CONSTRUCTOR FUNCTION TYPE_MANAGER(IN_EMPNO NUMBER) RETURN SELF AS RESULT
IS
BEGIN
SELF.OBJECT_TYPE_NAME := 'TYPE_MANAGER';
SELF.ROW_SELECT(IN_EMPNO => IN_EMPNO);
SELF.EMPLOYEES := PA_EMP.FS_MGR(IN_EMPNO);
RETURN;
END;
END;
/
CREATE OR REPLACE TYPE TYPE_DEPARTMENT UNDER ROW_DEPT(
-- attributes
EMPLOYEES TABLE_EMP
-- constructors
, CONSTRUCTOR FUNCTION TYPE_DEPARTMENT RETURN SELF AS RESULT
, CONSTRUCTOR FUNCTION TYPE_DEPARTMENT(in_deptno NUMBER) RETURN SELF AS RESULT
-- member functions
, MEMBER FUNCTION GET_MANAGER RETURN TYPE_MANAGER
) NOT FINAL
/
CREATE OR REPLACE TYPE BODY TYPE_DEPARTMENT IS
-- constructors
CONSTRUCTOR FUNCTION TYPE_DEPARTMENT RETURN SELF AS RESULT
IS
BEGIN
SELF.OBJECT_TYPE_NAME := 'TYPE_DEPARTMENT';
SELF.ROW_DEFAULT();
SELF.EMPLOYEES := TABLE_EMP();
RETURN;
END;
----------------------------------------------------------------------------------CONSTRUCTOR FUNCTION TYPE_DEPARTMENT(in_deptno NUMBER) RETURN SELF
IS
BEGIN
SELF.OBJECT_TYPE_NAME := 'TYPE_DEPARTMENT';
SELF.ROW_SELECT(IN_DEPTNO => IN_DEPTNO);
SELF.EMPLOYEES := PA_EMP.FS_DEPTNO(IN_DEPTNO);
RETURN;
END;
-- member functions
MEMBER FUNCTION GET_MANAGER RETURN TYPE_MANAGER
IS
t_manager TYPE_MANAGER;
BEGIN
SELECT TYPE_MANAGER(e.empno)
INTO t_manager
FROM EMP e
WHERE e.deptno = SELF.DEPTNO
AND e.job = 'MANAGER'
;
RETURN t_manager;
END;
END;
/
CREATE OR REPLACE TYPE TYPE_ENTERPRISE UNDER TYPE_OBJECT(
-- attributes
NAME
VARCHAR2(100)
, PRESIDENT
TYPE_MANAGER
, DEPARTMENTS TABLE_DEPT
, EMPLOYEES
TABLE_EMP
-- constructors
, CONSTRUCTOR FUNCTION TYPE_ENTERPRISE RETURN SELF AS RESULT
) NOT FINAL
/
CREATE OR REPLACE TYPE BODY TYPE_ENTERPRISE IS
-- constructors
CONSTRUCTOR FUNCTION TYPE_ENTERPRISE RETURN SELF AS RESULT
IS
BEGIN
SELF.OBJECT_TYPE_NAME := 'TYPE_ENTERPRISE';
SELF.NAME := 'King Corporation';
SELF.PRESIDENT := TYPE_MANAGER(7839);
SELF.DEPARTMENTS := PA_DEPT.FU_SELECT;
SELF.EMPLOYEES := PA_EMP.FU_SELECT;
RETURN;
END;
END;
/
AS RESULT
Herunterladen