Objektorientierte Datenintegration - ein Migrationsweg zur Objekttechnologie

OBJEKTspektrum Juli/August 1996, Nr. 4, Seite 20-28.

Jens Coldewey, Wolfgang Keller

Viele Unternehmen versprechen sich von einem Einstieg in objektorientierte Technologie verbesserte Möglichkeiten zur Wiederverwendung und die einfachere Entwicklung von verteilten Systemen. Gerade bei Großanwendern, wie Banken und Versicherungen, werden dabei häufig bestehende Systeme abgelöst, die eine monolithische, transaktionsorientierte Architektur aufweisen. Eine "Stichtagsablösung" ist riskant und meist auch nicht durchführbar. Der Artikel diskutiert die objektorientierte Datenintegration als Migrationsstrategie, bei der Altsysteme und neue Applikationen auf den gleichen Datenbeständen arbeiten. Die typischen Probleme werden erläutert und Lösungswege skizziert.


Problemstellung

Viele EDV-Großanwender, wie Banken und Versicherungen, stehen heute vor der Entscheidung, objektorientierte Software-Entwicklungsmethoden einzuführen, oder haben bereits erste Pilotprojekte gestartet. Am Anfang des Migrationsprozesses stehen meist monolithische, großrechnerbasierte Transaktionssysteme unterschiedlicher Qualität. Am Ende des Prozesses sollen eine objektorientierte Systemarchitektur sowie eine Entwicklungsmannschaft stehen, die auf das neue Denken gründlich eingeschworen ist. Dabei müssen die immensen Investitionen geschützt werden, die gerade große Anwender in den letzten Jahrzehnten in ihre Softwaresysteme getätigt haben. Bei diesen Kunden treten technische Probleme der Objektorientierung meist zugunsten wirtschaftlicher Abwägungen in den Hintergrund. Voraussetzung einer erfolgreichen Migration ist eine genau auf die Bedürfnisse des einzelnen Kunden abgestimmte Strategie. Diese darf nicht nur einzelne Applikationen betrachten, sondern muß die gesamte EDV-Landschaft des Unternehmens in die Planung der Migrationsstrategie einbeziehen.

Stichtagsablösungen bergen gerade bei Großanwendern nicht kalkulierbare Risiken - vielleicht ein Grund dafür, daß sie im Englischen doppeldeutig als Big Bang Strategies bezeichnet werden. Zweihundert und mehr Entwickler sowie millionenschwere Investitionen in die EDV-Landschaft können nicht mit einem großen Knall auf ein neues Paradigma umgestellt werden (vgl. [Ebe94]). Bei solchen Unternehmen ist eher damit zu rechnen, daß der Übergang zur Objektorientierung zehn und mehr Jahre dauert. Während dieser Zeit müssen alte und neue Systeme parallel betrieben und auch weiterentwickelt werden. Diese Anforderung ist bei vielen EDV-Anwendern ein zentrales Kriterium, an dem sich jede Migrationsstrategie messen lassen muß.

In der Praxis bieten sich verschiedene Ansätze für eine objektorientierte Teilrenovierung von Systemen an:

Eine Migrationsstrategie zur Objektorientierung wird sich bei Großkunden niemals auf einen einzelnen dieser Wege konzentrieren.

Üblicherweise wird der Übergang auch noch in mindestens drei Phasen eingeteilt:

Welche der beschriebenen Strategien eingesetzt wird, hängt vor allem von der individuellen Kosten/Nutzen- und Risikobewertung des Unternehmens ab. Dabei sollten das bestehende Altsystem und das projektierte Neusystem unter verschiedensten Gesichtspunkten betrachtet werden. In unserer Praxis hat sich der Einsatz der folgenden, groben Checkliste bewährt:

Im Lichte dieser Kriterien werden in einer Pilotphase vor allem GUI-Migrationen und Offline-Systeme in Frage kommen. In späteren Phasen werden die Zwischenlösungen eher mit Proxy-Objekten oder mit Hilfe von Datenintegration realisiert. Während Proxy-Objekte stark anwendungsspezifisch sind, hat sich in den letzten Jahren eine Standardlösung zur Datenintegration entwickelt, die wir im folgenden näher betrachten wollen.

Datenbank-Generationen und Tupelschnittstelle

