JavaHibernate
Back to current versionRestore this version

Hibernate Java ORM#

Hibernate ist ein ORM-Framework für [Java]]. ORM bedeutet: "Objekt-relationales Mapping" und heisst im Klartext, daß eine Datenstruktur von Objekten, so wie sie in Java üblich ist, in eine relationale Datenbank übertragen wird. Das bedeutet, daß eine Java-Applikation einfach und komfortabel auf eine echte Datenbank wie z.B. MySQL zugreifen kann. Die Daten werden somit persistent gemacht.

Zur Einführung ins Thema empfiehlt sich ein Blick auf die Webseite. Eine sehr tiefgehende Anleitung findet sich auf Java Persistenz mit Hibernate, das Thomas Bayen besitzt und wärmstens empfiehlt.

Wer jetzt neu anfängt, sollte überlegen, als Zugriffs-API direkt JPA zu verwenden. Das ist ein neuerer Java-Standard (seit Java6), der maßgeblich von Hibernate inspiriert wurde und deshalb ausgezeichnet unterstützt wird. Dennoch kann man jederzeit für besondere Anforderungen auch einzelne Hibernate-Spezialbefehle oder -Konfigurationen benutzen. Da JPA von verschiedenen ORM-Produkten zur Verfügung gestellt wird, kann man so Know-How (und ggf. sogar die ganze ORM-Bibliothek) austauschen. Es steht zu erwarten, daß die JPA-API langfristig die ursprüngliche Hibernate-API komplett verdrängen wird.

Tips und Tricks #

Bei der Arbeit mit Hibernate ist mir vor allem aufgefallen, wie komplex das Thema doch in Wirklichkeit ist. Früher dachte ich immer: Was regen die sich in all den SQL-Büchern so auf, die paar Tabellen können doch nicht so kompliziert zu bearbeiten sein. Im praktischen Betrieb mit Hibernate merkt man aber immer wieder, daß man auf Grenzen und "Bugs" stößt, die gar keine Bugs sind, sondern die in der Natur der Daten liegen, die man vorher evtl. nicht genau durchschaut hatte. Ich möchte betonen, daß ich bei aller Komplexität des Themas noch auf kein Problem gestossen bin, das man mit Hibernate nicht angemessen lösen konnte. Da mir einige dieser Aha-Momente schon wieder entfallen sind, möchte ich in Zukunft hier einen Platz haben, um sie festzuhalten.

eingebettete Arrays #

Man kann relativ einfach ein Array aus eingebetteten Komponenten erzeugen.

Wir erinnern uns: Komponenten sind Objekte, die keine eigene Identität besitzen und deshalb keine eigene Entity sind. Gedacht sind diese, damit man z.B. in Kunde eine Adresse einbinden kann, ohne daß diese Aufteilung innerhalb der Java-Klassen auch eine eigene Tabelle in der relationalen Datenbank erzeugt. Komponenten werden mit @Embeddable annotiert.

Nun kann man aber auch ein Array von Komponenten erzeugen. Dabei annotiert man ein Collection-Feld mit @CollectionOfElements. Jetzt kann man z.B. seinem Kunden mehrere Telefonnummern zuordnen. Intern geht das natürlich nicht ohne eigene Tabelle, das wird aber weitgehend vor dem Benutzer versteckt.

So weit - so gut. Für den normalen Benutzer reicht es bis hierhin. Ich bin nun aber auf ein besonderes Phänomen gestossen: Ändert man eines der Komponentenobjekte im Array, so werden als Antwort darauf beim nächsten flush() des Persistenzkontextes (also zumeist beim nächsten commit()) alle(!) Telefonnummern dieses Kunden aus der entsprechenden Tabelle mit DELETE gelöscht. Danach werden alle Telefonnummern wieder mit INSERT neu erzeugt. Bis dahin ist das erstmal umständlich und witzig, aber der Spaß hörte auf, als ich merkte, daß ein Zeiger auf eine Komponente, den ich zwischengespeichert hatte, nun nicht mehr gültig war. Das vorher gespeicherte Objekt war ja gelöscht worden, also konnte ich mit diesem nichts mehr machen - es war nicht mehr persistent.

Nach einigem überlegen fiel mir auf, das Hibernate gar nicht anders handeln kann. Meine Liste erlaubt ja grundsätzlich mehrere identische Telefonnummern. Wenn ich jetzt eine davon ändere: woran soll Hibernate erkennen, welche ich denn nun ändern will?!? Die fehlende Objektidentität zwingt also förmlich dazu, die ganze Collection zu ersetzen (zu löschen und neu zu erzeugen).

Nun war die Frage, wie man diese verlorene Identität wiederbekommen kann. Kann man, und zwar mit

  @CollectionId(columns = { @Column() }, generator = "sequence", type = @Type(type = "long"))

vor dem Collection-Feld. Diese Annotation ist laut Javadoc noch experimentell. Dementsprechend ist es zu verschmerzen, daß obige Zeile einige Informationen enthält, die ich eigentlich gar nicht einstellen will. (Für Neulinge: Hibernate wählt sonst immer sehr schön Standard-Parameter aus, so daß man vieles gar nicht konfigurieren muss.)

Diese Zeile führt dazu, daß die interne Tabelle, in der die Komponenten stehen, eine Primärschlüsselspalte bekommt. Bei der BEarbeitung der Persistenz merkt Hibernate das sofort und macht statt obigem DELETE & INSERT ein (richtigeres) UPDATE. gleichzeitig bleiben dann auch die vorhandenen Objekte persistent und deren Zeiger somit gültig.

Meine letzte Frage - die ein wenig offenbleibt - ist nun noch die, was denn nun eigentlich im Ergebnis der Unterschied zwischen dieser Lösung und der Verwendung einer echten Entity für meine Telefonnummern ist. Durch die Modellierung per Komponente hat mein Objekt offiziell (über die Java-Objekte) immer noch keine Datenbank-Identität (also keinen Primärschlüssel). Ich kann da also gar nichts falsch machen. Es ist immer sicher, daß das Löschen eines Kunden auch das Löschen einer Telefonnummer nach sich zieht. Andererseits kann man diese Beziehung per Kaskadierungs-Parametern auch für Entity-Beziehungen erzeugen. Letztlich ist es also vielleicht eine Geschmackssache.

-- ThomasBayen


Tags:  Java, Datenbank