Einführung
Dieses Konzeptdokument enthält einen Überblick über die Objektmodelle und relationalen Datenmodelle sowie eine
zusammenfassende Beschreibung eines Framework für persistente Datenspeicherung.
Relationale
Datenbanken und Objektorientierung
Relationale Datenbanken und Objektorientierung sind nicht vollständig kompatibel. Sie stellen zwei unterschiedliche
Sichten der Welt dar: In einem RDBMS sehen Sie nur Daten, in einem objektorientierten System nur Verhalten. Es ist
nicht so, dass eine Perspektive besser ist als die andere. Das objektorientierte Modell eignet sich eher für Systeme
mit komplexem und zustandsabhängigem Verhalten, in denen Daten zweitrangig sind, und Systeme, in denen durch Navigation
in einer natürlichen Hierarchie (z. B. Teilelisten) auf die Daten zugegriffen wird. Das RDBMS-Modell eignet sich für
Berichtsanwendungen und Systeme, in denen die Beziehungen dynamisch oder Ad-hoc sind.
Tatsächlich müssen beide Systeme zusammenarbeiten. In relationalen Datenbanken sind viele Informationen gespeichert.
Wenn objektorientierte Anwendungen auf diese Daten zugreifen möchten, müssen Sie in der Lage sein, aus einem RDBMS zu
lesen und in ein RDBMS zu schreiben. Außerdem müssen sich objektorientierte Systeme häufig Daten mit nicht
objektorientierten Systemen teilen. Deshalb ist es nahe liegend, ein RDBMS als Mechanismus für die gemeinsame Nutzung
der Daten einzusetzen.
Obwohl das objektorientierte und das relationale Design einige Gemeinsamkeiten haben (Objekte sind konzeptionell mit
Entitäten vergleichbar und Attribute mit Spalten), ist eine nahtlose Integration aufgrund der fundamentalen
Unterschiede eine Herausforderung. Der wichtigste Unterschied ist der, dass Datenmodelle Daten (über Spaltenwerte)
offen legen und Objektmodelle die Daten hinter ihren öffentlichen Schnittstellen verbergen.
Das relationale Modell setzt sich aus Entitäten und Relationen zusammen. Eine Entität kann eine physische Tabelle oder
eine logische Projektion mehrerer Tabellen sein. Diese logische Projektion wird auch Sicht genannt. Die folgende
Abbildung zeigt die Tabellen POSITION, AUFTRAG und PRODUKT und deren Beziehungen untereinander. Ein relationales Modell
hat die folgenden Elemente:
Ein relationales Modell
Eine Entität hat Spalten. Jede Spalte hat einen Namen und einen Typ. In der vorherigen Abbildung hat die Entität
POSITION die Spalten Positions_Id (der Primärschlüssel), Beschreibung, Preis, Menge, Produkt_Id und Auftrags_Id (die
letzten beiden Spalten sind Fremdschlüssel, die die Entität POSITION mit den Entitäten AUFTRAG und PRODUKT verknüpfen).
Eine Entität hat Datensätze oder Zeilen. Jede Zeile enthält eine eindeutige Sammlung von Informationen, die in der
Regel die persistenten Daten eines Objekts darstellen.
Jede Entität hat mindestens einen Primärschlüssel. LineItem_Id ist der Primärschlüssel für LINEITEM.
Die Unterstützung von Relationen ist anbieterspezifisch. Das Beispiel veranschaulicht das logische Modell und die
Relation zwischen den Tabellen PRODUKT und POSITION. Im physischen Modell werden Relationen normalerweise mit
Fremdschlüssel/Primärschlüssel-Referenzen implementiert. Wenn eine Entität in Relation zu einer anderen steht, enthält
sie Spalten, die Fremdschlüssel sind. Fremdschlüsselspalten enthalten Daten, die bestimmte Datensätze in der Entität
mit der zugehörigen Entität in Relation setzen können.
Relationen weisen Multiplizität (oder Kardinalität) auf. Gebräuchliche Kardinalitäten sind 1:1, 1:m, (m:1) und m:n. In
dem Beispiel hat POSITION eine 1:1-Beziehung zu PRODUKT und PRODUKT eine 0:m-Beziehung zu POSITION.
Ein Objektmodell enthält unter anderem Klassen (siehe [UML01] für eine
vollständige Definition eines Objektmodells). Klassen definieren die Struktur und das Verhalten einer Gruppe von
Objekten oder Instanzen. Die Struktur wird in Form von Attributen (Datenwerten) und Assoziationen
(Beziehungen zwischen Klassen) dargestellt. Die folgende Abbildung veranschaulicht ein einfaches Klassendiagramm, das
nur die Attribute (Daten) der Klassen zeigt.
Objektmodell (Klassendiagramm)
Ein Auftrag hat eine Nummer (die Auftragsnummer) und eine Assoziation zu einem oder mehreren (1..*) Positionen. Jede
Position hat eine Menge (die bestellte Menge).
Das Objektmodell unterstützt Vererbung. Eine Klasse kann Daten und Verhalten von einer anderen Klasse (z. B. erben die
Produkte Softwareprodukt und Hardwareprodukt Attribute und Methoden von der Klasse Produkt).
Die meisten Geschäftsanwendungen setzen relationale Technologie für physische Datenspeicher ein. Entwickler
objektorientierter Anwendungen müssen die Herausforderung bewältigen, die relationale Datenbank so zu integrieren, dass
Änderungen am Datenmodell sich nicht negativ auf das Objektmodell auswirken und umgekehrt. Es gibt viele Lösungen, mit
denen Anwendungen direkt auf relationale Daten zugreifen können. Die Herausforderung ist eine nahtlose Integration von
Objektmodell und Datenmodell.
Datenbank-APIs werden in unterschiedlichen Standardausführungen bereitgestellt (z. B. die Microsoft Open Data Base
Connectivity API oder ODBC) und sind proprietär (native Bindungen zu bestimmten Datenbanken). Die APIs bieten
DML-Pass-Through-Services (Data Manipulation Language, Datenbearbeitungssprache), mit denen Anwendungen direkt auf
relationale Daten zugreifen können. In objektorientierten Anwendungen müssen die Daten einer objektrelationalen
Übersetzung unterzogen werden, damit sie von der Anwendung verwendet werden können. Hierfür muss ein nicht
unerheblicher Teil des Anwendungscodes die Ergebnisse der Datenbank-API in Anwendungsobjekte übersetzen. Das
objektrelationale Framework kapselt den physischen Datenspeicher und stellt die entsprechenden Services für die
Übersetzung der Daten in Objekte zur Verfügung.
Zweck eines Framework für die persistente Datenspeicherung
Anwendungsentwickler verbringen mehr als 30 % ihrer Zeit mit der Implementierung des Zugriffs auf relationale
Datenbanken in objektorientierten Anwendungen. Wenn die objektrelationale Schnittstelle nicht ordnungsgemäß
implementiert wird, ist die Investition verloren. Die Implementierung eines objektrelationalen Framework sichert diese
Investition. Das objektrelationale Framework kann in nachfolgenden Anwendungen wiederverwendet werden, was die Kosten
für die objektrelationale Implementierung auf weniger als 10 % der Gesamtimplementierungskosten reduziert. Die
wichtigsten Kosten, die bei der Implementierung jedes Systems zu berücksichtigen sind, sind die für die Pflege. Über 60
% der Gesamtkosten eines Systems können auf die gesamte Lebensdauer gesehen der Pflege zugeschrieben werden. Ein
schlecht implementiertes objektrelationales System ist sowohl aus technischer als auch aus finanzieller Sicht ein
Albtraum.
-
Leistung. Besondere Aufmerksamkeit muss dem Zerlegen von Objekten in Daten und dem Zusammensetzen
von Objekten aus Daten gewidmet werden. In Systemen mit einem hohen und kritischen Datendurchsatz ist dies die
Achillesferse einer Zugriffsschicht mit mangelhaftem Design.
-
Designkompromisse minimieren. Ein beliebtes Verfahren der Anhänger der Objektorientierung, die
bereits Systeme mit relationalen Datenbanken erstellt haben, besteht darin, das Objektmodell so anzupassen, dass
Daten einfacher in relationalen Systemen abgelegt werden können, und das relationale Modell so zu ändern, dass
Objekte einfacher gespeichert werden können. Obwohl häufig geringfügige Anpassungen erforderlich sind, minimiert
eine sorgfältig entworfene Zugriffsschicht negative Eingriffe in das Design von Objektmodell und relationalem
Modell.
-
Erweiterbarkeit. Die Zugriffsschicht ist ein Whitebox-Framework, das Anwendungsentwicklern die
Erweiterung des Framework ermöglicht, falls eine bestimmte Funktionalität im Framework gewünscht wird.
Normalerweise unterstützt eine Zugriffsschicht ohne Erweiterung 65-85 % der Datenspeicheranforderungen einer
Anwendung. Falls die Zugriffsschicht nicht als erweiterbares Framework entworfen ist, kann die Erfüllung letzten
35-15 % sehr schwierig und kostenintensiv sein.
-
Dokumentation. Die Zugriffsschicht ist sowohl eine Blackbox-Komponente als auch ein
Whitebox-Framework. Die API der Blackbox-Komponente muss klar definiert, sorgfältig dokumentiert und leicht
verständlich sein. Wie bereits erwähnt, ist das Design der Zugriffsschicht auf Erweiterung ausgelegt. Ein
erweiterbares Framework muss sehr sorgfältig dokumentiert werden. Klassen, die in Unterklassen aufgeteilt werden
sollen, müssen angegeben werden. Die Merkmale der Protokolle aller relevanten Klassen (z. B. öffentlich, privat,
geschützt, final...) müssen angegeben werden. Außerdem muss zur Vereinfachung der Erweiterbarkeit ein erheblicher
Teil des Designs des Zugriffsschicht-Frameworks veröffentlicht und dokumentiert werden.
-
Unterstützung gebräuchlicher objektrelationaler Zuordnungen. Eine Zugriffsschicht muss
verschiedene grundlegende objektrelationale Zuordnungen unterstützen, auch wenn keine Erweiterung erforderlich ist.
Diese objektrelationalen Zuordnungen werden in einem späteren Abschnitt dieses Dokuments beschrieben.
-
Persistenzschnittstellen. In einer objektorientierten Anwendung enthält das Geschäftsmodell für
eine Objektanwendung das semantische Wissen über die Problemdomäne. Entwickler müssen Objekte bearbeiten und mit
Objekten interagieren können, ohne sich zu viel Gedanken über die Details der Datenspeicherung und des Datenabrufs
zu machen. Dazu muss den Anwendungsentwicklern eine sorgfältig definierte Untergruppe persistenter Schnittstellen
(speichern, löschen, suchen) bereitgestellt werden.
Es werden immer mehr einheitliche Verfahren für objektrelationale Anwendungen entwickelt. IT-Experten, die sich
wiederholt durch diese Thematik gekämpft haben, verstehen und erkennen mittlerweile bestimmte Strukturen und Verhalten,
die erfolgreiche objektrelationale Anwendungen aufweisen. Diese Strukturen und Verhalten wurden mit den
CORBA-Servicespezifikationen auf hoher Ebene formalisiert (die sich gleichermaßen für COM/DCOM-basierte Systeme
eignen).
Im Folgenden sind die CORBA-Servicespezifikationen aufgelistet, die in der objektrelationale Zuordnung anwendbar und
hilfreich sind:
In den folgenden Abschnitten werden diese Kategorien verwendet, um die Diskussion über einheitliche objektrelationale
Services zu strukturieren. Nähere Einzelheiten sollten Sie in den entsprechenden CORBA-Spezifikationen nachlesen.
Persistenz ist ein Begriff, der beschreibt, wie Objekte ein sekundäres Speichermedium verwenden, um ihren Zustand über
diskrete Sitzungen hinweg aufrecht zu erhalten. Persistenz bietet einem Benutzer die Möglichkeit, Objekte in einer
Sitzung zu speichern und in einer späteren Sitzung erneut auf sie zuzugreifen. Beim späteren Zugriffen haben die
Objekte genau denselben Zustand (z. B. Attribute) wie in der vorherigen Sitzung. In Mehrbenutzersystemen ist dies
möglicherweise nicht der Fall, da andere Benutzer auf dieselben Objekte zugreifen und diese ändern können. Persistenz
steht in Wechselbeziehung zu den anderen in diesem Abschnitt beschriebenen Services. Der Hinweis auf Beziehungen,
Parallelität und andere Services ist beabsichtigt (und mit der CORBA-Dekomposition der Services konsistent).
Beispiele für bestimmte Services der Persistenz:
-
Verbindungsmanagement für Datenquellen: Objektrelationale Anwendungen müssen eine Verbindung zur
physischen Datenquelle aufbauen. Relationale Datenbanksysteme erfordern in der Regel die Angabe des Servers und der
Datenbank. Die Einzelheiten des Verbindungsmanagements sind in der Regel von Datenbanklieferant zu
Datenbanklieferant verschieden, und das Framework muss auf entsprechend flexible Weise gestaltet werden.
-
Objektabruf: Wenn Objekte aus einer Datenbank wiederhergestellt werden, werden Daten aus der
Datenbank abgerufen und in Objekte übersetzt. Bei diesem Prozess werden Daten aus datenbankspezifischen Strukturen,
die von der Datenquelle abgerufen werden, extrahiert. Anschließend werden die Daten von Datenbanktypen in die
entsprechenden Objekttypen und/oder Klassen zerlegt, das entsprechende Objekt erstellt und spezielle
Objektattribute gesetzt.
-
Objektspeicherung: Der Prozess der Objektspeicherung ist eine Spiegelung des Objektabrufs. Die
Werte der entsprechenden Attribute werden aus dem Objekt extrahiert. Anschließend wird eine datenbankspezifische
Struktur mit den Attributwerten (eine SQL-Zeichenfolge, eine gespeicherte Prozedur oder ein spezieller
Fernprozeduraufruf) erstellt und die Struktur an die Datenbank übergeben.
-
Löschen von Objekten: Wenn Objekte aus einem System gelöscht werden, müssen die zugehörigen Daten
aus der relationalen Datenbank gelöscht werden. Beim Löschen eines Objekts müssen entsprechende Informationen aus
dem Objekt extrahiert werden. Anschließend muss eine Löschanforderung (eine SQL-Zeichenfolge, eine gespeicherte
Prozedur oder ein spezieller Fernprozeduraufruf) erstellt und die Anforderung an die Datenbank übergeben werden. In
einigen Sprachen wie Smalltalk und Java wird ein explizites Löschen von Objekten nicht unterstützt. Stattdessen
wird eine Strategie mit dem Namen Garbage-Collection unterstützt. Frameworks für die persistente
Datenspeicherung, die diese Sprachen unterstützen, müssen eine Alternative bieten, mit der Daten aus der Datenbank
entfernt werden können, sobald keine Anwendungen mehr auf die Daten verweisen. Eine gebräuchliche Methode für die
Datenbank ist die Verwaltung von Referenzzählern, die Aufschluss darüber geben, wie oft ein Objekt
von anderen Objekten referenziert wird. Wenn der Referenzzähler für ein Objekt auf 0 abfällt, verweisen keine
anderen Objekte mehr auf das Objekt, woraufhin das Objekt gelöscht werden kann. Es
kann in Ordnung sein, Objekte mit einem Referenzzähler von null zu löschen, da ein Objekt, selbst
wenn es nicht mehr referenziert wird, trotzdem abgefragt werden kann. Es muss eine datenbankweit gültige Richtlinie
definiert werden, die festlegt, wann das Löschen von Objekten zulässig ist.
Die persistente Speicherung von Objekten ist nur wenig hilfreich, wenn kein Mechanismus für das Suchen und Abrufen
spezieller Objekte vorhanden ist. Mit Abfragefunktionen können Anwendungen auf der Basis verschiedener Kriterien
Objekte abfragen und abrufen. Die vom objektrelationalen Zuordnungs-Framework bereitgestellten Basisabfrageoperationen
sind find und find unique. Die Operation find unique ruft ein bestimmtes Objekt ab, und find gibt eine Sammlung von
Objekten basierend auf Abfragekriterien zurück.
Abfragefunktionen für Datenspeicher variieren erheblich. Einfache dateibasierte Datenspeicher können strenge,
hausgemachte Abfrageoperationen implementieren, während relationale Systeme eine flexible Datenbearbeitungssprache
unterstützten. Objektrelationale Zuordnungs-Frameworks erweitern das relationale Abfragemodell, um es von der
Datenorientierung in Richtung Objektorientierung zu führen. Außerdem sind Pass-Through-Mechanismen implementiert, um
die Flexibilität relationaler Abfragen und anbieterspezifische Erweiterungen (z. B. gespeicherte Prozeduren) nutzen zu
können.
Manchmal treten Konflikte zwischen datenbankbasierten Abfragemechanismen und dem Objektkonzept auf. Die
Abfragemechanismen der Datenbank werden über Werte von Attributen (Spalten) in einer Tabelle
gesteuert. In den entsprechenden Objekten verhindert das Prinzip der Kapselung jedoch, dass die Werte der Attribute
sichtbar sind. Die Werte werden von den Operationen der Klasse gekapselt. Die Kapselung bringt den
Vorteil, dass sich Anwendungen einfacher ändern lassen. Die interne Struktur einer Klasse kann geändert werden, ohne
sich Gedanken über die abhängigen Klassen zu machen, solange die öffentlich sichtbaren Operationen der Klassen nicht
geändert werden. Ein Abfragemechanismus, der auf der Datenbank basiert, ist von der internen Darstellung einer Klasse
abhängig und steht damit im Gegensatz zum Prinzip der Kapselung. Das Framework hat deshalb die
herausfordernde Aufgabe zu verhindern, dass Abfragen die Anwendungen für Änderungen anfällig machen.
Transaktionsunterstützung ermöglicht dem Anwendungsentwickler, eine atomare Arbeitseinheit zu definieren. In der
Datenbankterminologie bedeutet dies, dass das System in der Lage sein muss, eine Reihe von Änderungen auf die Datenbank
anzuwenden, oder sicherstellen muss, dass keine der Änderungen angewendet wird. Die Operationen in einer Transaktion
müssen alle erfolgreich ausgeführt werden können, oder die Transaktion insgesamt scheitert. Objektrelationale
Frameworks müssen mindestens eine der relationalen Datenbank ähnliche Funktion für das Festschreiben und
Rückgängigmachen (COMMIT/ROLLBACK) von Transaktionen bereitstellen. Das Design objektrelationaler Frameworks in
Mehrbenutzerumgebungen kann viele Herausforderungen mit sich bringen und muss deshalb sorgfältig überdacht werden.
Zusätzlich zu den vom Framework für die persistente Datenspeicherung bereitgestellten Funktionen muss die Anwendung
wissen, wie Fehler behandelt werden. Wenn eine Transaktion scheitert oder abgebrochen wird, muss das System in der Lage
sein, die Transaktion in einem stabilen Zustand wiederherzustellen. Hierfür werden in der Regel die vorherigen
Zustandsinformationen aus der Datenbank gelesen. Deshalb besteht eine enge Interaktion zwischen dem Framework für
persistente Datenspeicherung und dem Framework für die Fehlerbehandlung.
Objektorientierte Mehrbenutzersysteme müssen den gleichzeitigen Zugriff auf Objekte (Parallelität) steuern. Wenn
mehrere Benutzer gleichzeitig auf ein Objekt zugreifen, muss das System einen Mechanismus bereitstellen, der
sicherstellt, dass Änderungen am Objekt im persistenten Speicher in einer vorhersehbaren und kontrollierten Weise
vorgenommen werden. Objektrelational Frameworks können pessimistische und/oder optimistische Steuerungselemente für den
gemeinsamen Zugriff implementieren.
-
Die pessimistische Steuerung des gemeinsamen Zugriffs setzt voraus, dass der Anwendungsentwickler
seine Absicht formuliert, wenn das Objekt aus dem Datenspeicher abgerufen wird (z. B. Lesezugriff, Schreibsperre
usw.). Wenn Objekte gesperrt werden, können andere Benutzer blockiert werden, wenn sie auf das Objekt zugreifen,
und dazu gezwungen werden, auf die Freigabe der Sperre zu warten. Eine pessimistische Steuerung des gemeinsamen
Zugriffs sollte mit Sorgfalt verwendet und implementiert werden, da sie leicht zu Deadlock-Situationen führen kann.
-
Bei der optimistischen Steuerung des gemeinsamen Zugriffs wird angenommen, dass es
unwahrscheinlich ist, dass mehrere Benutzer gleichzeitig auf dasselbe Objekt zugreifen. Konflikte beim gemeinsamen
Zugriff werden festgestellt, wenn die Änderungen in der Datenbank gespeichert werden. Wenn das Objekt nach seinem
Abruf von einem anderen Benutzer geändert wurde, wird ein Fehler an die Anwendung zurückgegeben, in dem das
Scheitern der Änderungsoperation angezeigt wird. Die Anwendung ist dafür verantwortlich, den Fehler festzustellen
und zu behandeln. Dies macht den Ruf nach einem Framework laut, das die parallelen Werte der Objekte speichert und
sie mit der Datenbank vergleicht. Die optimistische Steuerung des gemeinsamen Zugriffs erzeugt weniger Kosten, wenn
nur wenige Konflikte auftreten, ist aber teurer, wenn die Anzahl der Konflikte relativ hoch ist, weil in diesem
Fall Arbeiten doppelt ausgeführt werden müssen.
Alle Anwendungen, die gemeinsame Daten verwenden, müssen dieselbe Strategie für die Steuerung des gemeinsamen Zugriffs
verwenden. Wenn Sie für dieselben gemeinsamen Daten einmal eine optimistische und einmal eine pessimistische Steuerung
des gemeinsamen Zugriffs verwenden, können die Daten beschädigt werden. Eine konsistente Strategie für die Steuerung
des gemeinsamen Zugriffs wird am besten durch ein Framework für persistente Datenspeicherung gewährleistet.
Objekte haben Beziehungen zu anderen Objekten. Ein Objekt Auftrag kann beispielsweise viele Objekte Position haben. Ein
Objekt Buch kann viele Objekte Kapitel haben. Ein Objekt Mitarbeiter gehört zu genau einem Objekt Unternehmen. In
relationalen Systemen werden Beziehungen zwischen Entitäten mit Fremdschlüssel/Primärschlüssel-Referenzen
implementiert. In objektorientierten Systemen werden Relationen in der Regel explizit mit Attributen implementiert.
Wenn ein Objekt Auftrag Positionen hat, enthält das Objekt Auftrag ein Attribut mit dem Namen Positionen. Das Attribut
Positionen von Auftrag enthält viele Objekte Position.
Die Beziehungsaspekte eines objektrelationalen Framework sind von Persistenz-, Transaktions- und Abfrageservices
unabhängig. Wenn ein Objekt gespeichert, abgerufen, bearbeitet oder abgefragt wird, müssen die zugehörigen Objekte
berücksichtigt werden:
-
Müssen beim Abruf eines Objekts auch die zugeordneten Objekte abgerufen werden? Im Prinzip ja. Der Abruf der
zugehörigen Objekte kann jedoch teuer werden, wenn diese Objekte nicht benötigt werden. Ein gutes Framework lässt
eine Kombination von Strategien zu.
-
Müssen beim Speichern eines Objekts auch die zugeordneten Objekte gespeichert werden, wenn sie geändert wurden?
Auch hier richtet sich die Antwort nach dem Kontext.
Obwohl es konzeptionell gesehen vorteilhaft ist, objektrelationale Services gesondert zu betrachten, sind die
zugehörigen Implementierungen des objektrelationalen Framework jedoch von diesen Services abhängig. Die Services müssen
nicht nur in einzelnen Organisationen konsistent implementiert werden, sondern in allen Anwendungen, die sich Daten
teilen. Ein Framework ist die einzige ökonomische Lösung.
|