Gerade Großanwender, wie Banken und Versicherungen, haben bereits in den sechziger Jahren mit der Automatisierung ihrer Geschäftsprozesse begonnen. In der Regel läuft in diesen Unternehmen daher heute Software aus drei Generationen:

Objektdatenbanken sind erst seit wenigen Jahren auf dem Markt und spielen derzeit außerhalb von Pilotprojekten kaum eine Rolle im kommerziellen Umfeld von Großanwendern. Es ist jedoch damit zu rechnen, daß sich dies im Laufe der nächsten Jahre deutlich ändern wird. Insbesondere Systeme zur Unterstützung strategischer Entscheidungen werden mehr und mehr Nutzen aus Objektdatenbanken ziehen können. Eine vollständige Verdrängung von relationalen Systemen ist derzeit aber nicht wahrscheinlich, da die beiden Paradigmen sehr unterschiedliche Stärken und Schwächen haben (vgl. [Cha96]).

Um in diesem "Zoo von Technologien" effektiv Software entwickeln zu können, haben viele Unternehmen sogenannte Tupelschnittstellen entwickelt. Diese Komponenten kapseln den Zugriff auf die verschiedenen Datenbanktechnologien und bieten der Applikation eine einheitliche Schnittstelle für alle Daten. Die angebotene Sicht ist in der Regel vergleichbar mit einer relationalen Datensicht, wobei aber nur statische Abfragen zulässig sind. Solche Tupelschnittstellen unterstützen allerdings objektorientierte Anwendungen noch nicht optimal (siehe [Cat94]). Will man eine objektorientierte Sicht auf die Unternehmensdaten zur Verfügung stellen, stößt man auf eine Reihe schwieriger Probleme, die sich erst bei näherem Studium der Materie offenbaren - oder beim Systemtest.

Objektorientierte Anwendungen und relationale Datenbanken

Es gibt zwei grundsätzlich verschiedene Ansätze, aus einer objektorientierten Anwendung auf relationale Datenbanken zuzugreifen:

Anforderungen an eine Integrationslösung

Bei der Datenintegration soll der Anwendungskern objektorientiert gebaut werden, weshalb wir uns auf die Persistenz von Objekten konzentrieren. Wir wollen zunächst die Anforderungen an eine Zugriffsschicht erläutern, um dann die Komponenten üblicher Lösungen vorzustellen.

Die Abbildung von der objektorientierten Sicht des Anwendungskerns auf die Tupelschnittstelle erfolgt grundsätzlich nach dem in Abbildung 1 dargestellten Schema. Leider birgt diese einfache Abbildung einige Fallen und Probleme, auf die wir im folgenden genauer eingehen möchten.

Abb. 1: Grundschema zur Abbildung der objektorientierten Sicht des Anwendungskers auf die Tupelschnittstelle
1 Klasse
<->
1 Tabelle
1 Instanz
<->
1 Zeile
1 Attribut
<->
1 Feld

Behandlung komplexer Datentypen

Viele objektorientierte Programmiersprachen (zum Beispiel C++, Eiffel und Java) haben ein komplexes Typkonzept mit Ausschnitts- und Verbundtypen. Zusätzlich stehen Listen, Mengen und ähnliche Konstrukte zur Verfügung, die in Form von Klassenbibliotheken in das System integriert werden. Die üblichen relationalen Datenbanken kennen hingegen nur Zahlen und Zeichenketten in einigen festen Variationen. Die einzige Möglichkeit zur Strukturierung besteht in der Zusammenstellung von Tabellen.

Zur Überwindung dieser Lücke müssen hierarchische Typen "flachgeklopft" bzw. wieder zusammengefügt werden. Auch Listen, Mengen und ähnliches, die in objekt- orientierten Sprachen selbst wieder durch Objekte realisiert werden, müssen einer besonderen Behandlung unterworfen werden. Datenbanken unterstützen gerade die Arbeit mit Massendaten in besonders performanter Weise, so daß ein zu naiver Ansatz hier zu einem katastrophalen Leistungsverhalten führt.

Garantie der Objektidentität

In relationalen Systemen werden Sätze innerhalb einer Tabelle einzig durch ihren Inhalt identifiziert: Über einen Schlüssel kann ein Satz im allgemeinen eindeutig gefunden werden.

