Table of Contents
Ziel
Heute möchte ich euch zeigen, wie man mithilfe des Oracle Editions Feature ein Update einer auf PL/SQL basierenden Datenbankanwenduing mit minimaler Ausfallzeit für die Benutzer durchführen kann.
Oracle Editions
Mit der Version 11g Release 2 führte Oracle das Edition-based Redefintion Feature ein. Ziel dieses Features war eine Möglichkeit zu schaffen, eine Anwendung im laufenen Betrieb zu aktualisieren, ohne das der Benutzer in seiner Arbeit gestört wird. Mit Oracle 11g Release 2 ist es nun möglich, mehrere Versionen (Editions) eines Objektes vorzuhalten. Ein Objekt wird nun anhand seines Namens, Schemas und der Edition identifiziert. Oracle unterscheidet bei den Objekten zwischen “edtionable” und “non-editionable” Objekten. Bei den “non-editionable” Objekten kann nur eine Version in der Datenbank existieren, dies ist zum Beispiel bei Tabellen der Fall.
Editionable Objekte
- Function
- Trigger
- Synonym
- Procedure
- Package + Package Body
- Type + Type Body
- View
- Library
Non-Editionable Objekte
- Tables
- Puiblic Synonym
Jedes Schema kann nur eine Edition zur gleichen Zeit aktiv haben. Welche Version zurzeit in Benutzung ist, kann man wie folgt heraus finden.
SELECT SYS_CONTEXT('USERENV', 'CURRENT_EDITION_NAME') EDITION FROM DUAL;
Die Edition ora$base ist die Standardedition der Datenbank.
Testanwendung
Um dieses Tutorial verwenden zu können, muss eine Oracle 11g Release 2 Datenbank vorhanden sein.
BANNER -------------------------------------------------------------------------------- Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production PL/SQL Release 11.2.0.1.0 - Production CORE 11.2.0.1.0 Production TNS for Linux: Version 11.2.0.1.0 - Production NLSRTL Version 11.2.0.1.0 - Production
Zur Veranschaulichung habe ich eine einfache Buchanwendung erstellt. Für die Buchanwendung verwende ich einen eigenständigen Benutzer.
CREATE USER buchliste_adm IDENTIFIED BY password; GRANT connect, resource TO buchliste_adm; GRANT create view TO buchliste_adm;
Der folgende Code soll nur das Edition-based Redefinition Feature demonstrieren und wurde deshalb einfach gehalten.
-- Sequenz fuer Primaerschluessel CREATE SEQUENCE BUCHLISTE_PK_SEQ START WITH 1; -- Tabelle mit den Buchinformationen CREATE TABLE BUCHLISTE ( id NUMBER, name VARCHAR2(30) NOT NULL, autor VARCHAR2(30) NOT NULL, CONSTRAINT BUCHLISTE_PK PRIMARY KEY (id), CONSTRAINT BUCHLUSTE_BUCH_UQ UNIQUE (name, autor) ); -- Anwendungslogik CREATE OR REPLACE PACKAGE BUCHLISTE_VERWALTUNG IS PROCEDURE HINZUFUEGEN (p_name IN VARCHAR2, p_autor IN VARCHAR2); PROCEDURE ALLE_ANZEIGEN; END; / CREATE OR REPLACE PACKAGE BODY BUCHLISTE_VERWALTUNG IS PROCEDURE HINZUFUEGEN (p_name IN VARCHAR2, p_autor IN VARCHAR2) IS v_id NUMBER; BEGIN -- Naechste ID abrufen SELECT buchliste_pk_seq.NEXTVAL INTO v_id FROM dual; -- Buch hinzufuegen INSERT INTO buchliste VALUES (v_id, p_name, p_autor); -- Festschreiben COMMIT; END HINZUFUEGEN; PROCEDURE ALLE_ANZEIGEN IS BEGIN -- Ueberschrift DBMS_OUTPUT.PUT_LINE(RPAD('ID', 8, ' ') || ' ' || RPAD('NAME', 30, ' ') || ' ' || RPAD('AUTOR', 30, ' ')); DBMS_OUTPUT.PUT_LINE(RPAD('-', 8, '-') || ' ' || RPAD('-', 30, '-') || ' ' || RPAD('-', 30, '-')); -- Komplette Buchluste durchlaufen FOR v_buch IN (SELECT * FROM buchliste) LOOP -- Ausgabe der Buchinformationen DBMS_OUTPUT.PUT_LINE(RPAD(v_buch.ID, 8, ' ') || ' ' || RPAD(v_buch.NAME, 30, ' ') || ' ' || RPAD(v_buch.AUTOR, 30, ' ')); END LOOP; END ALLE_ANZEIGEN; END; /
Die Anwendung wird wie folgt verwendet. Mithilfe der Prozedur HINZUFUEGEN kann ein neues Buch zur Verwaltung hinzugefügt werden. Die Prozedur ALLE_ANZEIGEN zeigt die komplette Buchliste an.
SET serveroutput ON SET linesize 120 BEGIN BUCHLISTE_VERWALTUNG.HINZUFUEGEN(p_name => 'Tore der Welt', p_autor => 'Ken Follett'); BUCHLISTE_VERWALTUNG.ALLE_ANZEIGEN; END; /
Das Ergebnis sollte wie folgt aussehen.
ID NAME AUTOR -------- ------------------------------ ------------------------------ 1 Tore der Welt Ken Follett
Vorbereitung des Benutzers
Damit ein Benutzer neue Editions anlegen kann, müssen die entsprechenden Rechte vergeben werden und das Editons Feature muss aktiviert werden.
GRANT create any edition, drop any edition TO buchliste_adm; ALTER USER buchliste_adm ENABLE EDITIONS;
Die Spalte EDITIONS_ENABLED in der Tabelle DBA_USERS gibt Aufschluss, ob ein Benutzer das Editions Feature verwenden kann.
Vorbereitung der Testanwendung
Wie ich oben aufgeführt habe, ist eine Tabelle ein non-editionable Objekt. Aus diesem Grund sollte die Anwendung nicht direkt auf die Tabellen zugreifen, sondern über eine EDITIONING VIEW, da es sonst zum Beispiel zu einem Problem mit der folgenden Zeile kommen würde.
INSERT INTO buchliste VALUES (v_id, p_name, p_autor);
Würde nun die Tabelle BUCHLISTE um eine weitere Spalte, die keinen Standardwert besitzt, erweitert werden, würde beim nächsten INSERT eine Ora-Fehlermeldung auftreten.
Damit dies nicht passiert, sollte der Zugriff in jeder Edition nur über Views stattfinden. Es gibt nun zwei Möglichkeiten die Anwendung entsprechen anzupassen.
- Umbenennen der Tabelle und Erstellung einer EDITIONING VIEW mit dem Namen BUCHLISTE
- Erstellung einer EDITIONING VIEW mit neuem Namen und Anpassung des Package BUCHLISTE_VERWALTUNG
Ich werde in diesem Fall Möglichkeit 1 verwenden. Dafür werde ich die Tabelle BUCHLISTE in BUCHLISTE_BASIS umbenenen und eine neue View anlegen.
ALTER TABLE buchliste RENAME TO buchliste_basis; CREATE OR REPLACE EDITIONING VIEW buchliste AS SELECT id, name, autor FROM buchliste_basis;
Hinweis: Für das Anlegen einer EDITIONING VIEW wird das CREATE VIEW Recht benötigt.
Zum Abschluss ein Test, ob die Anwendung wieder korrekt funktioniert.
SET serveroutput ON SET linesize 120 BEGIN BUCHLISTE_VERWALTUNG.HINZUFUEGEN(p_name => 'Der Hobbit', p_autor => 'John R Tolkien'); BUCHLISTE_VERWALTUNG.ALLE_ANZEIGEN; END; / ID NAME AUTOR -------- ------------------------------ ------------------------------ 1 Tore der Welt Ken Follett 2 Der Hobbit John R Tolkien
Die Ausführung war erfolgreich. Unsere Anwendung arbeitet nun nicht mehr direkt mit der Tabelle, sondern stattdessen über die EDITIONING VIEW.
Neue Edition erstellen
Damit das Update der Anwendung nicht die aktuelle Programmversion beeinträchtigt, wird als nächstes eine neue Edition erstellt. Erst nachdem das Programmupdate erfolgreich war, wird diese neue Edition aktiviert. Dies hat zudem den Vorteil, dass bei einem Fehler die Verfügbarkeit der Anwendung nicht gefährdet ist.
CREATE edition buchliste_v2 AS CHILD OF ora$base;
Anschließend muss diese Edition auf Session Ebene aktiviert werden.
ALTER SESSION SET EDITION = buchliste_v2;
Hinweis: Alle bestehenden und neuen Sessions arbeiten weiterhin mit der alten Version.
Programmupdate einspielen
Nun werden die Änderungen an der Anwendung eingespielt. Die Anwendung wird um eine Verleihfunktion erweitert. Dafür wird zunächst die Basis Tabelle um zwei Spalten erweitert.
ALTER TABLE buchliste_basis ADD (verliehen_an VARCHAR2(30), verliehen_am DATE);
Hinweis: Die neuen Spalten dürfen keine NOT NULL Spalten sein oder müssen zumindest über einen Standardwert verfügen. Ansonsten gibt es Probleme mit der bestehenden Programmversion.
Anschließend wird die EDITIONING VIEW ersetzt.
CREATE OR REPLACE EDITIONING VIEW buchliste AS SELECT id, name, autor, verliehen_an, verliehen_am FROM buchliste_basis;
Zum Abschluss wird das Package aktualisiert und erweitert.
CREATE OR REPLACE PACKAGE BUCHLISTE_VERWALTUNG IS PROCEDURE HINZUFUEGEN (p_name IN VARCHAR2, p_autor IN VARCHAR2); PROCEDURE VERLEIHEN (p_id IN NUMBER, p_verliehen_an IN VARCHAR2, p_verliehen_am IN DATE); PROCEDURE ALLE_ANZEIGEN; END; / CREATE OR REPLACE PACKAGE BODY BUCHLISTE_VERWALTUNG IS PROCEDURE HINZUFUEGEN (p_name IN VARCHAR2, p_autor IN VARCHAR2) IS v_id NUMBER; BEGIN -- Naechste ID abrufen SELECT buchliste_pk_seq.NEXTVAL INTO v_id FROM dual; -- Buch hinzufuegen INSERT INTO buchliste VALUES (v_id, p_name, p_autor, NULL, NULL); -- Festschreiben COMMIT; END HINZUFUEGEN; PROCEDURE VERLEIHEN (p_id IN NUMBER,p_verliehen_an IN VARCHAR2, p_verliehen_am IN DATE) IS BEGIN UPDATE buchliste SET verliehen_an = p_verliehen_an, verliehen_am = p_verliehen_am WHERE id = p_id; COMMIT; END VERLEIHEN; PROCEDURE ALLE_ANZEIGEN IS BEGIN -- Ueberschrift DBMS_OUTPUT.PUT_LINE(RPAD('ID', 8, ' ') || ' ' || RPAD('NAME', 30, ' ') || ' ' || RPAD('AUTOR', 30, ' ') || ' ' || RPAD('VERLIEHEN_AN', 30, ' ') || ' ' || RPAD('VERLIEHEN_AM', 30, ' ')); DBMS_OUTPUT.PUT_LINE(RPAD('-', 8, '-') || ' ' || RPAD('-', 30, '-') || ' ' || RPAD('-', 30, '-') || ' ' || RPAD('-', 30, '-') || ' ' || RPAD('-', 30, '-')); -- Komplette Buchluste durchlaufen FOR v_buch IN (SELECT * FROM buchliste) LOOP -- Ausgabe der Buchinformationen DBMS_OUTPUT.PUT_LINE(RPAD(v_buch.ID, 8, ' ') || ' ' || RPAD(v_buch.NAME, 30, ' ') || ' ' || RPAD(v_buch.AUTOR, 30, ' ') || ' ' || RPAD(v_buch.VERLIEHEN_AN, 30, ' ') || ' ' || RPAD(v_buch.VERLIEHEN_AM, 30, ' ')); END LOOP; END ALLE_ANZEIGEN; END; /
Bevor die neue Programmversion aktiviert wird, wird zunächst ein Test der neuen VERLEIHEN Prozedur durchgeführt.
SET serveroutput ON SET linesize 160 BEGIN BUCHLISTE_VERWALTUNG.VERLEIHEN(p_id => 1, p_verliehen_an => 'Frodo', p_verliehen_am => SYSDATE); BUCHLISTE_VERWALTUNG.ALLE_ANZEIGEN; END; /
Das Ergebnis sieht wie folgt aus.
ID NAME AUTOR VERLIEHEN_AN VERLIEHEN_AM -------- ------------------------------ ------------------------------ ------------------------------ ------------------------------ 1 Tore der Welt Ken Follett Frodo 15-APR-13 2 Der Hobbit John R Tolkien
Wie man sieht waren die Änderungen erfolgreich.
Aktivieren der neuen Edition
Damit nun alle Benutzer in den Genuss der neuen Programmversion kommen, muss diese Edition als Standardedition aktiviert werden.
CONNECT / as sysdba ALTER DATABASE DEFAULT EDITION = buchliste_v2;
Jeder Benutzer der sich neu anmeldet arbeitet von dort an mit der neuen Programmversion.
CONNECT buchliste_adm/password SELECT SYS_CONTEXT('USERENV', 'CURRENT_EDITION_NAME') EDITION FROM DUAL; EDITION ------------------------ BUCHLISTE_V2
Fazit
Mit dem Editions Feature bietet Oracle eine einfache und sichere Methode um Anwendungen im laufenden Betrieb zu aktualisieren.