Java EE 7 Grundkurs

Beziehungen zwischen Entitäten

LinkedIn Learning kostenlos und unverbindlich testen!

Jetzt testen Alle Abonnements anzeigen
Die Java EE verfügt über verschiedene Annotationen, um Beziehungen zwischen einzelnen Entitäten darzustellen. Dieses Video erläutert die Definition der Beziehungen sowie dabei auftretende Probleme.

Transkript

Es ist bei der JPA wie im wahren Leben: Beziehungen sind das Salz in der Suppe. Alleine kann man zwar glücklich werden, aber es wird doch alles ein wenig anstrengender. Grund genug, dass wir uns in diesem Video mit Beziehungen im JPA-Umfeld auseinandersetzen. Generell ist es so, dass Entitäten verschiedene Beziehungen besitzen können. Diese drücken wir über Annotationen aus. Folgende Annotationen gibt es: OneToOne, das bedeutet, dass jeder Entität eine 1-1-Beziehung mit einer anderen Entität besitzt. Diese Annotationen werden auf Member-Ebene, beziehungsweise auf Getter-Ebene notiert. OneToMany, das bedeutet, dass eine Instanz dieser Entität eine Beziehung mit mehreren anderen Instanzen einer anderen Entität besitzen kann, also eine klassische 1-n-Beziehung. Auch das Gegenstück gibt es, die ManyToOne-Beziehung. Das bedeutet, dass mehrere Instanzen einer Entität eine Beziehung mit einer anderen Instanz einer anderen Entität besitzen können. Die n-1-Beziehung. Bitte lassen Sie sich von den Namen nicht irritieren. OneToMany wird immer ausgedrückt als eine Liste, während ManyToOne immer eine einzelne Referenz auf eine andere Instanz ist. Und zuletzt die ManyToMany-Beziehung. Das ist auf beiden Seiten, also bei beiden Entitäten eine Liste. Und diese besagt, dass mehrere Instanzen einer Entität eine Beziehung mit mehreren Instanzen einer anderen Entität besitzen können. Also die n-n-Beziehung. Beziehungen können, wie sollte es anders sein, zweiseitig sein. Anders als im wahren Leben muss dies jedoch nicht der Fall sein. Dann kann das auf beiden Seiten der Beziehung notiert werden. Die Beziehung kann ebenfalls kaskadierend gestaltet werden. Dass bedeutet, wenn eine Seite der Beziehung gelöscht wird, wird automatisch auch die andere Seite der Beziehung gelöscht. Achtung! Das sollten Sie mit Vorsicht einsetzen. Sie sollten sich genau überlegen, was Sie an der Stelle tun, denn wenn Sie nicht aufpassen, löschen Sie ein referenziertes Objekt, das seinerseits wieder andere Objekte referenziert, die ihrerseits wieder andere Objekte referenzieren. Und ehe Sie bis drei gezählt haben, ist Ihre Datenbank leer. Das kann erwünscht sein, muss es aber nicht. Darüber hinaus gibt es noch andere Formen der Beziehung, nämlich Embedded und Embeddable. Diese Beziehungen notieren eingebettete Klassen. Das heißt, das sind Klassen, die selber keine eigene Repräsentation in der Datenbank haben. Aber diese Klassen machen bestimmte Dinge einfach besser fassbar. Beispielsweise eine Kontaktklasse, die die Kontaktinformationen in sich selbst geschlossen hält. Dadurch müssen Sie in der referenzierenden Klasse eben nicht E-Mail-Adresse, Fax-Nummer, Telefonnummer, oder andere Daten halten, sondern Sie halten nur eine Instanz dieser Kontaktklasse. Grundsätzlich werden abhängige Listen immer per Lazy Loading geladen. Lazy Loading bedeutet, dass statt der tatsächlichen Instanz stets ein Proxyobjekt zurückgegeben wird. Das hat ganz gewaltige Vorteile, denn dieses Proxyobjekt beinhaltet lediglich den Primärschlüssel des eigentlich zu ladenden Objektes und ansonsten fungiert dieser Proxy als ein Platzhalter. Erst wenn andere Eigenschaften auf dem Proxyobjekt aufgerufen werden, werden die Daten wirklich geladen. Dadurch kann sich die Performance gewaltig steigern, denn wir müssen eben lediglich die bereits bekannten Daten, nämlich den Primärschlüssel laden und nicht die anderen 35 Felder eines referenzierten Objektes. Vor allen Dingen dann nicht, wenn wir dies nicht benötigen. Aber nochmal Achtung! Das kann ganz große Probleme im EJB-Umfeld aufwerfen. Und deswegen kann dieses Verhalten abgeschaltet werden. Warum das große Probleme aufwerfen kann? Stellen Sie sich vor, sie arbeiten mit einer statefull oder stateless session-Bean In dieser statefull/stateless session-Bean haben Sie einen EntityManager injiziert. Sobald nun die Abfrage auf diese statefull/stateless Session-Bean fertig ist, verfällt der EntityManager. Und der Proxy, der mit zurückgegeben wird, der für das Lazy Loading zuständig ist, hat einfach keine Komponente mehr, mit der er arbeiten kann. Das bedeutet für Sie: Sie bekommen zwar eine Liste von Objekten zurück, aber sobald Sie versuchen auf eine Eigenschaft eines Objektes dieser Liste zuzugreifen, wird es eine Exception geben. Wie gesagt, dieses Verhalten lässt sich abschalten. Da gibt es zwei Möglichkeiten. Zum Einen in einer JPQL-Abfrage das JOIN-FETCH-Schlüsselwort zu benutzen, oder auf Ebene einer Annotation den FetchType auf "EAGER" zu setzen. Eager Loading bedeutet nämlich, dass die referenzierten Komponenten sofort mitgeladen werden. Wie man sich vorstellen kann, kann dies performancetechnisch natürlich wieder negativ werden. Stellen Sie sich vor, Sie müssten eine Liste mit 10.000 Objekten mit jeweils 20 Feldern laden. Dann kann es sein, dass einfach große Mengen an Daten über das Netz geschickt werden obwohl sie gar nicht benötigt werden. Aus diesem Grund ist es sinnvoll, sich beim Design seiner Applikation bereits darüber Gedanken zu machen, wie man Eager beziehungsweise Lazy Loading implementieren möchte. Wenn Sie eine Variable vom Typ einer eins in der Instanz haben, also keine Liste, wird bei JPA immer Eager Loading benutzt. Wie gesagt, Lazy Loading gibt es nur, auf Ebene von Listen und dort ist es, wie auch bereits erwähnt, mit Vorsicht zu genießen. Nach so viel Theorie, lassen Sie uns einmal anschauen, wie das Ganze in der Praxis umgesetzt werden könnte. Achtung, ich sage es Ihnen gleich, Es wird viele Annotationen zu bestaunen geben. Wir sehen hier eine Webapplikation. In dieser Webapplikation werden Bücher angezeigt. In diesem Fall ist nur ein Buch definiert. Dieses Buch enthält seine normalen Daten Und eine Liste von Autoren. Ebenfalls, hier nicht angezeigt, gibt es noch einen Publisher, Also ein Verlag, in dem dieses Buch erschienen ist. Diese Daten sollen geladen und visualisiert werden. Wie ist dies umgesetzt? Nun, gebaut ist das Ganze in der Klasse "showBooks". In dieser View befindet sich eine data table. Diese data table erlaubt es uns, die Autoren auszugeben. Wir lesen sie aus der Eigenschaft "authors" der "Book"-Klasse aus und durchlaufen das Ganze dann in einem "ui: repeat"-Element. Werfen wir einen Blick auf die Klasse "Book" in der das Ganze ja hinterlegt ist. Diese Klasse "Book" ist erst mal eine für sich alleinstehende Entität. Sie verfügt aber über zwei Beziehungen. Zum einen gibt es eine ManyToOne-Beziehung mit dem Publisher. Der Publisher ist der Verlag. Und die ManyToOne-Beziehung besagt an dieser Stelle, viele Bücher können auf einen Publisher zeigen. Dies wird ausgedrückt, über eine Variable vom Typ "publisher". Wir haben hier bei der ManyToOne-Beziehung noch eine Angabe, dass es sich um eine optionale Beziehung handelt. Das bedeutet, wenn kein Publisher definiert ist, gibt es kein Problem. Es kann sich auch um ein noch nicht veröffentlichtes Buch handeln. ebenfalls gibt es hier die Angabe einer sogenannten JoinColumn. Diese JoinColumn verweist auf ein Feld "publisher". Die Frage ist nun natürlich: Welches Feld "publisher" ist gemeint? Dieses Feld "publisher" befindet sich in der Datenbank. Ich habe hier einmal die Datenbankdeklaration geöffnet für die Tabelle "books", in der ja die "Book"-Entität gespeichert wird. Hier gibt es dieses Feld "publisher". Es ist ein Integer-Wert und dies ist der Wert, der in der "Book"-Entität referenziert wird. Nachdem wir also geklärt haben, worauf diese JoinColumn-Annotation zeigt, können wir uns dem, etwas weiter unten stehenden, sehr komplexen Konstrukt zuwenden. In diesem Konstrukt wird eine Liste vom Typ "author" referenziert. Wir haben hier zunächst eine ManyToMany-Annotation. Diese ManyToMany-Annotation besagt, dass es hier eine n-n-Beziehung gibt, nämlich zwischen Büchern und Autoren. denn ein Autor kann mehrere Bücher geschrieben haben, und ein Buch kann potentiell von mehreren Autoren geschrieben sein. Es muss zu diesem Zweck eine Kreuztabelle geben. Dies wird ausgedrückt über die JoinTable-Annotation. Das Feld "name" darin gibt Auskunft, welche Tabelle gemeint ist. In der Datenbank sehen wir diese Tabelle. Sie heißt "authors_books" und sie verfügt über zwei Spalten, "author" und "book". Diese beiden Spalten werden in der JoinTable-Annotation ebenfalls angesprochen, nämlich über "joinColumns" und "inverseJoinColumns". Die "joinColumns" sind die Spalten, auf die sich diese Entität bezieht. Das heißt, der Primärschlüssel ausgedrückt über "referencedColumnName", wird in der Spalte "book" der "authors_books"-Tabelle hinterlegt. Dieselbe Information gilt für den anderen Zweig dieser Beziehung. Das ist die "inverseJoinColumns"-Information. Hier gibt es ebenfalls eine Information darüber, dass das Feld "id" von der referenzierten Entität, nämlich dem Autoren, in das Feld "author" gespeichert wird. Hier oben auf Ebene der ManyToMany-Annotation haben wir darüber hinaus noch weitere Informationen, nämlich eine sogenannte Cascading-Information. Diese Cascading-Information gibt Auskunft darüber, wie die JPA verfahren soll, wenn dieser Liste ein Autor zugeordnet wird, der noch nicht gespeichert ist. Die Cascading-Information "CascadeType.All" besagt, wenn dieser Autor noch nicht gespeichert wird, dann speichere ihn, wenn du diese Liste speicherst. Wenn der Autor in der Liste nicht mehr enthalten ist, dann wird er aus der Datenbank gelöscht. Wenn die komplette Entität gelöscht wird, werden alle Entitäten, die referenziert sind, gelöscht. Hier muss man vorsichtig sein. Es gibt verschiedene "CascadeTypes", die man da angeben kann. Die zweite Information, die hier gesetzt ist, ist die fetch-Information. Eine Beziehung, die wir hier haben, wird üblicherweise als Lazy Loading geladen, Das bedeutet, ich bekomme in diese Liste keine Autor-Instanzen sondern Proxys, die aussehen wie Autoren, aber eben keine Autoren sind. Wenn ich jetzt den EJB-Kontext verlasse, und das tue ich hier, dann werde ich ein Problem haben, sobald ich versuche, die Autoren abzurufen. Denn ich arbeite mit Proxys und in diesen Proxys wird versucht, auf die Felder der Autoren zuzugreifen. Nur es gibt keinen Persistence-Kontext mehr. Deswegen setze ich hier den "FetchType" auf "EAGER", und damit wird letztlich hier kein Proxy mehr injiziert für den einzelnen Autor, sondern es werden tatsächlich die kompletten Autor-Instanzen geladen. Vollständig. So dass ich diese Liste dann auch durchlaufen kann, wenn ich den EJB-Kontext verlassen habe. Werfen wir nun einen Blick auf die "Publisher"-Klasse. Diese "Publisher"-Klasse ist erst mal eine ganz normale Entität. Hier gibt es soweit auf den ersten Blick kaum interessante Dinge zu erkennen. Aber wenn wir etwas genauer hinschauen, sehen wir schon einige spannende Sachen. Zum einen gibt es hier eine Referenz auf die Bücher. Also die Bücher, die ein Verlag veröffentlicht hat. Die "Book"-Klasse verfügt eine Referenz auf den Verlag. Dies hier ist die andere Seite dieser Beziehung. Das heißt, der Verlag hält eine Liste der Bücher. Hier befidnet sich eine OneToMany-Annotation, und diese OneToMany-Annotation besagt jetzt, dass ein Verlag mehrere Bücher haben kann. Darüber hinaus gibt es hier wieder weiterführende Informationen, zum Beispiel das Cascading. Diese "cascade"-Information besagt, wenn der Verlag gespeichert wird, also wenn die Publisher-Instanz gespeichert wird, und in der Bücherliste sich noch ungespeicherte Bücher befinden, dann werden diese Bücher auch gespeichert. Sollte der Verlag gelöscht werden, werden auch alle Bücher, die damit referenziert sind, gelöscht. und so weiter. Die "mappedBy"-Angabe besagt, dass der Verlag eigentlich die schwächere Seite der Beziehug ist. Das bedeutet, die Hauptarbeit, beziehungsweise die Hauptbeziehung, der stärkste Fokus der Beziehung liegt auf der Buch-Klasse. Diese "mappedBy"-Angabe hier besagt, wo liegen denn die Verlagsinformationen auf der "Book"-Instanz. Nämlich in der Eigenschaft "publisher". Darüber hinaus besitzt dieser Publisher noch eine weitere Besonderheit. Wir haben hier nämlich einen Kontakt. Der Kontakt selber ist keine Entität. Wir erkennen das daran, dass oberhalb des Feldes die Embedded-Annotation steht. Diese Embedded-Annotation besagt: Du bist eigentlich gar keine von der JPA verwaltete Klasse. Es werden dir nur bestimmte Felder aus der Datenbanktabelle zugewiesen. Und dann wird diese Instanz hier zugewiesen. Es gibt dazu natürlich die entsprechende Klassendeklaration. Und diese Klassendeklaration besitzt nicht die Eigenschaften einer Entität. Das heißt, hier gibt es kein ID-Feld, und hier gibt es auch keine Entity-Annotation. Am Kopf der Klasse gibt es aber die Embeddable-Annotation und es ist immer so, dass es zu einer Embedded-Annotation eben auch das Gegenstück Embeddable geben muss. Was jetzt hier passiert ist letztlich, dass beim Abrufen der Daten eine "Contact"-Instanz erzeugt wird und die Felder der Datenbanktabelle da dieser Contact-Instanz zugewiesen werden. Und dann wir diese "Contact"-Instanz dem "Publisher" zugewiesen. Warum wir das machen? Naja. Wir haben dieselben Informationen nochmal beim Autoren. Auch hier haben wir Kontaktinformationen und es sind genau dieselben Informationen, also E-Mail, Telefon, Fax und so weiter. Es wäre jetzt einfach mühsig, das Ganze doppelt zu schreiben. Deswegen haben wir sie in einer eigenen Klasse definiert. Und auch hier ist wieder die Embedded-Annotation. Damit wird der JPA gesagt, der Autor beinhaltet Kontaktinformationen. Diese Kontaktinformationen werden in "Contact"-Instanzen gehalten. Auch hier gibt es wieder eine Liste. nämlich die Liste der Bücher. Und hier haben wir die andere Seite der ManyToMany-Beziehung. Die Schwächere. Das heißt, der Autor ist ein wenig mehr vom Buch abhängig. Und die Informationen, die für diesen Aufbau der Liste notwendig sind, also welche Spalten in welcher Tabelle herangezogen werden, die befinden sich auf der Eigenschaft "authors" der referenzierten Klasse. Beim "Book" ist das genau diese Eigenschaft. und hier ist diese JoinTable-Annotation. Dieses "mappedBy" dient also letztlich immer dazu, zu sagen: JPA, du musst an dieser Stelle nachschauen, um die Informationen zu bekommen. Ansonten gibt es hier beim Autoren nichts weiter zu beachten. Sie sehen schon: Es gibt eine ganze Menge zu bedenken und zu beachten, wenn man mit Beziehungen arbeitet. Das ist glaube ich wie im wahren Leben. Und auch wie im wahren Leben ist es so, dass gerade in Bezug auf Beziehungen, die wir in der JPA natürlich auch haben, noch nie ein Meister vom Himmel gefallen ist.

Java EE 7 Grundkurs

Lernen Sie die Grundlagen der Programmierung mit Java EE 7 verstehen und anwenden.

6 Std. 4 min (44 Videos)
Derzeit sind keine Feedbacks vorhanden...

Dieser Online-Kurs ist als Download und als Streaming-Video verfügbar. Die gute Nachricht: Sie müssen sich nicht entscheiden - sobald Sie das Training erwerben, erhalten Sie Zugang zu beiden Optionen!

Der Download ermöglicht Ihnen die Offline-Nutzung des Trainings und bietet die Vorteile einer benutzerfreundlichen Abspielumgebung. Wenn Sie an verschiedenen Computern arbeiten, oder nicht den ganzen Kurs auf einmal herunterladen möchten, loggen Sie sich auf dieser Seite ein, um alle Videos des Trainings als Streaming-Video anzusehen.

Wir hoffen, dass Sie viel Freude und Erfolg mit diesem Video-Training haben werden. Falls Sie irgendwelche Fragen haben, zögern Sie nicht uns zu kontaktieren!