In der objektorientierten Welt besitzen Objekte dagegen eine eigene Identität, unabhängig von ihrem Inhalt. Dadurch entstehen drei Probleme:

Abb. 2: Objektidentität und relationale Datenbanken
Ein Versicherungssystem soll die Möglichkeit bieten, mit einem Kunden mehrere Vertragsabschlüsse gleichzeitig nach dem "Alles-oder-Nichts-Prinzip" zu tätigen. Um eine finanzielle Überlastung des Kunden durch das Vertragspaket zu vermeiden, ist vor dem Abschluß jedes einzelnen Vertrages die Bonität des Kunden zu überprüfen, wobei die bereits abgeschlossenen Verträge berücksichtigt werden sollen. Zur Prüfung der Bonität wendet sich der Anwendungskern jedes Mal an die Datenbank, um das Objekt Kunde XY zu holen.

Wenn die Zugriffsschicht in diesem Szenario nicht beachtet, daß bei jedem Zugriff das bereits anfangs geholte Objekt Kunde XY gefordert ist, und statt dessen jedesmal ein neues Objekt aus der Datenbank holt und im Speicher neu erzeugt, so spiegelt dieses neue Objekt den Stand in der Datenbank wider und berücksichtigt die bisher abgeschlossenen Verträge nicht: Der Geschäftsvorfall wird fehlerhaft bearbeitet, da die Vertragsabschlüsse mit einem anderen Objekt durchgeführt werden als die Bonitätsprüfungen.

Beziehungen zwischen Objekten

Objektorientierte Systeme weisen in der Regel eine große Anzahl relativ einfacher Klassen auf, die über sehr komplexe Beziehungen miteinander interagieren. Die fachliche Komplexität eines Problems wird beim objektorientierten Design nicht so sehr durch komplexe Algorithmen repräsentiert, sondern eher durch die Komplexität der Beziehungen zwischen Objekten.

Relationale Systeme unterstützen Beziehungen nur in Form der Fremdschlüssel-Beziehung. Diese werden mit Hilfe von Select- oder Join-Operationen der Datenbank verfolgt, die aufwendig sind. Hier muß man beim Paradigmenwechsel besondere Vorsicht walten lassen.

Wichtig ist es hier, von der einfachen Formel 1 Klasse <-> 1 Tabelle abzuweichen. Dies ist insbesondere bei Aggregationen sinnvoll: Man stelle sich vor, für die Klasse Datum, die in den meisten Systemen vorhanden ist, würde eine eigene Tabelle erzeugt, die nur alle Datumsangaben in der Datenbank enthält! Daß ein solcher Entwurf nicht sinnvoll sein kann, ist intuitiv klar. Niemand käme auf die Idee, ein relationales System so zu bauen.

Zu beachten ist auch, daß Beziehungen in objektorientierten Sprachen immer unidirektional, also gerichtet, sind, während Fremdschlüssel-Beziehungen in relationalen Systemen immer in beiden Richtungen verfolgt werden können. Insbesondere zweiseitig verfolgbare 1:n -Beziehungen lassen sich in objektorientierten Systemen nur mit zusätzlichem Aufwand implementieren, indem z. B. Listen von Referenzen statt eines Fremdschlüssels eingesetzt werden.

Vererbung und Polymorphismus

Vererbung und Polymorphismus sind zwei zentrale Elemente des objektorientierten Paradigmas. Die Wartbarkeit eines gut entworfenen OO-Systems ist vor allem der Möglichkeit zu verdanken, ähnliche Klassen durch Vererbung und Polymorphismus wie eine Klasse behandeln zu können.

Relationale Systeme sehen diese Möglichkeit nicht vor. Zwar bietet die Entity/Relationship-Modellierung (Abk. E/R-Modellierung) die Möglichkeit, sogenannte "Ist-ein-Beziehungen" zu formulieren, doch die meisten Datenbanksysteme bieten dafür keine direkte Implementierung an. Das Problem der Umsetzung zerfällt bei genauerer Betrachtung in zwei Teilprobleme:

Die Zugriffsschicht muß also sowohl die Vererbungshierarchie kennen, als auch den exakten Typ eines Objekts, das sich in der Datenbank befindet. Sie muß in der Lage sein, Objekte typrichtig zu erzeugen und polymorph an den Anwendungskern zurückzugeben.

