Geschäftslogik in der Datenbank Architektur und Objektorientierte PL/SQL-Programmierung Andriy Terletskyy Lead Database Architect Hamburg, 17.06.2010 Geschäftslogik Geschäftslogik (engl. Business Logic, auch Anwendungslogik) ist ein abstrakter Begriff in der Softwaretechnik, der eine Abgrenzung der durch die Aufgabenstellung selbst motivierten Logik eines Softwaresystems von der technischen Implementierung zum Ziel hat. Allerdings ist der Begriff unscharf, da eine klare Trennung oft nicht möglich ist. • Mehrfache BL-Umsetzung • Datenbank Abhängigkeit • Datenverarbeitungs- und Datenerfassungslogik Java FAT-Client ASP.NET • Oracle EE ist mehr als eine Datenbank (Speicherort) • SQL und PL/SQL Engine • DBMS-Packages • Object Types Cobol Daten-Import Datenbank 2 12.06.2012 Geschäftslogik in der Datenbank • Type Mapping (Java&.NET) • JPublisher, SQLData, STRUCT, ODP.NET Datenbanken Architektur Makler FIX SWIFT E-Börsen Data-Warehouse Handelssystem Log log Log Log Log Log Transaktionen Bestandstamm Log Ausführung GattungStamm KundenStamm GattungStamm Log Bestandstamm Order KundenStamm Transaktionen Ausführung Abrechnung Buchung Order Back-Office Portfolio Transaktionen Bestandstamm GattungStamm KundenStamm (Data-HUBs) Abrechnung Aufrührung Reporting Bestandstamm Order Buchung Buchung GattungStamm KundenStamm 3 12.06.2012 Geschäftslogik in der Datenbank Virtuelle ODBMS auf Basis RDBMS (DOAG Konferenz 2004) BO-TYPES OO-ROWTYPES RDBMS 4 12.06.2012 Geschäftslogik in der Datenbank Aufbau • OO-ROWTYPE (ROWTYPE + DML, DEFAULT, TO_STRING, DBMS_OUTPUT- Methoden, PK/UKKonstruktoren, ROW_LOCK) • BO-Typen (Vererbung, Aggregation, Assoziation) Verwendung • Methoden statt üblicher DML- Anweisungen • Datennormalisierung über RDBMS • PK/UK- Konstruktoren - ziehen referenziertes Objekt über relationale ID (DEREF- Analogon) Generator • Java-Class • PL/SQL-Wrapper • DDL-Trigger mit asynchroner Queue Tabelle und OO-Rowtype (Beispiel) CREATE OR REPLACE TYPE ROW_STATUS UNDER GLOBAL.TYPE_OBJECT ( -- attributes STATUS_ID NUMBER(12) , KURZBEZEICHNUNG VARCHAR2(50) , ERSTELLT_ZST DATE , ERSTELLT_ID VARCHAR2(30) , GEAENDERT_ZST DATE , GEAENDERT_ID NUMBER(12) , GEAENDERT_ZAEHLER NUMBER(10) , BEZEICHNUNG VARCHAR2(200) CREATE OR REPLACE TRIGGER TBI$STATUS BEFORE INSERT ON WPH.STATUS FOR EACH ROW WHEN ( NEW.STATUS_ID IS NULL) DECLARE BEGIN SELECT STATUS_SEQ.NEXTVAL INTO :NEW.STATUS_ID FROM DUAL; END; -------------------------------------------------------------------------------------------- CREATE OR REPLACE TRIGGER TBU$STATUS BEFORE UPDATE ON WPH.STATUS FOR EACH ROW BEGIN :new.Geaendert_ID := SYS_CONTEXT('VPD', 'USERID'); :new.Geaendert_ZST := sysdate; :new.Geaendert_Zaehler := :old.Geaendert_Zaehler + 1; end; 5 12.06.2012 Geschäftslogik in der Datenbank -- constructors , CONSTRUCTOR FUNCTION ROW_STATUS RETURN SELF AS RESULT , CONSTRUCTOR FUNCTION ROW_STATUS(IN_STATUS_ID NUMBER) RETURN SELF AS RESULT -- member functions , MEMBER FUNCTION ROW_EXISTS(IN_STATUS_ID NUMBER) RETURN BOOLEAN , OVERRIDING MEMBER FUNCTION compare(in_type1 GLOBAL.TYPE_OBJECT ,in_type2 GLOBAL.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_STATUS_ID NUMBER) , MEMBER PROCEDURE ROW_DEFAULT , MEMBER PROCEDURE ROW_LOCK , MEMBER PROCEDURE ROW_LOCK(IN_STATUS_ID NUMBER) ) NOT FINAL RETURNING clause (INSERT und UPDATE) MEMBER PROCEDURE ROW_INSERT IS methodenname CONSTANT VARCHAR2(100) := 'WPH.ROW_STATUS.ROW_INSERT'; BEGIN IF SYS_CONTEXT('TRACE','TRACE_LEVEL') >= GLOBAL.PA_TRACE.TRACE_TRACE THEN GLOBAL.PA_TRACE.TRACE( GLOBAL.PA_TRACE.TRACE_TRACE, methodenname, 'Parameters: '); END IF; INSERT INTO WPH.STATUS ( STATUS_ID ,KURZBEZEICHNUNG ,BEZEICHNUNG ) VALUES ( SELF.STATUS_ID ,SELF.KURZBEZEICHNUNG ,SELF.BEZEICHNUNG ) RETURNING STATUS_ID ,KURZBEZEICHNUNG ,ERSTELLT_ZST ,ERSTELLT_ID ,GEAENDERT_ZST ,GEAENDERT_ID ,GEAENDERT_ZAEHLER ,BEZEICHNUNG INTO SELF.STATUS_ID ,SELF.KURZBEZEICHNUNG ,SELF.ERSTELLT_ZST ,SELF.ERSTELLT_ID ,SELF.GEAENDERT_ZST ,SELF.GEAENDERT_ID ,SELF.GEAENDERT_ZAEHLER ,SELF.BEZEICHNUNG ; EXCEPTION WHEN OTHERS THEN GLOBAL.PA_TRACE.TRACE( GLOBAL.PA_TRACE.TRACE_ERROR, methodenname, ' - Exception raised. ' ||SELF.TO_STRING() ||', SQLcode=' || to_char(SQLCODE) || ', ' || SQLERRM); RAISE; END; 6 12.06.2012 Geschäftslogik in der Datenbank SAVE und MERGE MEMBER PROCEDURE ROW_SAVE IS methodenname CONSTANT VARCHAR2(100) := 'WPH.ROW_STATUS.ROW_SAVE'; BEGIN IF SYS_CONTEXT('TRACE','TRACE_LEVEL') >= GLOBAL.PA_TRACE.TRACE_TRACE THEN GLOBAL.PA_TRACE.TRACE( GLOBAL.PA_TRACE.TRACE_TRACE, methodenname, 'Parameters: '); END IF; IF ROW_EXISTS(IN_STATUS_ID => SELF.STATUS_ID) THEN SELF.ROW_UPDATE; ELSE SELF.ROW_INSERT; END IF; EXCEPTION WHEN OTHERS THEN GLOBAL.PA_TRACE.TRACE( GLOBAL.PA_TRACE.TRACE_ERROR, methodenname, ' - Exception raised. ' ||SELF.TO_STRING() ||', SQLcode=' || to_char(SQLCODE) || ', ' || SQLERRM); RAISE; END; --------------------------------------------------------------- MEMBER PROCEDURE ROW_MERGE IS methodenname CONSTANT VARCHAR2(100) := 'WPH.ROW_STATUS.ROW_MERGE'; BEGIN IF SYS_CONTEXT('TRACE','TRACE_LEVEL') >= GLOBAL.PA_TRACE.TRACE_TRACE THEN GLOBAL.PA_TRACE.TRACE( GLOBAL.PA_TRACE.TRACE_TRACE, methodenname, 'Parameters: '); END IF; MERGE INTO WPH.STATUS A USING ( SELECT SELF.STATUS_ID AS STATUS_ID ,SELF.KURZBEZEICHNUNG AS KURZBEZEICHNUNG ,SELF.BEZEICHNUNG AS BEZEICHNUNG FROM DUAL ) B ON (A.STATUS_ID = B.STATUS_ID) WHEN MATCHED THEN UPDATE SET KURZBEZEICHNUNG = B.KURZBEZEICHNUNG ,BEZEICHNUNG = B.BEZEICHNUNG WHEN NOT MATCHED THEN INSERT ( STATUS_ID ,KURZBEZEICHNUNG ,BEZEICHNUNG ) VALUES ( B.STATUS_ID ,B.KURZBEZEICHNUNG ,B.BEZEICHNUNG ); EXCEPTION WHEN OTHERS THEN GLOBAL.PA_TRACE.TRACE( GLOBAL.PA_TRACE.TRACE_ERROR, methodenname, ' - Exception raised. ' ||SELF.TO_STRING() ||', SQLcode=' || to_char(SQLCODE) || ', ' || SQLERRM); RAISE; END; 7 12.06.2012 Geschäftslogik in der Datenbank Pessimistisches Locking - Default MEMBER PROCEDURE ROW_LOCK IS methodenname CONSTANT VARCHAR2(100) := 'WPH.ROW_STATUS.ROW_LOCK'; v_lock NUMBER; BEGIN IF SYS_CONTEXT('TRACE','TRACE_LEVEL') >= GLOBAL.PA_TRACE.TRACE_TRACE THEN GLOBAL.PA_TRACE.TRACE( GLOBAL.PA_TRACE.TRACE_TRACE, methodenname, 'Parameters: '); END IF; SELECT GEAENDERT_ZAEHLER INTO v_lock FROM WPH.STATUS WHERE STATUS_ID = SELF.STATUS_ID FOR UPDATE ; IF SELF.GEAENDERT_ZAEHLER <> v_lock THEN SELF.ROW_SELECT( IN_STATUS_ID => SELF.STATUS_ID); END IF; EXCEPTION WHEN OTHERS THEN GLOBAL.PA_TRACE.TRACE( GLOBAL.PA_TRACE.TRACE_ERROR, methodenname, ' - Exception raised. ' ||SELF.TO_STRING() ||', SQLcode=' || to_char(SQLCODE) || ', ' || SQLERRM); RAISE; END; declare i ROW_STATUS; begin i := ROW_STATUS(IN_STATUS_ID => 200); ... i.ROW_LOCK(); end; 8 12.06.2012 Geschäftslogik in der Datenbank Pessimistisches Locking mit PK oder UK MEMBER PROCEDURE ROW_LOCK(IN_STATUS_ID NUMBER) IS methodenname CONSTANT VARCHAR2(300) := 'WPH.ROW_STATUS.ROW_LOCK(IN_STATUS_ID NUMBER)'; BEGIN IF SYS_CONTEXT('TRACE','TRACE_LEVEL') >= GLOBAL.PA_TRACE.TRACE_TRACE THEN GLOBAL.PA_TRACE.TRACE( GLOBAL.PA_TRACE.TRACE_TRACE, methodenname, 'Parameters: ' ||'IN_STATUS_ID = '||IN_STATUS_ID||';' ); END IF; SELECT STATUS_ID ,KURZBEZEICHNUNG ,ERSTELLT_ZST declare ,ERSTELLT_ID i ROW_STATUS; ,GEAENDERT_ZST ,GEAENDERT_ID begin ,GEAENDERT_ZAEHLER ,BEZEICHNUNG i := ROW_STATUS(); INTO SELF.STATUS_ID ,SELF.KURZBEZEICHNUNG i.ROW_LOCK(IN_STATUS_ID ,SELF.ERSTELLT_ZST ,SELF.ERSTELLT_ID end; ,SELF.GEAENDERT_ZST ,SELF.GEAENDERT_ID ,SELF.GEAENDERT_ZAEHLER ,SELF.BEZEICHNUNG FROM WPH.STATUS WHERE STATUS_ID = IN_STATUS_ID FOR UPDATE ; EXCEPTION WHEN OTHERS THEN GLOBAL.PA_TRACE.TRACE( GLOBAL.PA_TRACE.TRACE_ERROR, methodenname, ' - Exception raised. ' ||'IN_STATUS_ID = '||IN_STATUS_ID||';' ||', SQLcode=' || to_char(SQLCODE) || ', ' || SQLERRM); RAISE; END; 9 12.06.2012 Geschäftslogik in der Datenbank => 200); Optimistisches Locking - UPDATE MEMBER PROCEDURE ROW_UPDATE IS methodenname CONSTANT VARCHAR2(100) := 'WPH.ROW_STATUS.ROW_UPDATE'; BEGIN IF SYS_CONTEXT('TRACE','TRACE_LEVEL') >= GLOBAL.PA_TRACE.TRACE_TRACE THEN GLOBAL.PA_TRACE.TRACE( GLOBAL.PA_TRACE.TRACE_TRACE, methodenname, 'Parameters: '); END IF; UPDATE WPH.STATUS SET KURZBEZEICHNUNG = SELF.KURZBEZEICHNUNG ,BEZEICHNUNG = SELF.BEZEICHNUNG WHERE STATUS_ID = SELF.STATUS_ID AND GEAENDERT_ZAEHLER = SELF.GEAENDERT_ZAEHLER RETURNING STATUS_ID ,KURZBEZEICHNUNG ,ERSTELLT_ZST ,ERSTELLT_ID ,GEAENDERT_ZST ,GEAENDERT_ID ,GEAENDERT_ZAEHLER ,BEZEICHNUNG INTO SELF.STATUS_ID ,SELF.KURZBEZEICHNUNG ,SELF.ERSTELLT_ZST ,SELF.ERSTELLT_ID ,SELF.GEAENDERT_ZST ,SELF.GEAENDERT_ID ,SELF.GEAENDERT_ZAEHLER ,SELF.BEZEICHNUNG ; IF SQL%ROWCOUNT <> 1 THEN GLOBAL.RAISE(-20999); END IF; -- Der Datensatz hat sich zwischenzeitlich geändert EXCEPTION WHEN OTHERS THEN GLOBAL.PA_TRACE.TRACE( GLOBAL.PA_TRACE.TRACE_ERROR, methodenname, ' - Exception raised. ' ||SELF.TO_STRING() ||', SQLcode=' || to_char(SQLCODE) || ', ' || SQLERRM); RAISE; END; 10 12.06.2012 Geschäftslogik in der Datenbank Optimistisches Locking - DELETE MEMBER PROCEDURE ROW_DELETE IS methodenname CONSTANT VARCHAR2(100) := 'WPH.ROW_STATUS.ROW_DELELE'; BEGIN IF SYS_CONTEXT('TRACE','TRACE_LEVEL') >= GLOBAL.PA_TRACE.TRACE_TRACE THEN GLOBAL.PA_TRACE.TRACE( GLOBAL.PA_TRACE.TRACE_TRACE, methodenname, 'Parameters: '); END IF; DELETE FROM WPH.STATUS WHERE STATUS_ID = SELF.STATUS_ID AND GEAENDERT_ZAEHLER = SELF.GEAENDERT_ZAEHLER; IF SQL%ROWCOUNT <> 1 THEN GLOBAL.RAISE(-20999); END IF; -- Der Datensatz hat sich zwischenzeitlich geändert EXCEPTION WHEN OTHERS THEN GLOBAL.PA_TRACE.TRACE( GLOBAL.PA_TRACE.TRACE_ERROR, methodenname, ' - Exception raised. ' ||SELF.TO_STRING() ||', SQLcode=' || to_char(SQLCODE) || ', ' || SQLERRM); RAISE; END; 11 12.06.2012 Geschäftslogik in der Datenbank Variablen und OO-ROWTYPE declare s VARCHAR2(50); begin SELECT s.kurzbezeichnung INTO s FROM WPH.STATUS s WHERE s.sTATUS_ID=200; end; declare s WPH.STATUS%ROWTYPE; begin SELECT * INTO s FROM WPH.STATUS s WHERE s.sTATUS_ID=200; end; 12 12.06.2012 Geschäftslogik in der Datenbank declare s WPH.STATUS.KURZBEZEICHNUNG%TYPE; begin SELECT s.kurzbezeichnung INTO s FROM WPH.STATUS s WHERE s.STATUS_ID=200; end; declare s WPH.ROW_STATUS; begin s := WPH.ROW_STATUS(IN_STATUS_ID => 200); end; Arbeiten mit OO-ROWTYPE declare i ROW_STATUS; begin i := ROW_STATUS(IN_STATUS_ID => 200); i.DBMS_OUTPUT; i.KURZBEZEICHNUNG := 'Eingestellt'; i.ROW_UPDATE; i.DBMS_OUTPUT; end; <ROW_STATUS> <OBJECT_TYPE_NAME>WPH.ROW_STATUS</OBJECT_TYPE_NAME> <STATUS_ID>200</STATUS_ID> <KURZBEZEICHNUNG>Einstellung</KURZBEZEICHNUNG> <ERSTELLT_ZST>12.07.02</ERSTELLT_ZST> <ERSTELLT_ID>-2</ERSTELLT_ID> <GEAENDERT_ZST>14.08.04</GEAENDERT_ZST> <GEAENDERT_ID>1469</GEAENDERT_ID> <GEAENDERT_ZAEHLER>2</GEAENDERT_ZAEHLER> <BEZEICHNUNG>ORDERAUSFUEHRUNG-STATUS</BEZEICHNUNG> </ROW_STATUS> <ROW_STATUS> <OBJECT_TYPE_NAME>WPH.ROW_STATUS</OBJECT_TYPE_NAME> <STATUS_ID>200</STATUS_ID> <KURZBEZEICHNUNG>Eingestellt</KURZBEZEICHNUNG> <ERSTELLT_ZST>12.07.02</ERSTELLT_ZST> <ERSTELLT_ID>-2</ERSTELLT_ID> <GEAENDERT_ZST>15.06.10</GEAENDERT_ZST> <GEAENDERT_ID>907</GEAENDERT_ID> <GEAENDERT_ZAEHLER>3</GEAENDERT_ZAEHLER> <BEZEICHNUNG>ORDERAUSFUEHRUNG-STATUS</BEZEICHNUNG> </ROW_STATUS> 13 12.06.2012 Geschäftslogik in der Datenbank Gattungsstamm – Input Daten G DE000AAR0082AAR0081900000640329 00"GD090 09062010"GD100 01"GD100A 1"GD160 004"GD161 DE"GD170 EO "GD171 EUR"GD172 EUR"GD190 PD"GD195 227"GD196 1"GD198A SEC"GD198B 2000"GD198C 2003"GD198D ZZZZ"GD198E ZZZZ"GD198F E302"GD198G M401"GD200 00025"GD201 DE03472 "GD205B KA05"GD211 1"GD212 703"GD213 25"GD214 22"GD215A J"GD217 AAC"GD218 J"GD218A J"GD218B J"GD218C J"GD220 142"GD220A 004"GD220C 183"GD225 A"GD226 60"GD227 1"GD228 0413"GD230 MTH"GD234 1"GD240 804110"GD260 AAREAL BANK MTN.HPF.S.67 "GD270A Aareal Bank AG "GD270B MTN-HPF.S.67 v.2010(2013) "GD280A Serie 67 v. 2010 (2013) "GD290 16092010"GD290A 16092010"GD300 16092013"GD312 6"GD321 10"GD322 16062010"GD323 15092013"GD388A 686665"GD388PIDE0006866650"GD400 03"GD410 9"GD420 7"GD423 J"GD424 B"GD430 099"GD440 2"GD455A 000001000000000000"GD455E 1"GD473 ²NOM=1000,W=EUR; "GD481A 05"GD483 1"GD500 84"GD504A J"GD505B 25000000000"GD505E 2"GD507 0550000"GD545 1"GD545A 2"GD546 09062010"GD571 J"GD572 J"GD621 099"GD622 DE000AAR0082"GD622PWAAR008"GD630A 000000500000000000"GD630B EUR"GD650A 000000500000000000"GD660 16062010"GD663A N"GD663D N"GD669 0000997590000"GD670A EUR"GD670B %"GD672 1"GD685 DTFSFB"GD697 2B"GD776 3"GD801A 0001750000000"GD805 F"GD810 093"GD811 1"GD812 16"GD812A 16"GD813 SEPT"GD813A SEPT"GD815 0092"GD815A 0044109589"GD819 2"GD821B 09"GD822 01"GD841 2"GD861A 000000100000000000"GD862 16092013"GD910 16092013"GD924 N"GD970G 01"GD986 ²deutsches Recht G DE000AAR0082AAR0081909920640329 00"GV992 ²0660/J/ / / / / / / / / / / / / ²0348/J/ / / / / / / / / / / / / ²2597/J/ / / / / / / / / / / / / G DE000AAR0082AAR0081909950640329 00"GV995 ²0660/ / / / / / / / / /J/ / / / ²0348/ / / / / / / / / / /J/ / / ²2597/ / / / / / / / / / /J/ / / E NO00030753019065612010060932000 00"ED001 2"ED002A 1"ED004A DD"ED023 1"ED024A 01012009"ED025A 31122009"ED190 02 G NO00030753019065611910000603643 00"GD090 29062010"GD260 PETROLIA DRILLING Navne-Aksjer NK 5 "GD460A 000000005000000000 NK 5"GD270A Petrolia Drilling ASA "GD270B H NO00030753019065612010032905600200"HD008 28062010"HD015 1300 U NO00030753019065612010060940500 00"UD001 1"UD002A 1"UD005 1"UD006 1"UD007 4"UD008A AZ"UD010A ST "UD010C 0000010000000"UD010D 0000000100000000"UD011A ST "UD011C 0000100000000"UD011D 0000000010000000"UD021 906561"UD021PINO0003075301"UD025 29062010²ca. "UD034 28062010"UD078 20"UD087 049"U D190 02"UD460A 000000000500000000"UD460B 000000005000000000 14 12.06.2012 Geschäftslogik in der Datenbank Gattungsstamm – Datenbank Model 15 12.06.2012 Geschäftslogik in der Datenbank Schwergewichtiger TYPE CREATE OR REPLACE TYPE TYPE_GATTUNG AUTHID CURRENT_USER UNDER GLOBAL.TYPE_OBJECT ( -- Attributes id NUMBER(12) , isin CHAR(12) , wkn CHAR(6) , sh NUMBER(2) , geloescht DATE , name VARCHAR2(80) , instrument VARCHAR2(80) , fids WP.TABLE_WMFID , ertraege WP.TABLE_ERTRAG , kapitals WP.TABLE_KAPITAL , ohc WP.TABLE_GATTUNG_OHC , hauptversammlung WP.TABLE_HAUPTVERSAMMLUNG , bogenerneuerung WP.TABLE_BOGENERNEUERUNG , umtausch WP.TABLE_UMTAUSCH , verlosung WP.TABLE_VERLOSUNG , termin WP.TABLE_GATTUNG_TERMIN , opposition WP.TABLE_OPPOSITION , ifp WP.TABLE_WMFID , NWKN VARCHAR2(4000) -- Nationale WKN -- Methods , CONSTRUCTOR FUNCTION TYPE_GATTUNG RETURN SELF AS RESULT , CONSTRUCTOR FUNCTION TYPE_GATTUNG(in_id NUMBER) RETURN SELF AS RESULT , CONSTRUCTOR FUNCTION TYPE_GATTUNG(IN_ID NUMBER, IN_DATUM DATE) RETURN SELF AS RESULT , CONSTRUCTOR FUNCTION TYPE_GATTUNG(in_isin VARCHAR2, in_sh NUMBER DEFAULT 0) RETURN SELF AS RESULT , CONSTRUCTOR FUNCTION TYPE_GATTUNG(in_wkn VARCHAR2, in_sh NUMBER DEFAULT 0) RETURN SELF AS RESULT , CONSTRUCTOR FUNCTION TYPE_GATTUNG(in_wkn VARCHAR2, in_sh NUMBER, in_datum DATE) RETURN SELF AS RESULT , CONSTRUCTOR FUNCTION TYPE_GATTUNG(in_isin VARCHAR2, in_wkn VARCHAR2, in_sh NUMBER DEFAULT 0, in_geloescht DATE DEFAULT NULL) RETURN SELF AS RESULT , CONSTRUCTOR FUNCTION TYPE_GATTUNG(in_isin VARCHAR2, in_wkn VARCHAR2, in_instrument VARCHAR2) RETURN SELF AS RESULT , MEMBER PROCEDURE add_ertrag(in_ertrag WP.TYPE_ERTRAG) , MEMBER PROCEDURE add_hauptversammlung(in_hauptversammlung WP.TYPE_HAUPTVERSAMMLUNG) , MEMBER PROCEDURE add_bogenerneuerung(in_bogenerneuerung WP.TYPE_BOGENERNEUERUNG) , MEMBER PROCEDURE add_kapital(in_kapital WP.TYPE_KAPITAL) , MEMBER PROCEDURE add_umtausch(in_umtausch WP.TYPE_UMTAUSCH) , MEMBER PROCEDURE add_verlosung(in_verlosung WP.TYPE_VERLOSUNG) , MEMBER PROCEDURE add_ohc(in_ohc WP.TYPE_GATTUNG_OHC) , MEMBER PROCEDURE add_termin(in_termin WP.TYPE_GATTUNG_TERMIN) , MEMBER PROCEDURE add_opposition(in_opposition WP.TYPE_OPPOSITION) , MEMBER PROCEDURE INIT_KAPITAL_DETAIL(SELF IN OUT TYPE_GATTUNG) , MEMBER FUNCTION GET_KAPITAL_DETAIL(SELF IN OUT TYPE_GATTUNG) RETURN WP.TABLE_KAPITAL , MEMBER PROCEDURE INIT_FID_DETAIL(SELF IN OUT TYPE_GATTUNG) , MEMBER FUNCTION GET_FID_DETAIL(SELF IN OUT TYPE_GATTUNG) RETURN WP.TABLE_WMFID , MEMBER PROCEDURE INIT_SCD_FIDS(IN_DATUM DATE DEFAULT NULL) , MEMBER PROCEDURE INIT_OHC(IN_BOERSENPLATZ_ID VARCHAR2) ,… ) NOT FINAL 16 12.06.2012 Geschäftslogik in der Datenbank Schwergewichtiger TYPE (Fortsetzung) CREATE OR REPLACE TYPE TABLE_ERTRAG AS TABLE OF WP.TYPE_ERTRAG; -----------------------------------------------------------------------------------------------------------------------CREATE OR REPLACE TYPE TYPE_ERTRAG UNDER WP.ROW_ERTRAG_BASE ( -- Attributes id NUMBER(12) , fids WP.TABLE_WMFID -- Methods , CONSTRUCTOR , CONSTRUCTOR , CONSTRUCTOR RESULT FUNCTION TYPE_ERTRAG RETURN SELF AS RESULT FUNCTION TYPE_ERTRAG(in_wmdaten WP.TYPE_WMDATEN) RETURN SELF AS RESULT FUNCTION TYPE_ERTRAG(OBJECT_TYPE_NAME VARCHAR2, ID NUMBER, BID_NR VARCHAR2, DATUM DATE, FIDS WP.TABLE_WMFID) RETURN SELF AS , CONSTRUCTOR , CONSTRUCTOR FUNCTION TYPE_ERTRAG(BID_NR VARCHAR2, DATUM DATE, FIDs WP.TABLE_WMFID) RETURN SELF AS RESULT FUNCTION TYPE_ERTRAG(in_BID_NR VARCHAR2, in_DATUM VARCHAR2, in_FIDs WP.TABLE_WMFID) RETURN SELF AS RESULT , CONSTRUCTOR FUNCTION TYPE_ERTRAG(IN_ERTRAG_BASE_ID NUMBER, IN_DATUM DATE DEFAULT NULL) RETURN SELF AS RESULT , MEMBER PROCEDURE INIT_SCD_FIDS(IN_DATUM DATE DEFAULT NULL) , OVERRIDING MEMBER FUNCTION compare(in_type1 GLOBAL.TYPE_OBJECT, in_type2 GLOBAL.TYPE_OBJECT) RETURN INTEGER , MEMBER PROCEDURE getDBid(in_gattung_id NUMBER) , MEMBER PROCEDURE save(in_gattung_id NUMBER,IN_DATUM DATE DEFAULT SYSDATE) , MEMBER FUNCTION TO_VF1 RETURN GLOBAL.TABLE_VARCHAR2 ) NOT FINAL --------------------------------------------------------------------------------------------------------------------------CREATE OR REPLACE TYPE ROW_ERTRAG_BASE UNDER GLOBAL.TYPE_OBJECT (-- attributes ERTRAG_BASE_ID NUMBER(12) , GATTUNG_BASE_ID NUMBER(12) , BID_NR VARCHAR2(5) , DATUM DATE , ERSTELLT_ZST DATE , GEAENDERT_ZST DATE , ERSTELLT_ID NUMBER(12) , GEAENDERT_ID NUMBER(12) , ERSTELLT_IP VARCHAR2(30) , GEAENDERT_IP VARCHAR2(30) , GEAENDERT_ZAEHLER NUMBER(10) -- define constructors , CONSTRUCTOR FUNCTION ROW_ERTRAG_BASE RETURN SELF AS RESULT , CONSTRUCTOR FUNCTION ROW_ERTRAG_BASE(ERTRAG_BASE_ID NUMBER, GATTUNG_BASE_ID NUMBER, BID_NR ,… ) NOT FINAL 17 12.06.2012 Geschäftslogik in der Datenbank Schwergewichtiger TYPE & Load Balancing MEMBER PROCEDURE INIT_KAPITAL_DETAIL(SELF IN OUT TYPE_GATTUNG) IS methodenname CONSTANT VARCHAR2(300) := 'WP.'||$$PLSQL_UNIT||'.INIT_KAPITAL_DETAIL'; FUNCTION parm RETURN VARCHAR2 IS BEGIN RETURN substr( 'Parameter: ' || CHR(10) || 'SELF:' || SELF.TO_STRING(),1,32760); END; BEGIN IF SYS_CONTEXT('TRACE','TRACE_LEVEL') >= GLOBAL.PA_TRACE.TRACE_TRACE THEN GLOBAL.PA_TRACE.TRACE( GLOBAL.PA_TRACE.TRACE_TRACE, methodenname, parm()); END IF; SELF.kapitals := WP.TABLE_KAPITAL(); FOR rec IN (SELECT d.kapital_base_id FROM WP.Kapital_Base d WHERE d.gattung_base_id = SELF.id ORDER BY datum ) LOOP SELF.kapitals.EXTEND; SELF.kapitals(SELF.kapitals.LAST) := WP.TYPE_KAPITAL(in_kapital_base_id => rec.kapital_base_id); END LOOP; EXCEPTION WHEN OTHERS THEN GLOBAL.pa_trace.TRACE( GLOBAL.pa_trace.TRACE_ERROR, methodenname, parm()||CHR(10)|| ', SQLcode=' || to_char(SQLCODE) || ', ' || SQLERRM); RAISE; END; --------------------------------------------------------------MEMBER FUNCTION GET_KAPITAL_DETAIL(SELF IN OUT TYPE_GATTUNG) RETURN WP.TABLE_KAPITAL IS methodenname CONSTANT VARCHAR2(300) := 'WP.'||$$PLSQL_UNIT||'.GET_KAPITAL_DETAIL'; FUNCTION parm RETURN VARCHAR2 IS BEGIN RETURN substr('Parameter: ' || CHR(10) || 'SELF:' || SELF.TO_STRING(),1,32760); END; BEGIN IF SYS_CONTEXT('TRACE','TRACE_LEVEL') >= GLOBAL.PA_TRACE.TRACE_TRACE THEN GLOBAL.PA_TRACE.TRACE( GLOBAL.PA_TRACE.TRACE_TRACE, methodenname, parm()); END IF; IF SELF.kapitals IS NULL THEN SELF.INIT_KAPITAL_DETAIL(); END IF; RETURN SELF.kapitals; EXCEPTION WHEN OTHERS THEN GLOBAL.pa_trace.TRACE( GLOBAL.pa_trace.TRACE_ERROR, methodenname, parm()||CHR(10)|| ', SQLcode=' || to_char(SQLCODE) || ', ' || SQLERRM); RAISE; END; 18 12.06.2012 Geschäftslogik in der Datenbank Fliegengewichtiger COMP CREATE OR REPLACE TYPE COMP_GATTUNG UNDER GLOBAL.TYPE_OBJECT ( -- Attributes GATTUNG_BASE_ID NUMBER , WKN CHAR(6) , ISIN CHAR(12) , SH NUMBER(2) , NAME VARCHAR2(80) , INSTRUMENT VARCHAR2(80) , GELOESCHT DATE -- Methods , CONSTRUCTOR FUNCTION , CONSTRUCTOR FUNCTION , CONSTRUCTOR FUNCTION , CONSTRUCTOR FUNCTION , CONSTRUCTOR FUNCTION , CONSTRUCTOR FUNCTION COMP_GATTUNG RETURN SELF AS RESULT COMP_GATTUNG(IN_GATTUNG_BASE_ID NUMBER) RETURN SELF AS RESULT COMP_GATTUNG(IN_ISIN VARCHAR2, IN_SH NUMBER DEFAULT 0) RETURN SELF AS RESULT COMP_GATTUNG(IN_WKN VARCHAR2, IN_SH NUMBER DEFAULT 0) RETURN SELF AS RESULT COMP_GATTUNG(IN_ISIN VARCHAR2, IN_WKN VARCHAR2, IN_SH NUMBER) RETURN SELF AS RESULT COMP_GATTUNG(IN_SECURITY_ID VARCHAR2, IN_ID_SOURCE VARCHAR2) RETURN SELF AS RESULT , MEMBER FUNCTION GET_ABRECHNUNGS_WAEHRUNG_ISO(IN_BOERSENPLATZ_ID VARCHAR2) RETURN VARCHAR2 , MEMBER FUNCTION GET_ABRECHNUNGS_WAEHRUNG_ID(IN_BOERSENPLATZ_ID VARCHAR2) RETURN NUMBER , MEMBER FUNCTION GET_ABRECHNUNGS_WAEHRUNG_ISO RETURN VARCHAR2 , MEMBER FUNCTION GET_HANDELS_WAEHRUNG_ISO(IN_BOERSENPLATZ_ID VARCHAR2) RETURN VARCHAR2 , MEMBER FUNCTION GET_HANDELS_WAEHRUNGEN_ISO(IN_BOERSENPLATZ_ID VARCHAR2) RETURN BBS.TAB_ROW_WAEHRUNG , MEMBER FUNCTION GET_RIC RETURN VARCHAR2 , MEMBER FUNCTION GET_RIC(IN_BOERSENPLATZ_ID VARCHAR2) RETURN VARCHAR2 , MEMBER FUNCTION GET_SEDOL RETURN VARCHAR2 , MEMBER FUNCTION GET_CUSIP RETURN VARCHAR2 , MEMBER FUNCTION GET_EMITTENT_NR RETURN VARCHAR2 , MEMBER FUNCTION GET_HEIMATLAND_ISO RETURN VARCHAR2 , MEMBER FUNCTION GET_HEIMATBOERSE_ID RETURN VARCHAR2 , MEMBER FUNCTION GET_LETZTER_HANDELSTAG(IN_BOERSENPLATZ_ID VARCHAR2) RETURN DATE , MEMBER FUNCTION IS_MIFID_RELEVANT RETURN BOOLEAN , MEMBER FUNCTION IS_DAX RETURN BOOLEAN , MEMBER FUNCTION IS_MDAX RETURN BOOLEAN , MEMBER FUNCTION IS_SDAX RETURN BOOLEAN , MEMBER FUNCTION IS_TECDAX RETURN BOOLEAN , MEMBER FUNCTION IS_STOXX RETURN BOOLEAN , MEMBER FUNCTION IS_HANDELBAR(IN_LAND_ISO VARCHAR2) RETURN BOOLEAN , MEMBER FUNCTION IS_HANDELBAR(IN_BOERSENPLATZ_ID VARCHAR2) RETURN BOOLEAN , MEMBER FUNCTION IS_HANDELBAR_FH(IN_BOERSENPLATZ_ID VARCHAR2) RETURN BOOLEAN , MEMBER FUNCTION IS_INVESTRO RETURN BOOLEAN , MEMBER FUNCTION IS_ZERTIFIKAT RETURN BOOLEAN , MEMBER FUNCTION IS_WARRANT RETURN BOOLEAN , MEMBER FUNCTION IS_FOND RETURN BOOLEAN , MEMBER FUNCTION IS_BEGO_FOND RETURN BOOLEAN , MEMBER FUNCTION IS_EHF RETURN BOOLEAN , MEMBER FUNCTION IS_DEF RETURN BOOLEAN ,… ) NOT FINAL 19 12.06.2012 Geschäftslogik in der Datenbank Schwergewichtiger TYPE CREATE OR REPLACE TYPE TYPE_KUNDE UNDER BBS.ROW_KUNDE ( -- attributes DEPOT BBS.TABLE_DEPOT, KONTEN BBS.TABLE_KONTO, KONTEN_BBS BBS.TABLE_KONTO_BBS, land nationalitaet rechtsform branche betreuer1 betreuer2 betreuer3 betreuer4 stammkreis VARCHAR2(35), VARCHAR2(35), VARCHAR2(105), VARCHAR2(105), VARCHAR2(35), VARCHAR2(35), VARCHAR2(35), VARCHAR2(35), VARCHAR2(35) -- define constructors , CONSTRUCTOR FUNCTION TYPE_KUNDE RETURN SELF AS RESULT , CONSTRUCTOR FUNCTION TYPE_KUNDE(IN_STAMMNR VARCHAR2) RETURN SELF AS RESULT , CONSTRUCTOR FUNCTION TYPE_KUNDE(IN_UNTERDEPOTID NUMBER) RETURN SELF AS RESULT , CONSTRUCTOR FUNCTION TYPE_KUNDE(IN_KUNDENID NUMBER, IN_KONTOID NUMBER) RETURN SELF AS RESULT , CONSTRUCTOR FUNCTION TYPE_KUNDE(IN_DEPOTNUMMER CHAR, IN_REFERENZNUMMER NUMBER) RETURN SELF AS RESULT ---------------------------------------------------------------- static functions instead of constructors -- duplicate signature problem in Java , STATIC FUNCTION CONSTRUCTOR_TYPE_KUNDE(IN_KUNDENID NUMBER) RETURN BBS.TYPE_KUNDE , STATIC FUNCTION CONSTRUCTOR_KONTONUMMER(IN_KONTONUMMER VARCHAR2) RETURN BBS.TYPE_KUNDE , STATIC FUNCTION CONSTRUCTOR_KONTO_BBS_ID(IN_KONTO_BBS_ID NUMBER) RETURN BBS.TYPE_KUNDE -- define member functions , MEMBER PROCEDURE ROW_SELECT(IN_STAMMNR VARCHAR2) , MEMBER PROCEDURE ROW_SELECT(IN_KUNDENID NUMBER, IN_VERSION NUMBER) , MEMBER PROCEDURE init_text_fields , MEMBER PROCEDURE INIT_DEPOT(SELF IN OUT TYPE_KUNDE, IN_REFERENZNUMMER NUMBER DEFAULT NULL) , MEMBER PROCEDURE INIT_DEPOT(SELF IN OUT TYPE_KUNDE, IN_DISPLAY_ID , MEMBER PROCEDURE INIT_KONTEN(SELF IN OUT TYPE_KUNDE) ,… ) NOT FINAL 20 12.06.2012 Geschäftslogik in der Datenbank NUMBER, IN_REFERENZNUMMER NUMBER DEFAULT NULL) Fliegengewichtiger COMP CREATE OR REPLACE TYPE COMP_KUNDE UNDER GLOBAL.TYPE_OBJECT ( -- Attributes KUNDE_ID NUMBER(12) , STAMMNUMMER CHAR(10) , INHABER VARCHAR2(35) , SPRACHE_ID NUMBER(12) -- Methods , CONSTRUCTOR FUNCTION COMP_KUNDE RETURN SELF AS RESULT , CONSTRUCTOR FUNCTION COMP_KUNDE(in_kunde_id NUMBER) RETURN SELF AS RESULT , CONSTRUCTOR FUNCTION COMP_KUNDE(in_stammnummer CHAR) RETURN SELF AS RESULT , CONSTRUCTOR FUNCTION COMP_KUNDE(in_unterdepot_id NUMBER) RETURN SELF AS RESULT ) NOT FINAL 21 12.06.2012 Geschäftslogik in der Datenbank Mischung OO-Type und relationale FK ID Analogon zur DEREF in ODBMS kann man mit dem FK eines Type den anderen Type instanzieren declare b wph.ROW_BOERSE; h wph.ROW_HANDELSSYSTEM; begin b := wph.ROW_BOERSE('194'); h := wph.ROW_HANDELSSYSTEM(b.HANDELSSYSTEM_ID); end; 22 12.06.2012 Geschäftslogik in der Datenbank Wie programmieren wir in PL/SQL -- Allocation ermitteln FOR rec_alloc IN (SELECT g.COLUMN_VALUE AS xml FROM TABLE (XMLSEQUENCE(rec_order.XML.EXTRACT('Order/Allocations/Allocation'))) g) LOOP -- Neue Allocation anlegen t_allocation := NEW WPH.TYPE_ALLOCATION ( IN_KUNDE_ID => t_ob.KUNDE_ID , IN_ANNAHMEART_ID => t_ob.ANNAHMEART_ID , IN_ALLOC_IDENT => t_ob.ORDER_NR_EXTERN , ...); -- Order hinzufuegen t_allocation.ADD_ORDER(IN_ORDER_BUCH_ID => t_ob.ORDER_BUCH_ID); -- Client Account ermitteln v_client_account := GLOBAL.PA_XML.EXTRACT_STRING(rec_alloc.XML, 'Allocation/ClientAccount'); -- Stueckzahl ermitteln v_stueckzahl := GLOBAL.PA_XML.EXTRACT_NUMBER(rec_alloc.XML, 'Allocation/Stueckzahl'); IF v_stueckzahl IS NOT NULL THEN -- Allocation hinzufuegen t_allocation.ADD_ALLOCATION(IN_CLIENT_ACCOUNT => v_client_account , IN_STUECKZAHL => GLOBAL.PA_XML.EXTRACT_NUMBER(rec_alloc.XML, 'Allocation/Stueckzahl') , IN_KURS => NULL); ELSE -- Prozentsatz ermitteln v_prozentsatz := GLOBAL.PA_XML.EXTRACT_NUMBER(rec_alloc.XML, 'Allocation/Prozentsatz'); -- Allocation hinzufuegen t_allocation.ADD_ALLOCATION(IN_CLIENT_ACCOUNT => v_client_account , IN_PROZENTSATZ => v_prozentsatz); END IF; END LOOP; -- Allocation einstellen t_allocation.EINSTELLEN(); 23 12.06.2012 Geschäftslogik in der Datenbank Wie programmieren wir in PL/SQL -- zentrale Schnittstelle zum Steuermodul PROCEDURE STEUER_BERECHNUNG(...) IS t_steuer BBS.TYPE_STEUER; BEGIN -- geschäftsbedingt Instanzierung IF ....GESCHAEFTSART = 'KKZ' THEN t_steuer := NEW BBS.TYPE_STEUER_KKZ(...) ELSIF ....GESCHAEFTSART = 'WPK' THEN t_steuer := NEW BBS.TYPE_STEUER_WPK(...) ELSIF <weitere Geschäftsbereiche> THEN t_steuer := NEW BBS.TYPE_STEUER_XXX(...) END IF; -- Aufruf der Steuerverrechnung t_steuer.BERECHNE_STEUERN; END; 24 12.06.2012 Geschäftslogik in der Datenbank Unsere Leitsätze Man sollte alles so einfach wie möglich machen, aber nicht einfacher Albert Einstein "Der einfache und klare Zweck sowie Prinzipien bedingen komplexes und intelligentes Verhalten". "Komplexe Regeln und Vorschriften bedingen einfaches und dummes Verhalten". Dee Hock, Designer VISA-Systems 1960-1970 Ein System erreicht Perfektion nicht, wenn nichts weiter hinzugefügt werden kann, sondern wenn nichts weiter entfernt werden kann. ??? 25 12.06.2012 Geschäftslogik in der Datenbank Q&A Vielen Dank ! 26 12.06.2012 Geschäftslogik in der Datenbank