Die objektorientierte Programmierung ist in der heutigen Softwareentwicklung nicht mehr wegzudenken. Auf Sprachelemente wie Kapselung, Polymorphie und Vererbung kann in modernem Softwaredesign nur schwer verzichtet werden. Ebenso sind relationale Datenbanken noch immer die erste Wahl für die persistente Speicherung von Daten. Sie skalieren für große Datenmengen, sind sicher, verwalten Transaktionen zwischen Benutzern und besitzen den größten Marktanteil von allen Datenbanken.
Die Sammlung der Probleme die auftreten, wenn man objektorientierte Datenstrukturen in einer relationalen Datenbank speichern möchte, werden als Object-relational Impedance Mismatch bezeichnet.
In dieser Arbeit werden die entstehenden Probleme, die durch den Unterschied zwischen den beiden Paradigmen entstehen, erläutert und bisher gefundene Lösungen für den Impedance Mismatch vorgestellt. Es wird gezeigt, warum es keine einzige perfekte Lösung für den IM geben kann, weshalb die bisher in der Industrie entwickelten Persistenzmechanismen in vielen individuellen Bereichen Anwendung finden.
Die Strategien werden kritisch betrachtet und eigene Verbesserungen und Konzepte ausgearbeitet, die eine Basis für ein weiteres Framework für Webapplikationen legen. Die Effizienz dieser verbesserten Konzepte wird durch einen Vergleich mit ähnlichen Produkten untersucht.
Die Theorie des Object-relational Impedance Mismatch, Die Kernprobleme, Lösungen: Ersetzen der Datenbank, Object-relational Mapper
Object-relational Mapping: Speichern des Zustandes eines Objektes, Speichern von Klassenhierarchien, Horizontales Mapping, Vertikales Mapping, Filter-Mapping, Generelles Mapping, Speichern von Beziehungen zwischen Objekten, Laden und Marshalling von Objekten, Vertiefung: Object Loading
Frameworks: Hibernate und Vererbung, Abfragesprachen, Beziehungen zwischen Objekten, Object Loading, Abbildung von Klassen und Attributen
Lösungsansätze am Beispiel-Framework PSCORM: Konzepte und Implementierung: Dynamischer Code, Metainformationen, Cache, Datenbankabstraktion, Abbildung von Klassen und Attributen, Abbildung von Vererbung, Beziehungen zwischen Objekten, Abfragesprache, Object Loading
INHALTSVERZEICHNIS
I Die Theorie des Object-relational Impedance Mismatch
1 Einführung
1.1 Der Aufbau dieser Arbeit
2 Die Kernprobleme
3 Lösungen
3.1 Ersetzen der Datenbank
3.2 Object-relational Mapper
4 Object-relational Mapping
4.1 Speichern des Zustandes eines Objektes
4.2 Speichern von Klassenhierarchien
4.2.1 Horizontales Mapping
4.2.2 Vertikales Mapping
4.2.3 Filter-Mapping
4.2.4 Generelles Mapping
4.3 Speichern von Beziehungen zwischen Objekten
4.4 Laden und Marshalling von Objekten
4.5 Vertiefung: Object Loading
5 Frameworks
5.1 Übersicht
5.2 Hibernate und Vererbung
5.3 Abfragesprachen
5.4 Beziehungen zwischen Objekten
5.5 Object Loading
5.6 Abbildung von Klassen und Attributen
5.7 Zusammenfassung
6 Fazit
6.1 Gibt es eine perfekte Lösung?
II Lösungsansätze am Beispiel-Framework PSCORM
7 Einführung
7.1 Aufbau dieses Teils
7.2 Besonderheiten von PHP
8 Konzepte und Implementierung
8.1 Dynamischer Code
8.2 Metainformationen
8.3 Cache
8.4 Datenbankabstraktion
8.5 Abbildung von Klassen und Attributen .
8.6 Abbildung von Vererbung
8.7 Beziehungen zwischen Objekten
8.8 Abfragesprache
8.9 Object Loading
9 Evaluation
9.1 Konfiguration
9.2 Test 1
9.3 Test 2
9.4 Ergebnisse
10 Zusammenfassung
A Beispiel Datenbank-Schema
B Quelltext der Evaluation
C Implementierungsübersicht PSCORM
Literaturverzeichnis
Teil I
Die Theorie des Object-relational Impedance Mismatch
1. Einführung
Impedance matching bezeichnet in der Elektrotechnik einen Vorgang, bei dem der Eingangswiderstand des Verbrauchers oder der Ausgangswiderstand der Quelle in einem Stromkreis so angepasst wird, dass der Wirkungsgrad genau 50% beträgt. Abstrakter formuliert muss man Leistungen zwischen zwei Punkten angleichen, um das optimale Ergebnis zur Übertragung von Signalen oder Energie zu erhalten. Der Begriff wurde dann später in die Informatik übernommen.
Definition 1 (Object-relational Impedance Mismatch):
Der Objectrelational Impedance Mismatch (kurz: IM) bezeichnet die Unvertr äglich keit zwischen dem relationalen Datenmodell (definiert durch das relationale Schema) und dem objektorientierten Programmierparadigma.
Beim Impedance matching in der Informatik sind die zwei Punkte, zwischen denen die Übertragung optimiert werden soll, die Welt der relationalen Datenbanken und die Welt der Objektorientierung.
Die objektorientierte Programmierung ist heute eine der bekanntesten Programmierparadigmen und beeinflusst das Design, die Methoden und den Entwicklungsprozess von moderner Software. Die Strukturen der objektorientierten Programmierung eignen sich am besten um die Anwendungslogik (Business Logic) und ein Model der realen Welt abzubilden. Polymorphie, Kapselung und Vererbung sind aus moderner Softwareentwicklung nicht mehr wegzudenken.
Relationale Datenbanken wurden konzipiert riesige Mengen von elementaren Da- tentypen sicher abzuspeichern und schnell und effizient abfragen zu können. Im relationalen Schema wird ein Model der realen Welt durch Relationen dargestellt. Relationen unterliegen dem mathematischen Konzept der mengentheoretischen Relation, dem Kartesischen Produkt von einer Liste von Ausprägungen der elementaren Datentypen.
Diese beiden Paradigmen eignen sich sehr gut, die Probleme auf ihrem Gebiet zu lösen, unterscheiden sich aber grundlegend in der Denkweise, der Methodik und im Design.
Heutzutage benötigt fast jede objektorientierte Applikation einen persistenten Da- tenspeicher. Muss der Zugriff auf die Daten auch für große Mengen performant bleiben, werden fast immer relationale Datenbankmanagementsysteme (RDMBS) hin- zugezogen. Es existiert also immer das Bedürfnis das Model der objektorientierten Applikation und das Model des relationalen Schemas miteinander zu verbinden. Nach einer Studie werden 30 - 40% der Entwicklungszeit einer Applikation dafür verwendet objektrelationale Lösungen für die Daten zu finden [Kee04]. Wenn der IM einmal sorgfältig gelöst wurde, hat das große Vorteile für die Entwicklung jeder weiteren Anwendung.
Bevor man sich also mit der Anwendungslogik und den Problemen beschäftigt, die sich in jedem Projekt neu ergeben, sollte man Zeit investieren den IM einmal zufriedenstellend zu lösen, um nicht in jedem Projekt erneut ein Drittel der verfügbaren Zeit verbrauchen zu müssen [New06].
1.1 Der Aufbau dieser Arbeit
Im nächsten Kapitel werden die Kernprobleme des Impedance Mismatch vorgestellt. Diese Probleme werden sich in allen Bereichen dieser Arbeit wiederfinden und unter verschiedenen Gesichtspunkten betrachtet werden.
Im dritten Kapitel werden allgemeine Lösungen für den IM vorgestellt: Entweder ersetzt man das relationale Datenbanksystem mit einem alternativen System, oder man versucht das Problem auf Softwareebene anzugehen. Es werden objektorien- tierte Datenbankmanagementsysteme betrachtet, sowie die Weiterentwicklung von relationalen Datenbankmanagementsystemen mit objektrelationalen Features.
Im vierten Kapitel konzentriere ich mich auf die Softwarelösung. Die Strategien eines Object-relational Mapper werden vorgestellt, die Anforderungen definiert und mögliche Schwierigkeiten veranschaulicht.
Im fünften Kapitel wird ein kleiner Überblick über die große Masse von Frameworks, die sich als Lösung für den IM bezeichnen gegeben. Auch die Standards für die Speicherung von Objekten, die größtenteils durch den Java Community Process entwickelt wurden, werden berücksichtigt.
Zusätzlich werden zwei PHP Frameworks (Kohana und Doctrine) und zwei Java Frameworks (Hibernate und TopLink) untersucht. Ich beschreibe, wie diese die Anforderungen aus Kapitel 4 implementieren.
Der erste Teil wird mit einem Fazit in Kapitel 6 abgeschlossen.
Im zweiten Teil der Arbeit wird ein selbstentwickeltes Framework in PHP namens PSCORM vorgestellt. Anhand dieses Beispiels, wird eine Möglichkeit gezeigt, wie man ein solches Framework umsetzen könnte.
1.1. DER AUFBAU DIESER ARBEIT 5
In der Einführung werden spezielle Eigenarten der Webentwicklung mit PHP vermittelt und erklärt, warum sich diese so stark auf die praktische PHP Programmierung und auf PHP Frameworks auswirken.
Im zweiten Kapitel werden Besonderheiten der Implementierung von PSCORM skizziert und Ideen aus dem theoretischen Teil weiter ausformuliert. Es werden Strate- gien aus den untersuchten Frameworks wieder aufgegriffen und eigene erklärt.
In einer Evaluation wird PSCORM mit Kohana und Doctrine, die schon im ersten Teil der Arbeit untersucht wurden, verglichen. Es werden zwei Tests für das Laden von Objekten ausgewertet. Danach werden diese Ergebnisse analysiert und kritisch bewertet, ob die neuen Konzepte für das Framework wirkliche Verbesserun- gen bedeuten können.
Im Schlusskapitel werden die Erkenntnisse dieser Arbeit zusammengefasst.
2 Die Kernprobleme
Es stellt sich zuerst die Frage, welche konkreten Probleme zur Überbrückung des Im-pedance Mismatches gelöst werden müssen. Um den Zustand der Objekte und das Modell der objektorientierten Applikation abdeckend in der Datenbank persistent speichern zu können, müssen zumindest folgende Teilprobleme behandelt werden:
Speichern der Struktur einer Klasse Ein Objekt im objektorientierten Model besitzt eine Klasse. Die Klasse definiert die Methoden und Eigenschaften des Objek- tes und kann sich in einer Klassenhierarchie befinden. Im relationalen Schema sind Klassen nicht enthalten und können deshalb nicht natürlich abgebildet werden.
Speichern des Zustandes eines Objektes Eine Objektinstanz kann in der Applikation mehrere Zustände annehmen und wird von Methoden von einem in den nächsten überführt. Der Zustand des Objektes muss im relationalen Schema dargestellt werden. Wie kann dieser identifizierbar sein und wieviel muss man von der Objektinstanz in der Datenbank pflegen, um dies zu erreichen?
Speichern von Beziehungen zwischen Objekten Im objektorientierten Model haben Objektinstanzen die Möglichkeiten untereinander Daten auszutauschen. Objekte bestehen nicht nur allein sondern sind mit anderen Objekten gekoppelt (Assoziation), aus weiteren Objekten zusammengesetzt (Komposition) oder beeinhalten Objekte (Aggregation). Diese Objektbeziehungen müssen im persistenten Datenspeicher festgehalten werden, denn auch sie bestimmen den Zustand eines Objektes. Ein Spezialfall einer Beziehung zwischen Objekten ist die Vererbung.
Speichern von Klassenhierarchien (Vererbung) Objektorientierte Modelle haben keine flachen Hierarchien. Klassen stehen durch das Prinzip der Vererbung in einer hierarchischen Beziehung zueinander. Im relationalen Schema ist es nicht vorgesehen Vererbung natürlich zu modellieren.
Abfrage von Objekten Mit einem RDMBS können komplexe Abfragen einfach gestellt und effizient bearbeitet werden. Diese Abfragen beziehen sich meist auf eine Menge von Objekten mit bestimmten Kriterien. Der Zugriff auf die Objekte wird also über Mengen erreicht. Im objektorientierten Model geschieht der Zugriff über die Navigation von Objektbeziehungen. Eine Gesamtsicht auf alle Objekte oder das Bilden von konkreten Mengen von Objekten ist schwierig, da immer Pfade von Beziehungen analyisert werden müssen.
Marshalling von Objekten Das Marhshalling bezeichnet den Vorgang aus ei- nem Abfragergebnis eine Menge von konkreten Objektinstanzen zu erstellen, die in der Applikation verwendet werden. Oft findet man auch die Begriffe Hydrating oder Object Retrieving. Ich werde des öfteren auch ”Hydrieren“verwenden.Ein Objekt kann eine beliebige Struktur haben, aber ein Abfrageergebnis ist immer ein einfaches Tupel. Wie erhält man aus dieser flachen Struktur eine Objektinstanz?
Aus diesen Teilproblemen ergeben sich Anforderungen für eine Lösung des Impedance Mismatch. Jedes Kernproblem erzeugt eine große Menge von Folgeproblemen, die sich auch überlappen können. Die Komplikationen lassen sich auch als Eigen- schaften der Paradigmen wie in [IBNW[09], S.38 ] beschreiben. Diese sind dann:
- (Structure) Eine Klasse hat eine beliebige Struktur und eine beliebiges Verhalten definiert durch Methoden. Diese Struktur muss abgebildet werden.
- (Instance) Der Objektzustand muss abgebildet werden.
- (Encapsulation) Auf ein Objekt wird über Methoden zugegriffen. Es kapselt sein Verhalten durch diese Methoden und wird somit nicht von außen defi- niert. Daten in der Datenbank haben keine Methoden und sind von überall modifizierbar.
- (Identity) Ein Objekt muss eine eigene Identität haben, die in beiden Modellen eindeutig ist (Objektidentität kurz OID).
- (Processing Model) Der Zugriff auf Objekte innerhalb des objektorientierten Models geschieht über Pfade bestehend aus Objekten. Im relationalen Modell wird auf Objekte (bzw Daten) in Mengen zugegriffen.
- (Ownership) Das objektorientierte Model der Applikation wird vom Entwicklerteam der Software verwaltet. Das Datenbankschema vom Datenbankadministrator. Das Datenbankschema kann nicht nur von einer einzelnen Applikation benutzt werden, sondern von mehreren. Wie werden also Änderungen des Datenbankschemas oder der Applikation behandelt, so dass alle Applikationen auf die Änderungen reagieren können?
Die Liste der Probleme ließe sich noch weiter vergrößern, wenn man sich weitere Details von Datenbanksystemen und objektorientierten Programmiersprachen betrachtet. Eine detailiertere Vertiefung bieten Cook and Ibrahim [CI05].
3. Lösungen
Es gibt verschiedene Lösungen für den IM. Zunächst möchte ich eine vorstellen, die dem eigentlichen IM aus dem Weg geht und einen anderen Ansatz verfolgt: Das Ersetzen der relationalen Datenbank mit einem anderen Datenbankmanagmentsystem. Danach beschäftige ich mich mit dem Object-Relational Mapping, welches die klassische Lösung des IM ist.
3.1 Ersetzen der Datenbank
Zunächst werden Objektdatenbankmanagementsysteme (OODBMS bzw ODBMS) betrachtet.
Definition 2 (ODBMS):
An object-oriented database system must satisfy two criteria: it should be a DBMS, and it should be an object-oriented system, i.e., to the extent possible, it should be consistent with the current crop of object-oriented programming languages. The first criterion translates into five features: persistence, secondary storage management, concurrency, recovery and an ad hoc query facility. The second one translates in- to eight features: complex objects, object identity, encapsulation, types or classes, inheritance, overriding combined with late binding, extensibility and computational completeness. [ABD + 89]
Der IM wird überbrückt, indem das RDMBS mit einem System ersetzt wird, welches den Prinzipien des objektorientierten Paradigma folgt. Das bedeutet, dass viele Aus- prägungen der Probleme die vorher genannt wurden auf natürliche Weise nicht mehr gelöst werden müssen. Gerade die Eigenschaften des zweiten Kriteriums wie: object identity, encapsulation und inheritance lösen die schwierigsten Probleme die in [IBNW09, S. 38] beschrieben werden. Das Objekt welches persistent gemacht werden soll, muss nicht mehr in atomare, in einer Datenbank speicherbare Datentypen zerlegt werden und beim Laden wieder zusammengesetzt werden. Gerade für kom- plexe Objekte mit vielen Abhängigkeiten untereinander ist dies ein enormer Vorteil. Der Grund warum dies mit ODBMSs so gut funktioniert ist, dass diese Systeme nicht mehr Relationen als Basis benutzen, sondern man sich veranschaulicht vorstellen kann, dass das Objekt ”sowieesist“inderDatenbankgespeichertwird.
Man könnte also annehmen, dass ODBMSs die sinnvollste, einfachste und ökonomischsten Lösungen für den IM sind. De facto ist es aber so, dass ODBMSs sich noch nicht so durchsetzen konnten, wie man anfänglich erwartet hatte. Wenn ein ODBMSs keine Relationen mehr benutzt, dann ist auch klar, dass man die Vorteile, die ein relationales DMBS mit sich bringt, verliert. In [Lea00] werden weitere folgende Gründe genannt, warum ODBMSs nicht so erfolgreich wurden:
- RDBMSs wurden mit objektorientierten Features bestückt und entwickelten sich so weiter. Dadurch gewannen die bereits in der Indstrie etablierten Datenbanksysteme wieder ihre verlorene Popularität gegenüber ODBMSs zurück.
- In ODBMSs ist es performanter komplexe Mengen von Datenobjekten zu speichern, ODBMSs können den Speicher des Clients direkt als Cache verwalten und der Optimierer des Systems wählt die beste Form für das physikalische Design der Datenbank. Diesen Vorteil reduzierten die RDBMSs indem sie ihre Prozesse Daten aus Tabellen abzufragen, weiter optimierten.
- Zwar ist es vorteilhafter eine komplexes, objektorientiertes Schema einer Ap- plikation mit einem ODBMSs persistent zu machen, doch die Bereiche in denen diese Schemas existieren und ODBMSs ihre Anwendungen finden sind nur kleine Nischenmärkte.
- RDBMSs stützen sich auf den lang etablierten Standard SQL. Die Object Da- tabase Management Group entwickelt zwar Standards für ODBMSs und ORM- Produkte, aber der Standard ist nicht weit verbreitet und zu wenige Herstel- ler unterstützen den Standard, um ihn wirklich für die Industrie relevant zu machen. So konnte sich zum Beispiel der OQL-Standard (Object Query Lan- guage) nie richtig durchsetzen.
- Das Fehlen von SQL wurde zu spät von Herstellern als gravierender Nachteil der ODBMSs realisiert.
- Firmen die bereits ein bestehendes RDMBS, bzw die Abwandlung davon als ORDMBS als bevorzuge Datenbanklösung benutzen, wechseln gerechtfertigter Weise zu keiner neuen Lösung. Nur wenn die neuen Systeme einen unwider- stehlich lukrativen, wirtschaftlichen Vorteil gegenüber den alten bieten würden - was ODBMSs nicht können - gäbe es eine Chance, RDBMSs in ihrer Vormachts- stellung in Gefahr zu bringen. Hinzu kommt, dass bestehende Hersteller von RDBMSs mehr Ressourcen und Mittel z. B. fürs Marketing zur Verfügung haben, weil sie bereits eine größere Basis an verkauften Produkten besitzen.
Die genannte Erweiterung der relationalen Datenbankmanagmentsysteme mit objek- torientierten Features bezeichnet man als Objektrelationales Datenbankmanage- mentsystem kurz ORDMBS. Die Definition eines ORDMBS ist schwer zu formulieren. Um genauer zu sein, gibt es überhaupt keinen Standard oder eine von allen anerkannte wissenschaftliche Arbeit, die einem erlauben würde ein ORDMBS zu charakterisieren. Der Übergang von einem RDMBS zu einem ORDMBS kann also mehr oder weniger flie- ßend sein. Somit können relationale Systeme schon als objektrelationale Systeme bezeichnet werden, wenn sie nur ein objektorientiertes Feature umsetzen. In [Dev01] wird versucht die Charakteristika eines ORDMBS so zu beschreiben:
3.1. ERSETZEN DER DATENBANK 11
- Erweiterung von Basisdatentypen
- Unterstützung von komplexen Objekten
- Vererbung
Es ist also nicht leicht ein konkretes DMBS als ORDMBS oder RDMBS zu bezeichnen [SM95]. Um trotzdem ein Gefühl entwickeln zu können, wie weit sich die Hersteller um die Entwicklung ihres RDMBS zu einem ORDMBS kümmern, zeigt Abbildung 3.1 ei- ne mögliche Klassifizierung einiger bekannter Datenbankmanagmentsysteme (Quelle für die Auswahl der Datenbanksysteme: Evans Data Corp: Users’ Choice Database Servers (2008)). Desto weiter ein Datenbanksystem auf dem Strahl weiter links steht, desto eher ist es meiner Einschätzung nach als reines ODBMSs charakterisierbar. De- sto weiter ein DMBS rechts auf dem Strahl erscheint, desto weniger objektrelationale Features unterstützt es. Es ist zu beachten, dass die meisten RDMBS sich in Rich- tung einer Position weiter links auf dem Strahl entwickeln, während sich nur wenige ODBMSs in die relationale Richtung bewegen. Anbei noch ein paar Anmerkungen zur
Abbildung in dieser Leseprobe nicht enthalten
Abbildung 3.1: Klassifizierung beliebter Datenbankmanagmentsysteme Platzierung der verschiedenen DMBS auf dem Strahl:
Oracle11 Mit Oracle Objects können benutzerdefinierte Objekte (Objekttypen) in der Datenbank gespeichert werden. Diese Objekte werden aus Basisdatentypen oder anderen Objekten zusammengesetzt. Zusätzlich zu der Definition des Objekt- typen können auch Methoden definiert werden. Danach ist es möglich diesen Typen in einer Tabelle zu verwenden. Dabei kann dieser Objekttyp mit verschiedenen ande- ren relationalen Informationen vermischt werden. Wird der Objekttyp als Typ einer Tabelle benutzt (Object Tables) kann sowohl auf das ganze Objekt (und dessen definierten Methoden), als auch auf jede Spalte einzeln zugegriffen werden. D. h. es ist möglich, das Objekt als Objekt oder auch als eine einzelne Zeile mit Spalten anzusprechen, um relationale Features nutzen zu können. Es sind Referenzen auf Objekte in Tabellen möglich, es gibt Collections und Typen können voneinander ab- geleitet werden. Extensions für die Spracherweiterung gibt es für C++, .NET und Java und natürlich über das eigene Call Level Interface; Zusätzlich gibt es eine SQL Spracherweiterung (inklusive DDL Statements) [Ora08].
Diese Sammlung von Eigenschaften und Fähigkeiten macht Oracle für mich zu dem DBMS welches man am ehesten als ORDMBS bezeichnen kann.
DB2 In DB2 ist es ebenfalls möglich benutzerdefinierte Typen zu erstellen. Die- se distinct Types können von Basisdatentypen abgeleitet werden und sind so- mit auch gut in den Database-Manager integriert. Zusätzlich können diesen Typen auch benutzerdefinierte Funktionen zugewiesen werden. Es wird ein Grundstock von Multimedia Datentypen mitgeliefert: Audio, Bilder, Video und Text. Von Funktio- nen können Tabelle statt Skalare zurückgegeben werden. Somit ist es möglich, jede Datenquelle als Tabelle darzustellen und so die gewohnten relationalen Features zu benutzen. Mit dem structured Type ist auch Vererbung möglich, denn structured Types können von anderen Typen abgeleitet werden. Dadurch können dann Tabellen von einem structured Type ebenso wie in Oracle als Objekttabellen erstellt wer- den und diese wiederum von anderen Tabellen deren Spalten und Methoden erben. Auch hier ist es möglich die Objekte in der Tabelle als separate Spalten anzuspre- chen. Zusätzlich werden Path-Expressions für die Abfrage von Beziehungen von Objekten genutzt und Object Views können erstellt werden. [IBM01].
PostgreSQL PostgreSQL tendiert in die ODBMSs-Richtung, da es auch die Vererbung in Tabellen unterstützt. Bei einem CREATE TABLE kann die Option INHERITS mit angegeben werden. Die Attribute der Elterntabelle werden dann auf die Kindtabellen übertragen. Zusätzlich versucht PostgreSQL den SQL3 Standard (in Entwicklung) umzusetzen. Deshalb ermöglicht es jetzt schon z. B. in Funktionen Tabellen zurückzugeben, oder die Verwaltung von XML als Datentypen. Es erweitert auch einige seiner Basisdatentypen zu Multimediatypen.
Microsoft SQL Server Features die man bei IBM und Oracle findet, findet man weniger in den Feature-Listen von Microsoft SQL Server ([Mic09]). Ich denke, das liegt daran, dass Microsoft andere Wege gefunden hat mit dem IM umzugehen [Mei06]. So findet sich z. B. LINQ als objektrelationales Feature wieder. Trotzdem liefert auch Microsoft eine Möglichkeit für Multimedia Datentypen (Large UserDefined Types: UDTs). Zu diesen Typen werden auch neue Definitionen von Aggregatfunktionen angeboten: Large User-Defined Aggregates: UDAs. Mit ORDPATH können Hierarische Daten als Baum abgespeichert werden.
Caché von Intersystems (nicht abgebildet) Caché von Intersystems [Int10] bezeichnet sich selbst als postrelationales Datenbanksystem. In der Abbildung taucht der Name nicht auf, da man es an jedem Punkt des Strahls zeichnen könnte. Caché bietet sowohl eine relationale als auch eine objektorientierte Datenbankansicht. Die Datenbasis ist weder relational noch objektorientiert, sondern hierarchisch.
3.2 Object-relational Mapper
Zwar lösen ODBMSs die Schwierigkeiten die der IM mit sich bringt, doch je nachdem welches ORDMBS oder RDMBS benutzt wird, gibt es noch genug Probleme die auf Softwareebene gelöst werden müssen. Es lässt sich nur schwer vorraussagen, wann sich Datenbanksysteme so verändern, dass es in der praktischen Anwendung überhaupt keine reinen relationalen Datenbanken mehr gibt. Deshalb gibt es genug Szenarien in denen der IM, oder Teile des IM nach wie vor eine große Rolle spielen und eine universellere Methode zur Lösung gefunden werden muss.
Wenn man die zu lösenden Schwierigkeiten betrachtet, fällt auf, dass die meisten dadurch entstehen, dass ein Element aus dem objektorientierten Paradigma im relationalen Schema nicht abgebildet werden kann. Es muss also ein Element (oder eine Kombination aus mehreren) des relationalen Models gefunden werden, das dieses Element des objektorientierten Models modellieren kann. Die Abbildungen zwischen diesen Elementen - auch Mapping genannt - können in verschiedenen Weisen realisiert werden. Eine Lösung des IM, die eine Menge von Mappings definiert und relationale Elemente in objektorientierte Elemente oder vice versa überführt, nennt man einen Object Relational Mapper oder kurz: ORM.
Im folgenden Kapitel konzentriere ich mich auf Object-relational Mappings. Ich stelle verschiedene schon erforschte Mappings für bestimmte Kernprobleme vor und erkläre die Probleme, die dabei entstehen.
Im Kapitel darauf betrachte ich ein paar Implementierungen dieser Strategien und zeige Vor- und Nachteile bei der Lösung dieser Probleme.
4. Object-relational Mapping
Die folgenden Strategien haben sich für einen ORM bewährt und lassen sich auch größtenteils in der Literatur wiederfinden. Höchstwahrscheinlich existieren in der Praxis noch viel mehr implementierte Lösungen für diese Problematik. Dadurch, dass viele Hersteller eine eigene, unabhängige ORM-Lösungen entwickeln mussten, weil es lange Zeit keine benutzbare, öffentliche Library oderÄhnliches gab, gibt es eine Vielzahl von unterschiedlichen Ansätzen. Es ist schwer sich für einen richtigen Lösungsansatz zu entscheiden, da sich von Fall zu Fall die Anforderungen stark un- terscheiden. Die möglichen Schwerpunkte durch die sich die Lösungen unterscheiden sowie Vor- und Nachteile sollen in späteren Kapiteln weiter untersucht werden. Des- halb werden hier zunächst die meist genutzen, in der Literatur am meisten genannten und in der Praxis etabliertesten Strategien vorgestellt.
4.1 Speichern des Zustandes eines Objektes
Die Konvention, die in den meisten Mappings getroffen wurde ist, dass eine Klasse als eine Relation (Tabelle) im Datenbankschema dargestellt wird. Der Name der Relation ist der Name der Klasse. Die Attribute (Spalten) der Relation sind die Attribute der Instanz der Klasse.
Den Zustand eines Objektes kann man also persistent speichern, indem man die Ausprägungen aller Attribute des Objektes in die Relation einfügt. Ein Mapping (welches auch sehr simpel sein kann) wandelt dabei den Typen des Objektattributes in den Typen der Tabellenspalte um. Eine Instanz eines Objektes wird dann durch ein Tupel in der Klassenrelation repräsentiert.
Dadurch, dass ein Tupel in einer Relation eindeutig unterscheidbar sein muss, kann es sein, dass weitere Eigenschaften zur Klasse hinzugefügt werden müssen. Diese nennt man auch Shadow information. Die Shadow Information ist häufig ein künstlicher numerischer Schlüssel, der sich für jedes neu eingefügte Tupel der Relation automa- tisch um 1 erhöht. Somit ist gewährleistet, dass jede Instanz der Klasse - auch wenn ihre Attribute gleich sind - eindeutig unterscheidbar ist. Dieses Prinzip wird Ein- deutigkeit der Objektidentität genannt. Ich schreibe OID für Object Identifier und meine damit jene Shadow information die ein Objekt in der Applikation und in der Datenbank eindeutig identifiziert. Meist haben Objektinstanzen in objektori-entierten Programmiersprachen eine eigene eindeutige Identität. Diese ist nicht als Shadow information verwendbar, da beim Laden eines Objektes aus der Datenbank eine neue Instanz der Klasse mit den Werten aus der Datenbank befüllt wird. Dabei besitzt dann das eben geladene Objekt A in der Applikation eine andere Identität als ein weiteres Objekt B, welches das selbe Tupel in der Datenbank repräsentieren kann. Dies ist in fast allen Mappern unerwünscht, da man nun nicht mehrÄnderun- gen in Objekt A auf Änderungen in Objekt B beziehen kann und man unerweigerlich Konflikte beim Speichern von Objekt A und Objekt B erhält. Ich gehe also - wenn nicht anders genannt - davon aus, dass in der Applikation zu jedem Zeitpunkt für jedes Tupel einer Klassenrelation nur eine Objektinstanz existiert.
4.2 Speichern von Klassenhierarchien
Die Modellierung einer Klassenhierarchie im relationalen Schema ist eines der komplexen Probleme des IM. Trotzdem wurde mit der Zeit mehr als nur eine Variante gefunden. Es gibt mehrere Mappings für die Darstellung der Vererbung:
4.2.1 Horizontales Mapping
Jede Klasse der Hierarchie besitzt eine eigene Tabelle. In jeder Tabelle werden alle Attribute der eigenen Klasse A und die Attribute der Klassen, von denen Klasse A geerbt hat, als Spalten eingetragen. Die Attribute der Elternklassen werden also auf jede Kindklasse dupliziert.
Die Beziehung zwischen der Relation der Kindklasse und der Relation der Eltern- klasse wird meistens gar nicht abgebildet. Das heißt die Information, dass Klasse A Kind von Klasse B ist, ist nur im objektorientierten Model bekannt. Das Map- ping ist also mit dieser Darstellung der Vererbung nicht bidirektional (Es gibt keine Möglichkeit ein ausschließlich relationales Model in ein objektorientiertes Model zu transformieren, wenn dieses Mapping benutzt wird). Um eine Klasse aus der Hierar- chie zu laden, muss genau eine Tabelle abgefragt werden. Jedoch gibt es Probleme, wenn die Struktur einer Klasse weit unten in der Hierarchie geändert wird. Wird z. B. ein Attribut umbenannt, muss die Änderung in allen Kindklassen ebenfalls erfolgen. Dieses Ändern ist fehleranfällig und aufwendig.
4.2.2 Vertikales Mapping
Anmerkung (Tiefe einer Klassenhierarchie):
Ich bezeichne die Tiefe der Klassenhierarchie als die Anzahl der Eltern und rekursiv dessen Eltern von einer Klasse. Wird also Klasse A von Klasse B abgeleitet und Klasse C von Klasse B dann ist die Tiefe der Hierarchie von A,B und C = 3.
Jede Klasse der Hierarchie besitzt eine eigene Tabelle, jedoch werden die Attribute der Elternklassen nicht in den Kindklassen dupliziert. Jede Relation der Kindklasse besitzt einen Fremdschlüssel auf den Schlüssel der Relation der Elternklasse (mehre- re bei Mehrfachvererbung). Ein Objekt der Kindklasse K abgeleitet von Elternklasse E kann also nur aus der Datenbank geladen werden, wenn das Tupel aus der Relati- on K und das dazu passende Tupel der Relation E geladen wird. Die Eltern Klasse kann jedoch wiederum von einer weiteren Klasse abgeleitet sein. Je nachdem wie tief die Klassenhierarchie ist, ist der gespeicherte Zustand eines Objektes auf mehr als eine Tabelle verteilt. Für eine sehr tiefe Klassenhierarchie müssen also für das Laden eines Objektes sehr viele Tabellen abgefragt werden. Das ÄndernderStruktureinerElternklasseistkeinProblem,daeskeineDuplikateder Attribute gibt. Jede Klasse kapselt seine Attribute in seiner Relation.
4.2.3 Filter-Mapping
Um die komplette Hierarchie abzubilden wird nur eine Tabelle benutzt. Alle In- stanzen der Klasse werden als Tupel in dieser Tabelle repräsentiert. Alle Attribute der kompletten Hierarchie befinden sich nur in dieser Tabelle. Da beim Einfügen eines Tupels von Klasse A nicht alle Spalten von Klasse B benötigt werden, muss es erlaubt sein dass ein Tupel der Klassentabelle leere Attribute der Klasse B bzw. A besitzt. Da dadurch ein Tupel eingefügt werden kann, dessen Attribute alle leer sind (außer der OID), muss die Zugehörigkeit eines Tupels zu der Klasse in der Hierarchie als Shadow information gespeichert werden. Meist geschieht dies durch eine weitere Spalte der Tabelle mit dem Namen der Klasse zu der das Tupel gehört. Diese Spalte wird deshalb auch Filter (bei Hibernate Discriminator) genannt und gibt der Strategie den Namen. Dadurch dass jedes Feld (bei disjunkten Klassenat- tributen) leer sein darf, ist es nicht mehr möglich durch das relationale Schema die Integrität der Daten zu gewährleisten. Auch ist es möglich Attribute in der Daten- bank zu speichern, die nicht zur Klasse des Tupels gehören. Die Applikation muss alle Abfragen mit dem Kriterium für den Filter erweitern, somit muss oft ein Index über den Discriminator gelegt werden, damit diese Abfragen performant bleiben.
4.2.4 Generelles Mapping
Anmerkung (Namen von Relationen):
Namen von Relationen (Tabellen) eines Schemas werde ich rekursiv markieren.
Beim generellen Mapping erhält keine Klasse der Hierarchie eine eigene Relation. Die komplette Struktur einer Klasse (Name der Klasse, alle Attribute, der Name der El- ternklasse(n)) wird in einem generellen Schema dargestellt. Eine Relation dieses Schemas speichert z. B. die Namen der Klassen und die Referenz auf eine Eltern- klasse. Eine andere Relation modelliert die Namen und Typen der Attribute und die Zugehörigkeit zu einer Klasse.
Die Abbildung 4.1 zeigt ein Beispiel für ein solches Schema. In Klassen sind die Na- men aller Klassen der objektorientierten Applikation gelistet. In Beziehungen sind Vererbungen, Assoziationen und Kompositionen von Klassen gespeichert. Jede Klas- se besitzt n Attribute, deshalb gibt es eine Beziehung zu Attribute, die wiederum Objekte mit Werten verbindet. In Werte stehen alle Werte aller Objekte in der Applikation. Jede Instanz in der Tabelle Instanzen hat eine eigene OID (Shadow
Abbildung in dieser Leseprobe nicht enthalten
Abbildung 4.1: Beispiel einer Struktur für generelles Mapping
Information). Eine Instanz kann von einer anderen Instanz abgeleitet sein, oder mit einer anderen Instanz eine Beziehung haben (z. B. Vererbung).
4.3 Speichern von Beziehungen zwischen Objekten
Die Beziehungen zwischen Objekten können so wie Beziehungen zwischen Relationen im klassischen relationalen Datenbankschema gelöst werden. Je nach Multiplizität der Beziehung (1:1 1:n oder n:m) werden Fremdschlüssel der Relation B in der Re- lation A angelegt, wenn A und B eine Beziehung eingehen. Bei einer Multiplizität von n:m muss eine weitere Beziehungsrelation eingefügt werden, die von Relation A und Relation B jeweils einen Fremdschlüssel speichert (Zwischentabelle).
Dieser klassische Ansatz reicht für die meisten Applikationen aus und der Groß- teil der ORM-Software benutzt diesen auch. Dennoch wird z. B. in [LG07] noch eine andere Möglichkeit beschrieben: Für jede Beziehung zwischen Objekten wird eine Beziehungsrelation eingefügt (Zwischentabelle wie bei n:m im klassischen Fall). Je nach Multiplizität werden in der Tabelle beide Fremdschlüssel einzeln (1:1), nur eine Seite (1:n) oder beide kombiniert mit Unique-Constraints belegt. Der Grund für die zusätzlichen Tabellen im Schema ist, dass z. B. bei einer Beziehung zwischen Re- lation A und Relation B mit Multiplizität 1:n der Fremdschlüssel beim klassischen Schema in Relation B eingefügt werden muss. Betrachtet man die Beziehung aus der Sicht von Relation A, ist nicht mehr zu erkennen, dass eine Beziehung zu Rela- tion B überhaupt besteht. Es ist auch nur Relation B für die Pflege der Beziehung verantwortlich. Dies entspricht nicht dem Prinzip der Kapselung von Informationen im Objekt der Relation A. Außerdem kann die Liste der Fremdschlüssel in einer Relation, die an besonders vielen Beziehungen beteiligt ist, schnell sehr lang werden. Durch eine empirische Untersuchung kommt [LG07] zu dem Schluss, dass die Lösung mit vielen Beziehungsrelationen ungefähr gleich schnell im Vergleich zu der klassischen ist.
4.4 Laden und Marshalling von Objekten
Das Laden von Objekten wird in den meisten ORM-Lösungen zu unausführlich be- handelt. Mit einer naiven Idee lässt sich das Problem meist zufriedenstellend lösen. Es ist z. B. sehr einfach ein einzelnes Objekt mit einer gegebenen Objekt-ID aus der Datenbank zu laden und die passende Instanz in der objektorientierten Applikation zu erstellen:
- Die Datenbank muss nach dem Tupel für die Objekt-ID angefragt werden.
- Die Werte des Tupels müssen in die Attribute des Objektes überführt werden (Marshallling).
Kritisch wird es jedoch, wenn man eine größere Menge von Objekten gleichzeitig laden will. Der naive Ansatz - der zwar das Prinzip der Kapselung von Eigenschaften und Methoden einhält - scheitert hier an der Leistungsfähigkeit. Für eine Menge von n Objekten werden n simple Abfragen an die Datenbank getätigt, anstatt eine große Abfrage zu tätigen und über das Ergebnis zu iterieren. Hier kommt die Eigenart des IM besonders gut hervor: Jedes Objekt möchte seine eigenen Abfragen an die Datenbank verwalten (Kapselung), aber bei relationalen Abfragen werden Objekte in großen Mengen zurückgegeben (Performanz).
Ein weiterer wichtiger Aspekt ist die Entscheidung wie die Anfragen an das System gestellt werden. Am häufigsten werden diese drei Möglichkeiten benutzt:
1. Alle Abfragen werden in reinem SQL geschrieben. Der Entwickler muss dafür Sorge tragen, dass alle Informationen für jedes Objekt vollständig vorhanden sind. (reines SQL).
2. Die Abfragen werden in einem Abfrageobjekt mit einer objektorientierten API gekapselt. Diese Struktur wird später in reines SQL umgewandelt (objektori- entierte SQL API).
3. Es wird eine eigene Abfragesprache definiert, die große Ähnlichkeiten mit SQL hat. Die Sprache vereinfacht den Umgang mit Objekten und wird ebenfalls später zu reinem SQL transformiert. (eigene Abfragesprache).
Es stellt sich die Frage, ob es besser ist dem Anwender eine bekannte Anfragesprache als API zur Verfügung zu stellen (wie z. B. SQL) oder aber alle Details für das Laden von Objekten in abstrakten Methoden der objektorientierten Applikation zu kapseln (2.). Die Meinungen, ob die Anfragesprache als SQL oder als abstrakte Query Spra- che umgesetzt werden sollte, gehen weit auseinander. Somit wird in [Amb98, S. 7] die Benutzung von SQL als Verletzung gegen das Prinzip der Kapselung angesehen. Wenn in der Applikation SQL direkt benutzt wird, bindet man die Applikation dau- erhaft an das Datenbankschema. Man verliert dadurch die Möglichkeit das Design des Schemas dem Mapper zu überlassen. Ebenso wird die Schnittstelle zur Daten- bank nicht mehr abstrahiert, d. h. die Applikation ist von einem bestimmten DMBS abhängig. Wird das DMBS gewechselt, muss auch die Applikation aufwendig geändert werden. Das Design der Abfragesprache wird als besonders schwierig betrachtet. Die Sprache soll in der Domain des Objekt-Models konstruiert werden, aber muss mächtig genug sein, um alle komplexen SQL-Abfragen, die sonst geschrieben werden würden, behandeln zu können ([Rus08, S. 24]). Die Entscheidung SQL, eine objekt- orientierte SQL API oder eine eigene Abfragesprache zu entwerfen, ist deshalb sehr komplex und viele Lösungen kann man an diesem Merkmal unterscheiden.
4.5 Vertiefung: Object Loading
Bevor ich im nächsten Kapitel auf verschiedene konkrete Implementierungen einge- hen werde, möchte ich zunächst ein Thema noch weiter vertiefen: Das Laden von Objekten scheint nicht oft Inhalt von wissenschaftlichen Arbeiten zu sein, obwohl dort meiner Meinung nach die besten Optimierungen bezüglich Perfor- manz möglich sind.
Beginnt man eine ORM-Lösung naiv zu implementieren könnte man schnell zu folgender Struktur kommen:
- Jede Klasse, die persistent gemacht werden soll leitet eine Basisklasse ab, in der die Persistenzmechanismen als Methoden zur Verfüung stehen (save(), get(), delete())
- Es gibt eine Methode get() mit dem Parameter OID, welche den verknüpften Datensatz aus der Datenbank lädt (Verbindung herstellen, SQL absetzen, Daten auslesen, init() aufrufen)
- Es gibt eine weitere Methode init(), die die Werte aus dem Ergebnis der Abfrage von get() in die Attribute des Objektes überführt. Die Methode init() ordnet also den Attributen eine Zeile des Abfrageergebnisses zu.
Diese Lösung scheint erst einmal sehr simpel und funktional zu sein. Die Klassen, die persistent gemacht werden sollen (Entity-Klassen), müssen die Basisklasse nur ableiten und init() so überschreiben, dass die speziellen Attribute der Klasse mit Daten gefüllt werden, sobald get() aufgerufen wird.
Ich stelle nun ein programmiertes Beispiel vor, bei dem ein Teil des Beispiel-Daten- bankschemas (Anhang A) benutzt wird. In diesem Beispiel sollen alle Verbindungen zwischen Aggregation und Project als konkretes Objekt erstellt werden. In die- sem Objekt (ich nenne es Aggregation2Project) sind dann jeweils zwei Objekte aggregiert: Project und Aggregation. Alle drei Objekte leiten die Basisklasse ab (BaseClass). Die init()-Funktionen von Project und Aggregation übertragen die Daten aus dem Resultset in ihre Attribute. Die init()-Funktion von Aggre- gation2Project tut das auch, aber zusätzlich instanziiert sie ein Project-Objekt und ein Aggregation-Objekt und ruft auf beiden jeweils die get() Methode auf.
[...]
-
-
-
-
Laden Sie Ihre eigenen Arbeiten hoch! Geld verdienen und iPhone X gewinnen. -
-
Laden Sie Ihre eigenen Arbeiten hoch! Geld verdienen und iPhone X gewinnen. -
-
Laden Sie Ihre eigenen Arbeiten hoch! Geld verdienen und iPhone X gewinnen. -
-
Laden Sie Ihre eigenen Arbeiten hoch! Geld verdienen und iPhone X gewinnen. -
-
Laden Sie Ihre eigenen Arbeiten hoch! Geld verdienen und iPhone X gewinnen. -
-
Laden Sie Ihre eigenen Arbeiten hoch! Geld verdienen und iPhone X gewinnen. -
-
Laden Sie Ihre eigenen Arbeiten hoch! Geld verdienen und iPhone X gewinnen. -
-
Laden Sie Ihre eigenen Arbeiten hoch! Geld verdienen und iPhone X gewinnen. -
-
Laden Sie Ihre eigenen Arbeiten hoch! Geld verdienen und iPhone X gewinnen. -
-
Laden Sie Ihre eigenen Arbeiten hoch! Geld verdienen und iPhone X gewinnen. -
-
Laden Sie Ihre eigenen Arbeiten hoch! Geld verdienen und iPhone X gewinnen. -
-
Laden Sie Ihre eigenen Arbeiten hoch! Geld verdienen und iPhone X gewinnen. -
-
Laden Sie Ihre eigenen Arbeiten hoch! Geld verdienen und iPhone X gewinnen. -
-
Laden Sie Ihre eigenen Arbeiten hoch! Geld verdienen und iPhone X gewinnen. -
-
Laden Sie Ihre eigenen Arbeiten hoch! Geld verdienen und iPhone X gewinnen. -
-
Laden Sie Ihre eigenen Arbeiten hoch! Geld verdienen und iPhone X gewinnen. -
-
Laden Sie Ihre eigenen Arbeiten hoch! Geld verdienen und iPhone X gewinnen. -
-
Laden Sie Ihre eigenen Arbeiten hoch! Geld verdienen und iPhone X gewinnen.