Verschiedene Transaktionskonzepte

Bis jetzt haben wir uns nur mit Problemen beschäftigt, die direkt mit dem Paradigmenbruch zusammenhängen. Viele moderne Anwendungen benötigen zusätzlich eine Flexibilisierung des Transaktionskonzeptes. Klassische Transaktionssysteme sind darauf optimiert, genau definierte, kurze Geschäftsvorfälle stark parallelisiert abzuarbeiten. Ein Kontoführungssystem einer Bank muß Millionen von kurzen Einzelbuchungen pro Tag abarbeiten. In den letzten Jahren wurden jedoch verstärkt Systeme entwickelt, die Beratern oder Managern bei ihren Entscheidungen helfen. Solche Systeme unterstützen Geschäftsprozesse, die mehrere Stunden oder gar Tage dauern und dem Anwender ein hohes Maß an Flexibilität bieten. Für diese unterschiedlichen Arten von Geschäftsprozessen muß die Datenbank verschiedene Transaktionen anbieten:

Da die Wahl der Transaktionslogik von der jeweiligen Anwendung abhängt, sollte die Zugriffsschicht beide Möglichkeiten bieten.

Transparenz bei der Programmierung

Relationale Datenbanken stellen ein eigenes Konzept dar, das unabhängig von der Programmiersprache ist. Persistente Objekte fügen sich dagegen nahtlos in die Programmiersprache ein. Daher ist es sinnvoll zu fordern, daß sich der Anwendungscode von Persistente Objekten nicht von dem transienter Objekte unterscheiden soll. Die Funktionen der Datenhaltung sollten mit den üblichen Mitteln der Programmiersprache formuliert und Spracherweiterungen vermieden werden.

Listing 1: Beispiel einer C++ Einbindung
void beispiel ( d_String kundenName ) {

// Datenbankfehler werden über den C++-Exception-Mechanismus bearbeitet

try {

// Anlegen und starten einer Transaktion

Transaction trans;

trans.begin()

// Deklaration der Variablen - Ref ist ein persistenter Pointer

d_Ref<Kunde> alterKunde;

// d_Set<d_Ref<Vertrag>> repräsentiert eine Menge persist. Objekte

d_Set<d_Ref<Vertrag>> vertraege;

// Holen des Kunden

// Hier wird nur eine Objekt-Id geladen;

// (Die Methode ist nicht ODMG konform,

// kann aber auf ODMG-Basis implementiert werden)

alterKunde = Kunde::d_getByName(kundenName);

// Dereferenzieren der Verträge

// Durch das erste Verfolgen des Pointers wird das

// eigentliche Objekt geladen

vertraege = alterKunde->abgeschlVertraege;

// Anlegen eines neuen Kunden - fast wie bei "normalen" Objekten

d_Ref<Kunde> neuerKunde = new(Kunde);

// Objekt kennzeichnen, damit die folgenden Änderungen

// beim commit zurueckgeschrieben werden.

// (Bei neuen Objekten eigentlich nicht nötig.

// Hier nur zur Verdeutlichung)

neuerKunde->markModified();

// das Objekt wird durch belegen der Attribute verändert;

neuerKunde->Name = ...

// Übernahme der Verträge

neuerKunde->abgeschlVertraege = vertraege;

// Löschen des alten Kunden

alterKunde.delete_object();

// Beenden der Transaktion

trans.commit()
}

catch ... // Fehlerbehandlung wird hier nicht dargestellt

};

ODMG-Verträglichkeit

Die "Object Database Management Group" (Abk. ODMG) standardisiert die Schnittstelle von Objektdatenbanken (vgl. [ODMG96]). Nicht alle Aspekte dieses Standards sind im Rahmen der Zugriffsschicht interessant. Die Programmierschnittstelle der Zugriffsschicht sollte sich jedoch möglichst an die ODMG-Sprachanbindung der gewählten Sprache halten. Dies erspart die ansonsten notwendigen Konzeptarbeiten und erleichtert den Programmierern die Einarbeitung in die Zugriffsschicht, da sie auf Standardliteratur zurückgreifen können. Ferner kann die Zugriffsschicht später leichter durch eine normkonforme Objektdatenbank ausgewechselt werden.

