Unsere Datenschutzrichtlinie wird in Kürze aktualisiert. Bitte sehen Sie sich die Vorschau an.

Java EE 7: Geschäftsanwendungen

Mit Lazy Loading umgehen

Testen Sie unsere 2013 Kurse

10 Tage kostenlos!

Jetzt testen Alle Abonnements anzeigen
Lazy Loading ist eine der Errungenschaften von O/R-Mapping-Frameworks – und gleichzeitig ein gewaltiges Problem, wenn man mit EJBs und verteilten Applikationen arbeiten möchte. Sehen Sie, welche Strategien man beim Einsatz von Lazy Loading einsetzen kann.

Transkript

Lazy Loading ist eine der herausragenden Eigenschaften von Hibernate, beziehungsweise de JPA. Allerdings ist Lazy Loading im EJB Umfeld ein riesiges Problem, denn Lazy Loading funktioniert nur mit einem aktivierten EntityManager und dieser EntityManager ist leider nicht mehr existent, wenn der Client mit den Daten arbeiten möchte. Aus diesem Grund werden wir uns in diesem Video damit auseinandersetzen, wie wir mit Lazy Loading umgehen können, welche Strategien man wählen kann, um Lazy Loading bei Bedarf vielleicht dann doch zu aktivieren oder eben generell zu deaktivieren. Wir werden ebenfalls unseren Code so anpassen und wir werden noch einige andere weitreichende Änderungen an der Art und Weise vornehmen wie unsere Entitäten annotiert sind. Generell gilt, abhängige Listen werden per Lazy Loading geladen. Und Lazy Loading bedeutet nicht anderes als der statt der tatsächlichen Instanz eines Objektes ein Proxy-Objekt zurückgegeben wird. Dieses Proxy-Objekt hat den ganz gewaltigen Vorteil, dass eben eigentlich nicht das geladen wird, was man wirklich in dem Objekt drin hat, sondern nur erstmal Platzhalter. Und erst wenn auf Eigenschaften des Proxy-Objekts zugegriffen wird, werden die Daten geladen. Das bedeutet, wenn ich die Daten nicht brauche, dann werden sie auch nicht geladen und das ist natürlich effizient, auf den ersten Blick. Wie gesagt, im Java EE Umfeld ist das aber ein riesen Problem, denn Lazy Loading funktioniert nur so, dass wir einen aktiven EntityManager Context haben. Und den gibt es nicht mehr. Der EntityManager Context hört auf zu existieren, sobald die Entität serialisiert wird. Deswegen kann man das Lazy Loading auch abschalten. Grundsätzlich gibt es verschiedene Strategien, wie wir mit dem Lazy Loading umgehen können. Wir können es dauerhaft abschalten. Dazu können wir auf Ebene der One To Many und der Many To Many Assoziation den Fetch Type.EAGER setzen. Wir können es bei Bedarf abschalten. Das bedeutet wir sind in der Lage, auf Ebene einer JPQL-Abfrage ein JOIN FETCH Konstrukt zu benutzen. Mit JOIN FETCH wird dafür gesorgt, dass das was da gejoint wird auch sofort mitgeladen wird. Und eine dritte Variante ist es sogenannte Constructor Expressions zu verwenden, das heißt, wir lassen uns Datentransferobjekte definieren und geben, die ohne Lazy Loading funktionieren. In unserer Applikation werden wir uns dafür entscheiden das Lazy Loading komplett abzuschalten. Das bedeutet, wir verwenden den FetchType.EAGER für die Abhängigen Entitäten der Customer Entität. Dies kann und wird im Webumfeld später zu Problemen führen, denn wenn wir dort eine Listendarstellung beispielsweise umsetzen wollen, dann laden wir potenziell viel zu viele Daten. Aber in so einem Moment kann man immer noch auf beispielsweise Constructor Expression zurückgreifen und sich somit Datentransferobjekte erzeugen, die selber ohne Lazy Loading auskommen und nur die Daten beinhalten, die man tatsächlich anzeigen möchte. Wir befinden uns hier in unserem JUnit Test. Wir werden nun hier den Kunden noch um zusätzliche Informationen erweitern und ergänzen, nämlich um die Angabe einer Adresse. Diese wird dann der Addresses-Auflistung zugewiesen und um die Angabe eines Kommunikationsdatensatzes, dieser wird dann der Cummunications- Auflistung des Kunden zugewiesen. Wenn wir nun den Test ausführen, werden wir feststellen, dass es einen Fehler gibt. Und wenn wir uns das etwas genauer anschauen, sehen wir, dass es sich um eine NullPointerException handelt. Diese NullPointerException tritt auf, wenn wir versuchen die Adresse der Addresses-Auflistung zuzuweisen. Hintergrund ist, dass wir diese Addresses-Auflistung bisher nicht initialisiert haben. Aus diesem Grund werden wir nun die Customer Entität etwas abändern, nämlich so, dass die Addresses-Auflistung tatsächlich initialisiert wird. Innerhalb der Customer Klasse können wir dies gleich bei der Definition der lokalen Membervariablen erledigen, indem wir hier neue ArrayList Instanzen vom Typ Address, beziehungsweise vom Typ Communication zuweisen. Damit ist dieses Problem gelöst. Wir können nun unser EJB Modul erneut exportieren, um es dem Test zur Verfügung zu stellen. Wir müssen wieder dieselben unnötigen Einstellungen entfernen, das bedeutet, wir wollen keine Beans exportieren und wir wollen auch nicht das META-INF Verzeichnis exportieren und wir überschreiben einfach unser, bisher schon vorhandenes Archiv. Wenn wir nun das Testprojekt refreshen, wird die neue Version des JAR Archivs geladen, und wenn wir nun unseren Test ausführen, werden wir sehen, dass die Listen vorinitialisiert sind. Nun können wir unsren Test erweitern und das Überprüfen der Daten, die wir an den Server übergeben haben. Weil bisher überprüfen wir ja nicht, ob Adresse und Kommunikationseintrag korrekt gespeichert worden sind. Zu diesem Zweck werden wir nun den Kunden erneut abrufen und dann versuchen auf Adresse und Kommunikation zuzugreifen. Wir rufen den Kunden also erneut ab und überprüfen danach, ob Adressen vorhanden sind, lassen uns den Adresseintrag geben, überprüfen den Eintrag darauf, ob die Werte identisch sind und genau dasselbe tun wir bei einem Kommunikationseintrag. Sehen wir uns nun an was passiert, wenn wir nun den Test erneut laufen lassen. Wir bekommen eine Exception. Diese Exception besagt, dass eine bestimmte Klasse nicht gefunden werden konnte, nämlich die Klasse PersistentBag, aus dem org.hibernate.collection.internal Package. Das ist sehr ärgerlich, denn der Client soll ja eigentlich gar nichts davon Wissen, dass auf dem Server Hibernate in irgendeiner Art und Weise läuft. Um dieses Problem dennoch zu lösen, müssen wir Hibernate-Core unserem Projekt, dem Class Path mit hinzufügen. Zu diesem Zweck machen wir ein Rechtsklick auf unser Projekt, wählen "Properties", wählen "Java Build Path" und klicken im Reiter "Libraries" auf "Add External JARs". Nun wechseln wir in den WildFly Ordner. Hier gibt es ein Unterordner "modules". Der hat ein Unterordner "system" und dieser wiederum ein Unterordner "layers". Innerhalb von "layers" befindet sich ein Ordner "base". Dieser Ordner "base" verfügt über einen Ordner "org" und hier drinnen finden wir einen Ordner "hibernate". Wenn wir hier in den Unterordner "main" gehen, finden wir "hibernate-core" in der Version, in der der jboss, beziehungsweise der WildFly Server Hibernate benutzen. Dieses JAR Archiv fügen wir nun unserem Projekt hinzu. Das ist zugegebenermaßen nur ein Workaround. Normalerweise werden Sie bemüht sein dieses Problem nicht aufkommen zu lassen, indem Sie beispielsweise ein Hibernate 4 JAR Archiv Ihrem Client mit hinzufügen und von vornherein mit ausliefern. Wenn wir nun den Test laufen lassen, werden wir feststellen, dass wir einen anderen Fehler erhalten werden. Nämlich die Meldung "failed to lazily initialize a collection" mit weiterführenden Informationen, ganz hinten steht der spannende "could not initialize proxy-no Session". Das Ganze tritt auf, wenn wir den abgerufenen Kunden darauf überprüfen, ob wir eine Adressenliste haben. Und das Ganze ist dann Ausdruck dessen, dass das Lazy Loading aktiviert ist. Aus diesem Grund werden wir nun das Lazy Loading beim Customer deaktivieren. Wir wechseln also in die Customer Entität und werden nun hier bei den OneToMany Assoziationen noch ein Attribut setzen, nämlich fetch mit dem Wert FetchType.EAGER. Dies machen wir bei beiden Assoziationen. Und wo wir schon dabei sind, können wir auch nochmal einen ganz kurzen Blick auf das Datenbankschema werfen, was generiert worden ist und uns überlegen, ob wir dieses Datenbankschema tatsächlich beibehalten wollen. Innerhalb des Datenbankschemas erkennen wir drei Tabellen: address, communication und customer. Diese repräsentieren unsere Entitäten, das sind gute Tabellen. Darüber hinaus gibt es aber zwei weiter Tabellen, nämlich: customer_address und customer_communication. Deren Aufgabe besteht offensichtlich darin, die Communication-, beziehungsweise Address Entitäten auf den Customer abzubilden. Eigentlich wäre es naheliegend, wenn Address, beziehungsweise Communication jeweils nur ein Fremdschlüsselfeld besäßen, in dem der Fremdschlüssel des Customers drin verfasst ist. Darüber hinaus gibt es noch eine weitere Tabelle, nämlich hibernate-sequence. Und diese hibernate-sequence Tabelle wird genutzt, um den Primärschlüssel zu erzeugen. Alles in allem ist das kein Datenbankschema und kein Datenbankmodell, wir es uns vorschweben sollte. Lassen Sie uns aus diesem Grund schnell ein paar Änderungen vornehmen. Wir definieren auf Ebene der Listen sogenannte @JoinColumns. Diese @JoinColumn gibt dabei an, dass ein Fremdschlüssel in der Spalte Customer ID erfasst wird. Und damit wird automatisch das Datenbankschema so angepasst, dass es nun mehr eine Spalte Customer ID gibt, indem der Fremdschlüssel, also der Primärschlüssel des Customers drin enthalten ist. Verantwortlich für die Sequence-Tabelle ist hier "GenerationType.Auto". Wenn wir diesen auf "Identity" setzen, dann wird Hibernate auch in jedem Fall den Primärschlüssel-Generierungsansatz von MySQL benutzen und dieser Primärschlüssel-Generierungsansatz besagt, wir verwenden hier ein Identitätsfeld. Damit haben wir die Anpassung unter unsere Entitäten vorgenommen. GeneratedValue verwendet nun die Strategie Identity. die @OneToMany-Assoziationen sind erstens als Lazy Loading-Assoziation ausgeführt und zweitens verwenden Sie nun mehr eine @JoinColumn-Annotation, mit der wir Auskunft darüber geben, dass in der jeweiligen Tabelle der Fremdschlüssel erfasst wird. Nun werden wir noch einmal die Applikation auf dem Server deployen und Hibernate das Datenbankschema neu generieren lassen. Ebenfalls können wir nun unseren Test ausführen. Wir werden feststellen, dass der Test funktionieren wird. Wir führen also einen Rechtsklick auf den Testfall aus und wählen die Option Run AsJUnit Test. Einige Sekunden später ist der Testfall abgeschlossen und wir stellen fest, jawoll, alles grün, alle Tests sind erfolgreich abgeschlossen worden. Sollte es bei Ihnen auf der Kommandozeile fehlerhafte Ausgaben geben, die aus MySQL hinweisen, dann ist es zu empfehlen, dass Sie in der MySQL Workbench von Hand die Tabellen löschen. Machen Sie einfach ein Rechtsklick dadrauf und wählen Sie die Option DROP TABLE. Sollte alles funktioniert haben, sehen Sie hier im Datenbankschema auch nur noch drei Tabellen, nämlich Address, Communication und Customer. Ein Rechtsklick dadrauf und wir können uns davon überzeugen, dass wir ein Feld Customer ID bei Address und bei Communication nun mehr besitzen. Dort stehen dann die Primärschlüssel der Datensätze drin, die wir eingefügt haben. In diesem Video haben wir uns also damit auseinandergesetzt, wie wir Lazy Loading behandeln können, wie wir damit umgehen können, wie wir auch die daraus resultierenden Fehler beseitigen können. Und wir haben die Gelegenheit genutzt gleich noch unsere Entitäten so anzupassen, dass das Tabellenschema, das erzeugt wird, dann auch tatsächlich unseren Vorstellungen entspricht und das wir tatsächlich eine geeignete Primärschlüssel- Generierungsstrategie einsetzen.

Java EE 7: Geschäftsanwendungen

Verfolgen Sie, wie eine komplette Business-Applikation unter dem Einsatz des gesamten Java-Enterprise-Techologiestacks ensteht.

5 Std. 2 min (39 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!