Wirtschaftsinformatik: Komplexe Datenbank-Anwendungen
Sie sind hier: Startseite › Wirtschaftsinformatik › Komplexe Datenbank-Anwendungen (Persistenz)
IC / CM, Kurs vom 01.10.2005 - 31.03.2006
- Persistenz (Konzepte)
- Persistenz (Implementierung)
- Objekt-relationale Abbildung
- Umsetzung in Hibernate
Persistenz (Konzepte)
Betrachtungsdimensionen
Persistenz ist die dauerhafte Speicherung von Daten über die Ausführungszeit von Programmen hinaus. In betriebswirtschaftlichen Anwendungen wird dies üblicherweise in Datenbanksystemen realisiert.
Zu unterscheiden sind drei Betrachtungsdimensionen:
- Transparenz (keine, komplett),
- Technologie (Relationales Datenbanksystem, Objektorientiertes Datenbanksystem, XML-Datenbanksystem) und
- Objektorientiertheit (Grade der Objektorientierung in steigender Reihenfolge: Satzmengen, Datentransfer-Objekte, Geschäfts-Objekte).
Relationale Datenbanksysteme, vorherrschende Technologie im betriebswirtschaftlichen Bereich, sind eine ausgereifte Technologie, die auf Tabellenstrukturen basiert. Wegen ihrer einfachen Strukturen ist eine gute Verständlichkeit garantiert. Wenn für die Programmierung eine objektorientierte Programmiersprache eingesetzt wird, stellt sich jedoch die Frage, wie objekt-orientierte Strukturen auf Tabellenstrukturen abgebildet werden.
Objektrelationale Datenbanksysteme sind eine Erweiterung relationaler Datenbanksysteme um objektorientierte Strukturen und verringern die gerade beschriebene Konzeptlücke. Bisher ist jedoch noch kein verstärkter Einsatz dieser Erweiterungen erkennbar.
Objektorientierte Datenbanksysteme basieren auf objektorientierten Strukturen und weisen daher nur eine geringe bis keine Konzeptlücke zu objektorientierten Programmen auf. Sie waren in den 90er Jahren ein vielbeachtetes Thema, haben sich aber nicht durchgesetzt und führen jetzt ein Nischendasein.
XML-Datenbanksysteme sind eine relativ neue Technologie, die auf XML-Strukturen basiert. Sie weisen keine Konzeptlücke auf, wenn die Anwendung ebenfalls auf XML-Strukturen basiert. Die Konzeptlücke zu objektorientierten Programmen ist geringer als bei relationalen Strukturen. Diese Technologie ist besonders für semi-strukturierte bis unstrukturierte Daten (z.B. Texte) geeignet.
Hierarchische- und Netzwerkdatenbanksysteme sind alte Technologien, vorwiegend in Großrechnersystemen. Viele geschäftskritische Daten wurden in solchen Systemen gespeichert, daher sind solche Systeme in der Praxis häufig anzutreffen. Sie weisen eine große Konzeptlücke zu objektorientierten Programmen auf und werden deshalb immer mehr durch relationale Systeme ersetzt.
Objektorientiertheit
Die Verwendung einer objektorientierten Programmiersprache bestimmt nicht die Objektorientiertheit des Programms. Diese hängt von den im Programm tatsächlich verwendeten Strukturen ab.
Im geringsten Grad der Objektorientiertheit liefert die Datenbank Satzmengen an das Programm zurück, die als solche im Programm weiterverwendet werden.
In einem zweiten Schritt liefert die Datenbank Datensätze in Form von Objekten zurück (Datentransferobjekte). Diese Objekte entsprechen aber 1:1 den Datenbankstrukturen, sind nur Behälter für Daten und haben kein Verhalten (Methoden).
Geschäftsobjekte, höchster Grad der Objektorientiertheit, beinhalten fachliche Logik und entsprechendes Verhalten, basieren auf Strukturen wie Vererbung und Assoziation und erfordern komplexe Transformationen der zugrunde liegenden Datenbankstrukturen (z.B. objektrelationale Abbildung). Sie sind in zwei Arten unterteilbar: Domänen-Objekte stellen Konzepte aus der fachlichen Logik dar (z.B. Kunde, Rechnung, Bestellung). Diese Objekte haben einen Zustand und ein Verhalten, d.h. sie haben Attribute auf die zugegriffen werden kann und Methoden, die auf das fachliche Konzept abgestimmte Funktionalität bereitstellen. Dienst-Objekte beschreiben Abläufe, (z.B. Buchung einer Reise inklusive Hotel- und Flugbuchung), bilden oft eine Fassade für Domänen-Objekte und dienen häufig zur Transaktionsbegrenzung, d.h. eine Methode des Dienstobjekts entspricht einer Transaktion.
Transparenz
Transparenz bezeichnet den Umfang an Operationen zur Speicherbehandlung, die im Programm explizit aufgerufen werden müssen. Das betrifft vor allem zwei Aspekte: Das Laden von Daten und Überführen in programminterne Strukturen sowie das Rückschreiben geänderter programminterner Strukturen in die Datenbank. Beispiel: Das Programm arbeitet mit Objekten für Bestellungen, die jeweils eine Liste von Bestellpositionen beinhalten und eine Referenz auf einen Kunden haben. Gespeichert werden die Daten in einer relationalen Datenbank. Auszuführende Funktionen sind das Laden einer Bestellung, das Ändern einer Bestellposition, z.B. die Bestellmenge, oder das Hinzufügen einer Bestellposition.
Keine Transparenz herrscht vor, wenn das Programm per SQL explizit auf die Tabellen für Bestellungen, Kunden und Bestellpositionen zugreifen muss, um ein Bestellobjekt zu erzeugen. Es muss sich die geänderte Bestellposition merken und spätestens vor Transaktionsende per SQL-Befehl (update) die Änderung speichern. Es muss sich die eingefügte Bestellposition merken und spätestens vor Transaktionsende per SQL-Befehl (insert) die neue Position speichern.
Führt das Programm nur einen Befehl zum Laden des Bestellobjekts aus, liegt komplette Transparenz vor. Die notwendigen Daten zum Kunden und zu den Bestellpositionen werden automatisch aus der Datenbank geholt. Das Programm führt nur einen erfolgreichen Transaktionsabschluss durch (commit); alle Änderungen werden automatisch gespeichert.
Transparenz wird durch Persistenz-Rahmenwerke (Persistence Frameworks) realisiert. Hierbei sind verschiedene Grade zwischen keiner und kompletter Transparenz möglich. Persistenz-Rahmenwerke stellen Schnittstellen und Implementierungen für die Speicherung von Daten zur Verfügung. Sie sollten nicht selbst entwickelt werden (komplex, hoher Arbeitsaufwand, viele Fehlermöglichkeiten; bindet Ressourcen, die für die Anwendungsentwicklung fehlen). Sie sollten einfach in der Benutzung sein, da es sonst trotz guter Funktionalität an Akzeptanz fehlt. Wesentlich ist das Programmiermodell. Die Integration erfolgt z.B. in Applikationsserver oder andere Umgebungen. Persistenz sollte jedoch nicht an eine spezielle Umgebung gebunden werden. Bekannte Rahmenwerke (Auswahl) sind Spring JDBC, Hibernate und EJB.
Funktionen eines Persistenz-Rahmenwerks
Das Laden von Objekten erfolgt über die Navigation im Objektgraph (ausgehend von geladenen Objekten über Referenzen erreichbare Objekte laden), über Identifikatoren (identifier), über eine objekt-orientierte Abfragesprache oder über eine native Abfragesprache (z.B. SQL).
Es gibt verschiedene Ladestrategien für assoziierte Objekte (fetching strategies): immediate fetching (sequenzieller Datenbankzugriff), lazy fetching (Nachladen assoziierter Objekte bei Bedarf), eager fetching (zusammengefasstes Laden von Objekt und assoziierten Objekten mit Verbundoperationen) und batch fetching (gleichzeitiges Laden zusätzlicher Objekte, die möglicherweise in Folgeoperationen benötigt werden).
Das Speichern von Objekten erfolgt explizit durch den Aufruf von Speicherfunktionen oder implizit durch die automatische Erkennung von Änderungen (dirty management). Zu unterscheiden sind zwei Speicherstrategien: Bei Speicherstrategien für assoziierte Objekte (persistence by reachability) garantiert die Kaskadierung von Speicherfunktionen auf assoziierte Objekte eine referenzielle Integrität. Die explizite Definition von Kaskadierungen ist anpassbar an den jeweiligen Zweck. Mit Speicherstrategien für bidirektionale Beziehungen werden bidirektionale Beziehungen in Programmen durch zwei unidirektionale Referenzen abgebildet. Es wird definiert, welche unidirektionalen Referenzen zusammen eine bidirektionale Beziehung bilden und es erfolgt eine Klärung, welche Seite für die Speicherung zuständig ist und welche die inverse Seite ist.
Weitere Funktionen eines Persistenz-Rahmenwerkes sind die Zustandsverwaltung von Objekten (mögliche Zustände: transient, persistent, abgekoppelt), die Identifikation von Objekten (automatische Schlüsselerzeugung auf unterschiedlichen Wegen), die Beziehungsverwaltung (Kardinalitäten, uni- versus bidirektionale Beziehungen), eine objektorientierte Abfragesprache (Mächtigkeit), Transaktionen (lokale versus globale Transaktionen) und das Caching (Objekte, die sich selten ändern, Referenzdaten, Clustering).
Ressourcen-Management
Innerhalb von Programmen muss ein Zugriff auf die zugrunde liegenden Ressourcen erfolgen, üblicherweise auf Datenbanksysteme (Ressourcen-Management). Zwei Arten von Objekten spielen eine Rolle: Fabriken zur Erzeugung von Verbindungen und Verbindungen.
Eine Fabrik zur Erzeugung von Verbindungen
repräsentiert den Datenspeicher und erzeugt und verwaltet
Verbindungen (Beispiele: DriverManger
in
JDBC,
SessionFactory
in Hibernate). Fabriken sind
häufig schwergewichtig, d.h.
aufwändig in der Erzeugung.
Eine Verbindung repräsentiert eine
Kommunikationssitzung mit dem Datenspeicher und ist häufig
leichtgewichtig, d.h.
kann ohne großen Aufwand erzeugt und wieder geschlossen werden
(Beispiele: Connection
in
JDBC,
Session
in Hibernate).
Persistenzaspekte
Es gilt verschiedene Persistenzaspekte zu beachten: Einerseits der Umgang mit verschiedenen DB-Systemen (nicht kompatible SQL-Dialekte trotz Standardisierung, unterschiedliches transaktionales Verhalten, Optimierungen), andererseits vorhandene Anwendungen (Legacy Systems: das Datenmodell ist vorhanden und die Anwendung muss sich an dieses anpassen).
Und schlussendlich die Arbeitsaufteilung zwischen DB-System und Programmtechnologie. Das DB-System sollte das machen, was es am besten kann. Das sollte es auf natürlichem Wege tun. Keine Reimplementierung von Datenbankfunktionaltiät in Programmen, z.B. Verbunde, Sperren.
Persistenzstrategien
Eine Persistenzstrategie ist durch eine Auswahl in Bezug auf die Dimensionen Technologie, Objektorientiertheit und Transparenz gegeben. Beispiele: Relationale Datenbank, Satzmengen, keine Transparenz (SQL-basierter klassischer Ansatz, häufig anzutreffen), Relationale Datenbank, Datentransferobjekte, teilweise Transparenz (im Zusammenhang mit EJB 1 und 2 anzutreffen), Relationale Datenbank, Geschäftsobjekte, komplette Transparenz (Ansatz auf Grundlage objekt-relationaler Abbildung, immer häufiger anzutreffen), Objektorientierte Datenbank, Geschäftsobjekte, komplette Transparenz (führt ein Nischendasein), XML-Datenbank, Geschäftsobjekte, keine Transparenz (Ansatz auf Grundlage von XML-Datenbindungen).
Charakteristisch für den SQL-basierten Ansatz sind SQL-Anweisungen zum Lesen und Schreiben von Objekten, eventuell der Einsatz von gespeicherten Prozeduren und dieser Ansatz ist im Allgemeinen nicht transparent. Damit ist kein Werkzeug für die objekt-relationale Abbildung notwendig, es können die Spezifika des jeweiligen SQL-Dialekts genutzt werden (Optimierung) und es ist ein einfacher Zugang zur mengenorientierten Verarbeitung. Allerdings ist das Schreiben und Warten des SQL-Codes arbeitsaufwändig und macht einen erheblichen Teil des Anwendung-Codes aus. Auch der Änderungsaufwand kann erheblich sein (z.B. neue Felder). Einsatzfelder für den SQL-basierten Ansatz sind Anwendungen, die im Wesentlichen eine Sicht auf die vorhandenen Tabellen darstellen, die direkte Manipulation der Tabellendaten, wenn Objektorientierung keine Vorteile bringt, wenn eine mengenorientierte Arbeitweise vorherrschend ist (Berichte, batch updates) oder wenn ein exotisches Datenmodell vorliegt.
Die Objekt-relationale Abbildung ist eine Abbildungsebene zwischen Klassen und Tabellen auf Basis von Metadaten (XML, Annotationen usw.). Sie ist im Allgemeinen transparent, wobei der Grad der Komplettheit variieren kann. Vorteilhaft ist die erhebliche Reduktion des Codes, die Steigerung der Produktivität und die einfachere Wartung. Es fördert ein objekt-orientiertes Denken und die Generierung Datenbank-spezifischen Codes vereinfacht die Migration auf ein anderes DB-System erheblich. Allerdings gibt es keine volle Kontrolle mehr über den Speicherungsprozess, Anfrageoptimierungen sind nicht mehr so einfach möglich und es ist nicht so gut für mengenorientierte Operationen geeignet. Einsatzfelder der Objekt-relationalen Abbildung sind Geschäftsobjekte mit komplexer fachlicher Logik, die Konzentration auf einzelne Objekte (Load-, Edit-, Store-Zyklus), wenn das Datenmodell zur objekt-orientierten Sichtweise passt oder das Caching von Objekten nutzbringend eingesetzt werden kann.
Persistenz (Implementierung)
Strukturierung der Programme
Innerhalb eines Programms wird Code für die fachliche Logik (Geschäftslogik) und zur Umsetzung der Persistenz benötigt. Es stellt sich die Frage nach Aufteilung dieser Code-Arten, d.h. nach der Strukturierung der Programme. Hierzu gibt es verschiedene Alternativen, die jeweils in bestimmten Situationen vorteilhaft sind und nachfolgend beschrieben werden.
Bei der unstrukturierten Mischung von Geschäftslogik und Persistenz-Code wird innerhalb der Implementierung Code zur Umsetzung der Geschäftslogik beliebig mit Code zum Laden und Speichern von Objekten/Daten gemischt. Typisches Beispiel sind Web-Anwendungen auf Grundlage von Skriptsprachen (PHP, MySQL). Sie sind schlecht wartbar, da Änderungen auf Ebene der Datenbank an beliebigen Stellen im Programm nachgeführt werden müssen. Das Programm ist schlecht verständlich, da die Geschäftslogik nur schwer hinter den Details für die Speicherung der Daten erkannt werden kann. Und es ist schlecht testbar, da Geschäftslogik und Persistenz nicht getrennt getestet werden können.
Table Data Gateway
Table Data Gateway (nach Fowler) ist ein Objekt, das als Kapsel für eine gesamte Tabelle dient. Es beinhaltet sämtlichen SQL-Code zum Zugriff auf alle Zeilen der Tabelle und bietet nach außen eine Schnittstelle an, mit der Daten gelesen und geändert werden können. Lesefunktionen liefern Satzmengen an den Aufrufer zurück. Dieses Objekt liefert eine tabellenorientierte Sicht auf die Daten, d.h. keine echte objekt-orientierte Sicht, aber immerhin eine Code-Verbesserung.
Beispiel: PersonTableGateway
mit find(id): RecordSet
,
findWithLastName(String): RecordSet
,
update(id, lastName, firstName, numberOfDependents)
,
insert(id, lastName, firstName, numberOfDependents)
und delete(id)
.
Row Data Gateway
Row Data Gateway
(nach Fowler) ist ein Objekt, das als Kapsel für eine Zeile
dient und sämtlichen SQL-Code zum Zugriff
auf die Zeile beinhaltet. Zum Auffinden von Daten-Transfer-Objekten
(DTOs)
wird ein weiteres Objekt benötigt. Diese Struktur ist nicht
sehr transparent, da z.B.
update
explizit erfolgen muss. Es liefert jedoch eine
stärker objekt-orientierte Sicht auf die
Daten, da ein Gateway-Objekt
in einem gewissen Maß ein Domänen-Objekt
repräsentiert.
Beispiel: PersonFinder
mit
find(id): PersonGateway
und
findWithLastName(String): List<PersonGateway>
zeigt auf PersonGateway
mit den Attributen
lastName
, firstName
sowie
numberOfDependents
und den Methoden insert
,
update
und delete
.
Active Record
Active Record (nach Fowler) ist wie das Row Data Gateway, enthält allerdings weitere Geschäftslogik und verzeichnet somit eine weitere Steigerung der Objekt-Orientiertheit.
Beispiel: PersonFinder
mit find(id): Person
und findWithLastName(String): List<Person>
zeigt
auf Person
mit den Attributen
lastName
, firstName
sowie
numberOfDependents
, den Methoden insert
,
update
und delete
sowie den
weiteren Methoden getExemtion()
,
isFlaggedForAudit()
und getTaxableEarnings()
.
Data Mapper
Data Mapper (nach Fowler) ist ein Objekt, das eine Abbildung zwischen Datenbankstrukturen und Programmstrukturen vornimmt (objekt-relationale Abbildung). So sind komplexe Transformationen möglich, die Ergebnis-Klassen enthalten keinerlei Persistenz-Code mehr und echte Domänen-Objekte oder Daten-Transfer-Objekte, die nur Datenbehälter sind, können erzeugt werden.
Beispiel: PersonMapper
mit den Methoden insert
,
update
, delete
und select
zeigt einerseits auf die Datenbank und andererseits auf
Person
mit den Attributen lastName
,
firstName
, numberOfDependents
und den
Methoden getExemtion()
, isFlaggedForAudit()
und getTaxableEarnings()
.
Datenzugriffsmuster
Das Datenzugriffsmuster (DAO-Muster) ist ein allgemeines Muster zu Abstraktion von Speicherungsfunktionen durch Datenzugriffsobjekte (DAOs).
Die Realisierung ist beispielsweise möglich durch Table Data Gateway (das statt Satzmengen Listen von DTOs zurückliefert) oder Data Mapper (der Domänen-Objekte oder DTOs zurückliefert).
Die programmtechnische Umsetzung erfolgt durch eine Schnittstelle zur Definition der Funktionalität oder durch Implementierung(en) der Schnittstelle.
Datenzugriffsobjekte
Datenzugriffsobjekte (DAOs) sind unabhängig von Persistenzstrategien, d.h. sie können sowohl transparente als auch nicht-transparente Persistenz anbieten. Sie können im Rahmen von Transaktionen eingesetzt werden ohne diese jedoch selbst zu steuern.
Datenzugriffsobjekte enthalten Methoden zum Anlegen, Laden, Suchen und Löschen von Objekten sowie Methoden zum Speichern von Änderungen im Falle nicht-transparenter Persistenz.
DAOs
transformieren speichertechnik-spezifische Ausnahmen
(z.B. SQLException
)
in speichertechnik-neutrale Ausnahmen. Ebenso können sie
Aggregationsfunktionen bereitstellen.
Der Vorteil bei speichertechnik-neutralen Ausnahmen
ist, dass Nutzer eines DAO nicht mehr mit den
Ausnahmen der jeweiligen Speichertechniken
(z.B. SQLException
)
arbeiten, sondern statt dessen mit inhaltlich aussagekräftigen
Ausnahmen. Beispiele aus der DAO-Unterstützung des
Spring-Rahmenwerks sind DataAccessException
,
DataAccessResourceFailureException
und
UncategorizedDataAccessException
.
DAOs müssen an Transaktionen teilnehmen können, steuern sie aber nicht. DAOs vereinfachen das Testen von Anwendungen, da sämtliche Speicheroperationen hierüber kanalisiert werden. Für Testzwecke kann ein einfaches DAO gebaut werden, das die DAO-Schnittstelle implementiert aber z.B. die Daten nur im Hauptspeicher hält. Auf diese Weise kann das Testen erheblich verkürzt werden.
Objekt-relationale Abbildung
Abbildung von objekt-orientierten Strukturen auf Tabellen
Die Objekt-relationale Abbildung ist eine Abbildung von objekt-orientierten Strukturen auf Tabellen.
Das objekt-orientierte Modell enthält reichere Strukturierungsmechanismen als das relationale Modell. Eine Transformation auf struktureller Ebene ist notwendig. Folgende Konzepte müssen transformiert werden: Klassen, komplexe Datentypen, Assoziationen, Kompositionen und Vererbung.
Transformation von Klassen
Bei der Transformation von Klassen werden Klassen zu Tabellen und Attribute zu Spalten. Die Standardvariante: Eine Klasse wird eine Tabelle. Aber es sind auch andere Verhältnisse denkbar: z.B. eine Klasse auf mehrere Tabellen. Standarddatentypen werden auf korrespondierende Typen in der Datenbank abgebildet.
Bei der Umwandlung von Klassen in Tabellen werden die Typen wie
folgt umgesetzt: long
wird zu bigint
,
string
wird zu varchar
, int
bleibt int
und Identifikationsattribute bekommen
generell den Typ bigint
.
Komplexe Typen, die keine Korrespondenz im Typsystem der
Datenbank haben, müssen gesondert behandelt werden,
z.B. durch programmtechnische
Transformation. Eine Klasse, die beispielsweise Geldbeträge
inklusive Währungsangabe abbildet, wird in der Datenbank durch
zwei Attribute (Wert, Währung) gespeichert. Das Attribut
initialPrice
vom Typ MonetaryAmount
wird
in der Tabelle zu initialPrice_value: decimal
und
initialPreis_currency: varchar
. Die Transformation
erfolgt durch den Programmcode (Transformation von
komplexen Attributen).
Transformation von Assoziationen
Bei der Transformation von Assoziationen sind 1:n-Assoziationen (bei Umwandlung der Klassen in Tabellen landet ein Fremdschlüssel auf der n-Seite) und n:m-Assoziationen (Einführung einer Zwischentabelle, die Fremdschlüssel der beiden Seiten enthält) zu unterscheiden.
Es können unidirektionale Beziehungen (eine Klasse enthält eine Referenz auf die andere Klasse, es kennt also nur einer den anderen) und bidirektionale Beziehungen (beide Klassen enthalten jeweils eine Referenz auf die andere Klasse, sie kennen sich also beide) auftreten.
Transformation von Kompositionen
Bei der Transformation von Kompositionen ist zwischen Entity-Typen und Wert-Typen zu unterscheiden.
Entity-Typen haben eine eigene Datenbankidentität sowie einen Lebenszyklus und existieren unabhängig von anderen Entity-Typen. Eine Klasse Nutzer hat beispielsweise Attribute wie Vorname und Nachname.
Wert-Typen haben keine Datenbankidentität, ihre Daten werden in die Daten der besitzenden Entität eingebettet. Außerdem sind Wert-Typen abhängig von anderen Entity-Typen. Jetzt kommt also eine zweite Klasse Adresse ins Spiel, mit Attributen wie Straße, Postleitzahl und Ort. Diese Klasse ist durch die Anweisungen Haus und Rechnung an erstere gebunden. In der entstehenden Gesamttabelle Nutzer werden die Attribute der zweiten Klasse durch diese doppelte Anbindung an Klasse Nutzer zu Haus_Straße, ... sowie Rechnung_Straße.
Transformation von Vererbungen
Bei der Transformation von Vererbungen gibt es drei Varianten.
Eine Tabelle für die gesamte Klassenhierachie:
Ein Beispiel soll die Funktionsweise verdeutlichen. Klasse
Bankkonto und Klasse Kreditkarte sind von Klasse Gebühren
abgeleitet. Die entstehende Tabelle nimmt einfach alle Attribute
aller Klassen auf. Vorteile: einfache Struktur, keine
Verbundoperationen notwendig, polymorphe Beziehungen und Abfragen
möglich. Nachteile: Datenkonsistenz schwieriger zu
gewährleisten, da Spalten aus den abgeleiteten Klassen keine
not null
Beschränkung haben können.
Eine Tabelle pro Unterklasse: Gleiche Klassen
wie gerade. Jetzt werden jedoch auch drei Tabellen erstellt. Die
Tabellen, die aus den abgeleiteten Klassen entstehen, erhalten
jeweils noch ein Identifikationsattribut. Vorteile: polymorphe
Beziehungen und Abfragen möglich, Datenkonsistenz in Bezug
auf not null
-Spalten bleibt erhalten. Nachteile:
Verbundoperationen notwendig.
Eine Tabelle pro konkreter Unterklasse: Wir
bleiben bei den oben beschriebenen Klassen. Die abgeleiteten
Klassen werden zu Tabellen, werden jeweils um ein
Identifikationsattribut ergänzt und enthalten zusätzlich
jeweils die geerbten Attribute. Vorteile: Datenkonsistenz in Bezug
auf not null
-Spalten bleibt erhalten, keine
Verbundoperationen notwendig. Nachteile: Polymorphe Beziehungen
und Abfragen nicht möglich.
Eine polymorhe Beziehung ist die Beziehung zu einer Klasse, die wiederum Unterklassen hat.
Umsetzung in Hibernate
Hibernate und Persistenz
Hibernate unterstützt die Persistenz für Java-Objekte durch ein einfaches Komponentenmodell (POJOs). Die Transformation von Objektorientiertheit-Konzepten erfolgt durch Klassen und komplexe Datentypen sowie Assoziationen, Kompositionen und Vererbung. Aus Funktionalität-Sicht bietet Hibernate das Laden von Objekten, Ladestrategien für Assoziationen, Dirty-Management, Speichern von Objekten, Speicherstrategien für Assoziationen, Persistence by Reachability, Beziehungsverwaltung, Zustandsverwaltung und Identifikation von Objekten, objektorientierte Abfragesprache, Transaktionen und Caching.
Hibernate bietet Transparenz durch objekt-relationale Abbildung (OR-Mapping). Java-Klassen, Tabellen und OR-Abbildung (Metainformation, z.B. in XML) sind über einen OR-Abbilder verknüpft.
Persistenz-Lebenszyklus für Objekte
Betrachten wir nun den Persistenz-Lebenszyklus für Objekte. Objekte können die Zustände transient (englisch für "vorübergehend"), persistent (englisch für "hartnäckig") und detached (englisch für "abgekoppelt") annehmen.
Der Lebenszyklus beginnt mit der Anweisung get()
,
load()
oder find()
, womit gleich auf ein persistentes Objekt
zugegriffen wird. Wird zuerst jedoch ein neues Objekt erzeugt, ist
es transient. Es kann nun
direkt wieder weggeschmissen werden
(garbage) oder aber weiter
verarbeitet werden. Durch Aktionen wie save()
oder saveOrUpdate
findet ein Zustandswechsel statt,
das Objekt befindet sich dann im Zustand
persistent. Wird ein persistentes
Objekt durch delete()
gelöscht, nimmt es wieder den
transienten Zustand an.
Doch bleiben wir vorerst im persistenten Zustand. Durch Aktionen
wie evict()
, close()
oder clear()
geht das persistente Objekt in den Zustand
detached über. Es wurde
somit aus der Session entfernt, verliert seine Identität
jedoch nicht und kann dadurch natürlich auch wieder persistent
gemacht werden. Es kann also durch Aktionen wie update()
,
saveOrUpdate()
oder lock()
wieder in den
Zustand persistent
überführt werden. Es kann jedoch auch aus dem
detached-Zustand heraus im
garbage-Collector
landen und damit Lebenszyklus beendet werden.
Unidirektionale Beziehungen
Eine unidirektionale 1:n-Beziehung wird in Hibernate
durch das Einfügen des Attributes fetch="..."
auf der "ich-kenne-den-anderen"-Seite (<many-to-one
name="item" class="de.fhtwberlin.biditem1.Item"
fetch="...">
) und auf der
ahnungslosen Seite mit der Angabe des Attributes lazy="..."
markiert (<class name="Item" table="Item"
lazy="...">
).
Resultierend aus den verschiedenen Kombinationsmöglichkeiten
ergeben sich folgende Ladestrategien: Mit
fetch="select"
und
lazy="true"
eine
SQL-Anweisung
für die kennende Klasse, eine
SQL-Anweisung
für die gekannte nur bei Bedarf. Mit fetch="select"
und lazy="false"
eine SQL-Anweisung für
die kennende Klasse, eine SQL-Anweisung für die
gekannte direkt hinterher. Mit fetch="join"
und lazy="true, false"
eine
SQL-Anweisung
für beide Klassen zusammen.
Bidirektionale Beziehungen
Bei einer bidirektionalen 1:n Beziehung muss geregelt werden, welche Seite für die Speicherung der Beziehung zuständig ist. Tatsächlich setzt sich eine bidirektionale Beziehung aus zwei unidirektionalen zusammen, deren Zusammengehörigkeit für das System nicht ersichtlich ist.
Mit inverse="true"
wird für eine Seite
festgelegt, dass diese als Inverse einer anderen betrachtet wird
und damit die andere Seite der Beziehung die Speicherung vornimmt.
Das Attribut cascade
legt fest, wie Einfügungen,
Änderungen und Löschungen kaskadiert werden.
Mögliche Werte sind none, save-update, delete, all,
all-delete-orphan, delete-orphan
.