Automatisches Erzeugen der Zugriffsschicht

Die für die Zugriffsschicht benötigten Methoden können zu großen Teilen automatisch erzeugt werden. Existiert kein Altsystem, kann die Zugriffsschicht sogar vollständig generiert werden (vgl. [Hah95]), da keine Rücksicht auf bestehende Datenbankschemata genommen werden muß. Implementierungen für C++ können durch den Einsatz von Templates auf Generierung verzichten. Die Generierung wird dann vom Compiler erledigt. Sollen bestehende Datenbestände genutzt werden, so muß allerdings das "Mapping", also die Umsetzung zwischen dem Tabellenschema und den Objekten, manuell erfolgen.

Isolierte Ansatzpunkte für Optimierungen

Um eine verwendbare Anwendung zu erhalten, müssen normalerweise innerhalb der Zugriffsschicht noch gezielt Optimierungen vorgenommen werden. Das können z. B. Denormalisierungen des Datenbankschemas sein oder Cache-Techniken mit fachlichem Know-how (sogenannte Look-Ahead-Caches). Da solche Optimierungen in der Regel erst nach ausführlichen Leistungsmessungen am laufenden System vorgenommen werden, muß man zentrale "Schrauben" vorsehen, an denen diese Optimierungen mit möglichst geringem Aufwand durchgeführt werden können.


Komponenten einer Lösung

Um nicht nur über Probleme und Anforderungen zu schreiben, wird es Zeit, eine Lösung vorzustellen, die über fünf Jahre aus dem in [Hah95] beschriebenen System weiterentwickelt wurde. Wir werden die wesentlichen Elemente im folgenden kurz diskutieren. Eine technisch erschöpfende Behandlung des Themas ist im Rahmen eines kurzen Artikels allerdings nicht möglich.

Zunächst soll am Beispiel von C++ ein Gefühl für die mögliche Spracheinbettung vermittelt werden. Listing 1 zeigt einige Operationen auf einem persistenten Objekttyp Kunde; hierzu gehören:

Der Objekttyp Kunde besitzt neben fachlichen Methoden auch Methoden der Zugriffsschicht.

Die Schnittstelle lehnt sich an die C++-Sprachanbindung der ODMG an. Sie setzt sich zusammen aus ererbten Methoden (wie markModified), generierten Methoden (wie d_getByName) und eigenen Klassen (wie d_Transaktion).

Die Architektur, die die Datenbank hinter dieser Schnittstelle versteckt, ist in Abbildung 3 dargestellt. Die Komponenten werden im folgenden einzeln erläutert.

Abb3: Die Komponenten der objektorientierten Zugriffsschicht. Diese verdeckt die Datenbank vollständig

Objektabbildung

Der komplexeste Teil einer Zugriffsschicht ist die Abbildung der Objekte auf die relationale Datenbank. Sie sorgt dafür, daß Objekte sich aus der Datenbank laden und in eine Datenbank zurückschreiben können. Die Umwandlung kann für Attribute und Beziehungen getrennt betrachtet werden.

Abbildung der Attribute

Um komplexe Typen einer objektorientierten Sprache auf das einfach Typsystem der Datenbank umzuwandeln, wird im wesentlichen Attribut für Attribut konvertiert. Diese Konvertierung läßt sich in zwei Teilaufgaben zerlegen: "Mapping" und "Casting" (siehe Abbildung 4):

Beim Mapping wird für jedes Attribut einer Klasse angegeben, aus welchen Feldern der Datenbank der Wert beschafft werden kann. Das Casting setzt dann den Typ der Programmiersprache in den Typ der Datenbank um. Handelt es sich bei dem Attribut um einen Verbundtyp, so wird er beim Mapping auf ein komplexes Pseudofeld der Tabelle abgebildet und beim Casting aufgebrochen.

Auch die Vererbung von Attributen muß von der Objektabbildung gelöst werden. Hier sind zwei Alternativen denkbar:


Abb. 4: Das Casting verwandelt Datentypen der Datenbank in Datentypen der Anwendung. Dies können auch schon Records sein (Adresse). Das Mapping baut Objekte aus typisierten Pseudotabellen (EntityObjects) zusammen. EntityObjects sind ein logisches Konstrukt, das nicht physisch implementiert werden muß. In der Abbildung wird die Behandlung der Objekt-Identifikatoren nicht gezeigt.

