O/R-Mapper und Alternativen
Seite 3: Relationales Erbe
Vererbung fĂĽr relationale Datenbanken
O/R-Mapper bieten viele Möglichkeiten, die genannten Unterschiede zwischen OO- und Datenbankwelt zu überwinden. So stellen Hibernate & Co. verschiedene Verfahren zur Abbildung von Vererbungen und Assoziationen bereit. Ihr Vorteil liegt darin, dass sie im Idealfall objektorientierten Code und Datenbank trennen. Dies gestattet den Einsatz separater Entwicklerteams: Auf der einen Seite können sich die Domänenentwickler im Wesentlichen mit ihren OO-Modellen beschäftigten, auf der anderen Seite erstellen die Datenbankfachleute das Mapping.
In der Praxis sind jedoch viele der beschriebenen konzeptionellen Unterschiede nur mit viel Geschick, Wissen und Erfahrung zu überbrücken. Wesentlich ist zum Beispiel, wer das Datenbankschema ändern darf. Kann man von einem OO-Modell ausgehend die Mappings definieren und das Datenmodell generieren, lässt sich eine der Stärken von O/R-Mappern nutzen: Greift man nicht in die von ihnen generierten SQL-Statements ein, ist eine beliebige relationale Datenbank verwendbar. Ist der OO-Entwickler jedoch nicht „Herr des Schemas“, etwa weil er mit einem bestehenden Datenmodell arbeiten muss, können korrekte Mappings großen Aufwand erfordern.
Identität: Zwei Interpretationen
Da OO-Modelle und Datenbanken das Konzept „Identität“ unterschiedlich umsetzen, erfordert auch dieser Aspekt Überlegungen. Bei Objekten definiert ihre Adresse im Speicher die Identität. Verschiedene Objekte dürfen denselben Inhalt haben (String == String beziehungsweise String.equals(String)). In einer Datenbank könnte es zwar prinzipiell vorkommen, dass mehrere Zeilen dieselben Werte enthalten, allerdings wären sie dann nicht zu unterscheiden. In der Regel stellen O/R-Mapper die Identität von Objekten in der Datenbank durch einen automatisch vergebenen Primärschlüssel sicher. Diesen Wert verwenden sie meistens auch im Domänenmodell. O/R-Frameworks erzeugen jedoch weitere Instanzen im Kontext des jeweiligen Benutzers sowie im Cache. Arbeiten mehrere Anwender gleichzeitig mit denselben Daten, ist es für den Entwickler nicht immer leicht, die Übersicht über die verschiedenen Objekte zu bewahren, Transaktionen korrekt zu implementieren, und zu entscheiden, wann er welche Daten wo aktualisieren muss.
Von O/R-Mappern erzeugte SQL-Statements bringen nicht nur Vor-, sondern auch Nachteile mit sich: Abfragen erfolgen bei Mappern typischerweise durch „Query by example“, eine API, oder eine eigene Sprache. Bei Hibernate ist dies das auf Objektebene operierende HQL. Wer die Leistungsfähigkeit von Hibernate ausnutzen will, kommt um diese Sprache nicht herum.
Andererseits erfolgt die Optimierung von Datenbankzugriffen in der Regel durch Anpassung von SQL-Statements. Zwar kann man die von Hibernate generierten Befehle durch eigene ĂĽberschreiben, hat damit aber einen seiner Vorteile verloren. Denn statt der Arbeit mit Mappings und HQL steht nun SQL-Kodierung im Vordergrund; unter Verzicht auf die Datenbankabstraktion. FĂĽr Hibernate bedeutet dies, dass man im Endeffekt die Definition der Mappings sowie HQL und SQL gut beherrschen sollte.
Verwendet er Assoziationen, muss sich der Entwickler mit der zweckmäßigen Vorgehensweise beim Laden beschäftigen. Meistens ist es kaum sinnvoll, den gesamten Objektbaum mit dem ersten Objekt rekursiv zu laden und damit alle Referenzen aufzulösen. Daher benutzen O/R-Mapper „lazy loading“ – sie liefern zunächst nur ein Proxy-Objekt und beschaffen von ihm referenzierte Objekte erst bei Bedarf aus der Datenbank. Wird eine Gruppe von Objekten immer gemeinsam benötigt, kann es jedoch effizienter sein, sie in einem Rutsch zu laden – eine weitere Konfigurations- und HQL-Option. Schließlich funktioniert das Laden bei Bedarf nur mit einer aktiven Hibernate-Session. Diese Voraussetzung ist zum Beispiel nicht gegeben, wenn man Abfragen über Data Access Object Patterns auflöst beziehungsweise in Web-Sessions nutzt.
Ein weiterer Nachteil des O/R-Mapping liegt in der angeforderten Datenmenge. SQL erlaubt die Auswahl der zu ladenden Spalten. Diese Möglichkeit bieten O/R-Mapper nicht ohne Weiteres; sie laden immer komplette Objekte, weil OO-Konzepte keine „Teilobjekte“ kennen.