Objektabbildung und Beziehungen

Bei Beziehungen muß man zwischen einfachen Beziehungen (1:1 ) und mehrfachen Beziehungen (1:n und n:m ) unterscheiden. Einfache Beziehungen lassen sich problemlos durch Fremdschlüssel-Beziehungen realisieren. Soll die Beziehung auch im Objektmodell bidirektional sein, so werden auf beiden Seiten der Beziehung Referenzen gehalten, deren Konsistenz von den Methoden der Zugriffsschicht sichergestellt wird. Man sollte allerdings in Objektmodellen im Gegensatz zu E/R-Modellen sehr sparsam mit wechselseitigen Beziehungen sein, da sie eine enge gegenseitige Abhängigkeit zwischen den Klassen erzeugen, welche Entwicklung und Wartung erschwert.

Schwieriger ist die Modellierung von mehrfachen Beziehungen. Hier wird im Objektmodell auf Container aus Klassenbibliotheken - z. B. Listen oder Mengen - zurückgegriffen. Bei der konkreten Implementierung muß die Unterstützung der Datenbank für Massenzugriffe ausgenutzt werden, um eine gute Performanz zu erhalten.

Verwaltung der Objektidentität

Eine Objekt-Id (Abk. OID ) ist ein systemweit eindeutiger Identifikator für eine Objektinstanz. Er enthält üblicherweise neben einem Kunstschlüssel auch Informationen über die Klasse des Objekts.

Müssen keine Altsysteme auf die Daten zugreifen, können die OIDs als eigene Spalte der Datenbanktabelle abgespeichert werden. Darf bei Altanwendungen das Datenbankschema nicht verändert werden, so muß ein spezieller OID-Verwalter erstellt werden, der den Tabellennamen und den Primärschlüssel der Tabelle als OID einsetzt. Dieser klassenspezifische OID-Verwalter erbt von dem Standard-Verwalter, um Redundanz und Fallunterscheidungen zu vermeiden.

Objekttabelle und Dereferenzierung

Soll auf ein bestimmtes, persistentes Objekt zugegriffen werden, so muß das Objekt zunächst aus der Datenbank geladen werden. Dies geschieht über spezielle Klassenmethoden, die Dereferenzierungsmethoden. Diese Methoden überprüfen anhand der OID in der Objekttabelle, ob das gewünschte Objekt bereits geladen ist. In der Objekttabelle werden Referenzen auf alle derzeit in den Prozeß-Adreßraum geladenen Objekte gespeichert. Ist die Instanz noch nicht geladen, so wird der entsprechende Datenbankzugriff ausgelöst, ansonsten wird eine Referenz auf die gesuchte Instanz zurückgegeben. Zusammen mit der OID-Verwaltung wird durch diesen Mechanismus die Objektidentität sichergestellt. Die Derefenzierungsmethoden werden in C++ über "Smart Pointer" gekapselt.

Meta-Information

Um polymorphe Zugriffe richtig bearbeiten zu können, benötigt die Zugriffsschicht Informationen über die Vererbungshierarchie der Anwendung. Sie muß z. B. wissen, daß Anfragen auf die abstrakte Klasse Kunde nicht die Tabelle Kunde betreffen, sondern die beiden Tabellen PrivatKunde und GeschäftsKunde. (siehe Listing 2).

Die Zugriffsschicht muß wissen, wie sie Objekte des gewünschten Typs erzeugen kann. Diese Informationen verwaltet die in Abb. 3 dargestellte Komponente Meta-Information, indem sie den Vererbungsbaum als einen Baum von Prototypen anlegt (vgl. [Gam95]). Mit Hilfe dieser Prototypen erzeugt die Zugriffsschicht anhand der Typinformationen in der OID den dynamisch korrekten Typ unabhängig von der Anfrage. Die Meta-Information löst dadurch das Problem des dynamischen Bindens.

Listing 2: Polymorphieproblem bei Abfragen auf Supertypen
class PrivatKunde : Kunde {...};

class GeschaeftsKunde : Kunde {...};

...

d_Set<d_Ref<Kunde>> dieKunden;

...

dieKunden = Kunde::d_getSome (...);

// Die Instanzen sind von mehreren Subtypen, stehen also in unterschiedlichen Tabellen

Filter

Abfragen an die Datenbanken arbeiten mit Filterobjekten; das sind Objekte, die alle Parameter einer Anfrage aufnehmen, um dann an die Abfragemethode übergeben zu werden. Filter sind eine "Billigvariante" einer Object Query Language (Abk. OQL). Sie können sehr speziell sein, z. B. auf die Attribute einer bestimmten Klasse filtern, oder sie können völlig allgemein gehalten werden, indem sie einfach einen SQL-String aufnehmen. Durch diese große Flexibilität sind Filterobjekte besonders gut geeignet, Abfragen zu modellieren. So lassen sich z. B. Beschränkungen auf statische Abfragen einfach dadurch modellieren, daß es nur spezielle Filterobjekte gibt, die genau diese Abfragen implementieren.

Transaktionssteuerung

Die Transaktionskomponente realisiert optimistische Transaktionen und bietet beim Starten einer Transaktion die Auswahl, ob optimistisch oder pessimistisch gesperrt werden soll. Für optimistische Logik stützt sie sich auf Zeitstempel, die jedem Objekt als Attribut mitgegeben werden. Beim Beenden einer Transaktion werden die betroffenen Tabellen gesperrt, und die Zeitstempel der Objekte werden mit denen in der Datenbank verglichen. Stimmen die Stempel nicht überein, so ist ein Konflikt aufgetreten, und die Transaktion scheitert.

Hier tauchen besondere Probleme bei der Migration auf: Wie werden die Zeitstempel aktualisiert, wenn andere Anwendungen schreibend auf die Tabelle zugreifen? In diesem Zusammenhang hat sich die Lösung bewährt, einen Datenbank-Trigger auf die entsprechenden Tabellen zu setzen. Im Rahmen dieses Triggers werden dann die Stempel aktualisiert. Dieses Vorgehen ist für die Altanwendungen völlig transparent. Es ist jedoch nur durchführbar, wenn die Datenbank Trigger unterstützt und diese nicht bereits von einer Anwendung belegt sind. Ist das nicht der Fall, so ist die optimistische Transaktionslogik nicht realisierbar.

Daß Änderungen immer nur am Ende einer Transaktion komplett in die Datenbank geschrieben werden, kann auch dazu benutzt werden, die kurzen Transaktionen von Systemen wie IMS oder CICS zu überlisten. Die Zugriffsschicht generiert hier am Ende einer logischen Transaktion einen kurzen Stapellauf, der als eine Transaktion des Transaktionssystems ausgeführt werden kann (vgl. [Kel96]).

Bewertung

Die dargestellte Architektur ist auf typische Aufgabenstellungen interaktiver objektorientierter Anwendungen ausgelegt - also auf das Einlesen einzelner Objekte zur Bearbeitung durch den Benutzer. Bessere Performanz läßt sich nur noch durch den Einsatz von Objektdatenbanken erreichen, mit denen sich - für geeignete Anwendungen mit komplexen Objektgeflechten - noch einmal Beschleunigungen um den Faktor 10 bis 100 erreichen lassen. Seit neuestem gibt es eine zunehmende Anzahl von Produkten, die Objektdatenbanken und Host-Systeme koppeln können. Wir haben mit diesen Produkten allerdings noch keine praktische Erfahrung sammeln können.

Die dargestellte Architektur ist darauf ausgerichtet, daß Aktionen vom Anwendungskern angestoßen und in der Datenbank durchgeführt werden. Das bedeutet umgekehrt aber, daß keine Massenverarbeitung möglich ist, wie sie in SQL mit Befehlen wie UPDATE ... WHERE ... angestoßen werden. Anwendungen, die vor allem auf solche Konstrukte bauen, sollten nicht mit dieser Architektur gebaut werden, da sie keine klare Trennung zwischen Datenbank und Anwendungskern aufweisen.

Die Zugriffsschicht sieht es nicht vor, durch Trigger ausgelöste Änderungen der Datenbank im Anwendungskern nachzufahren. Zwar lassen sich theoretisch Datenbank-Trigger auch in der objektorientierten Welt durch Observer modellieren (vgl. [Gam95]), doch sollte dabei beachtet werden, daß diese Trigger vom physikalischen Datenbank-Layout ausgehen, also nicht unbedingt eindeutig einem Objekt zugeordnet werden können. Außerdem wären dazu Änderungen in

den "Stored Procedures" alter Anwendungen nötig. Die gleichen Probleme

erschweren die Datenintegration, wenn alte "Stored Procedures" oder "Integrity Constraints" existieren.

Die Datenintegration ist eine für Online-Systeme bewährte Migrationsstrategie im Umfeld großer EDV-Systeme. Dabei sind allerdings eine ganze Reihe technischer Probleme zu lösen, für die es in zunehmendem Maß auch fertige Produkte gibt. Die zu lösenden Probleme bleiben allerdings gleich, so daß dieser Artikel auch als Checkliste für Produkte dienen kann. Insbesondere der Zugriff über Transaktionssysteme auf Großrechner-Datenbanken und der Zugriff auf beliebig schlecht strukturierte Altdatenbestände werden aber bisher nicht ausreichend abgedeckt (vgl. [Kel96]). Ob derzeit erscheinende Produkte die Anforderungen abdecken werden, muß sich in der Praxis noch zeigen.

Viel Aufwand ist für die Bewältigung von Problemen zu erbringen, die mit Datenbankfunktionalität unmittelbar nichts zu tun haben. So ist zum Beispiel die Integration einer Zugriffsschicht in das Sicherheits- und Rechnerbetriebssystem ein nicht zu vernachlässigender Punkt. Herkömmliche Transaktionssysteme wie IMS und CICS bieten hier einfache Lösungen und gute Überwachungsmöglichkeiten, die teuer nachgebaut werden müssen, wenn man als Großanwender nicht auf sie verzichten kann.

Ein wichtiger Zusatznutzen eines Migrationsprojektes nach der vorgestellten Methode kann ein Generator für konventionelle Datenbank-Zugriffsschichten auf dem Host sein. Man kann Zugriffsmodule einer Tupelschnittstelle so konstruieren, daß sie für objektorientierte und konventionelle Anwendungen geeignet sind. Ist bei einem Anwender eine solche konventionelle Datenbankzugriffsschicht noch nicht vorhanden, kann alleine daraus oft schon der Nutzen für ein Zugriffsschicht-Projekt gerechtfertigt werden.


Literatur

[Cat94] R.G.G. Cattell, Object Data Management, Addison-Wesley 1994

[Cha96] A.B. Chaudhri, P. Osmon, Databases for a New Generation -- What are the Options for Managing Persistent Objects?, in: Object Expert, S. 26ff., März/April 1996

[Ebe95] K. Eberhardt, S. Kutscha, Veraltete Systeme hemmen Innovation -- Altsystem-Abhängigkeit ist wichtiger als High-Tech-Themen, in: Computerwoche CW-Extra, 17. Februar 1995

[Gam95] E. Gamma, R. Helm, R. Johnson, J. Vlissides, Design Patterns, Elements of Reusable Object Oriented Software, Addison-Wesley, 1995

[Hah95] W. Hahn, F. Toennissen, A. Wittkowski: Eine objektorientierte Zugriffsschicht zu relationalen Datenbanken, in: Informatik Spektrum 18(Heft 3/1995); S. 143-151, 1995

[Kel96] W. Keller, Ch. Mitterbauer, K. Wagner, Objektorientierte Datenintegration über mehrere Technologiegenerationen, Proceedings ONLINE, Kongress VI, Hamburg 1996

[ODMG96] R.G.G. Cattell (Ed.) et. al., Object Database Standard (ODMG 93), Release 1.2; Morgan Kaufmann Publishers, 1996


Kontakt

Jens Coldewey (E-Mail-Adresse: jensc@sdm.de) und Wolfgang Keller (E-Mail-Adresse: wk@sdm.de) sind Berater bei der Firma sd&m, software design & management GmbH & Co. KG. Sie begleiten Großkunden bei der Einführung objektorientierter Technologie. Beide arbeiten derzeit in dem vom BMBF geförderten Projekt ARCUS, das sich mit Entwurfsmustern für die objektorientierte Software-Architektur betrieblicher Informationssysteme befaßt (WWW-Adresse: http://www.sdm.de/g/arcus/).