creating digital answers

EclipseLink, das bessere Hibernate?

EclipseLink, das bessere Hibernate?

Inhaltsverzeichnis

Einführung
Beispiel: Hibernate in einer einfachen Webanwendung
Problembereiche von Hibernate am Beispiel
Lösungsmöglichkeit 1: User Interface (UI) Objekte und Daten mehrfach laden
Lösungsmöglichkeit 2: Hibernate Session an HTTP Session binden
Favorisierte Lösung: Verwendung von EclipseLink
Fazit

Einführung

logo-eclipse-linkOder auch: Was ist EclipseLink?

EclipseLink ist ein Objektrelationaler Mapper (ORM). Durch den Einsatz eines ORMs können Java-Objekte in einer spalten-basierten Datenbank abgelegt werden, ohne dass man sich manuell um die Datentransformation kümmern muss.

Für die Programmiersprache Java gibt es diverse ORMs, der bekannteste dürfte Hibernate sein. EclipseLink ist die Referenzimplementierung für die Java Persistence API 2.1 (JSR 388).

Warum soll man EclipseLink benutzen?

Möchte man sich nicht zu sehr mit SQL beschäftigen und modelliert objektorientiert, dann lässt sich mit einem ORM in Kombination mit den richtigen Frameworks (z.B. EclipseLink + spring-data JPA + QueryDSL) sehr viel Zeit sparen, die man für die Geschäftslogik verwenden kann. Die Frage des am besten passenden ORM ergibt sich aus der Art der Anwendung, die man implementieren möchte.

Beispiel: Hibernate in einer einfachen Webanwendung

Hibernate eignet sich sehr gut für Anwendungen, die einen einfachen Request / Response Zyklus haben, hier besonders die klassische HTML Formular-getriebene Anwendung:

 

Standard Hibernate Flow

HTML Formular-getriebene Anwendung

 

In diesem einfachen Beispiel werden keine Datenbankdaten in der HTTP-Session behalten, das HTML kann ohne Probleme im Browser angezeigt werden.

Problembereiche von Hibernate am Beispiel

In Zeiten moderner JavaScript Webanwendungen, die oft nur aus einer einzigen HTML Seite bestehen und sämtliche Daten dynamisch nachladen, ergeben sich jedoch Probleme. Zur besseren Veranschaulichung arbeiten wir mit einem einfachen Demo-Projekt:

Wir arbeiten im Folgenden mit einer dynamischen Webanwendung, mit der sich Bücher, Autoren und Adressen verwalten lassen. Das vereinfachte Datenmodell hierzu könnte wie folgt aussehen:

EclipseLink demo Klassendiagramm

Vereinfachtes Datenmodell Bücherverwaltung


Ein Buch (ISBN Nummer, Titel, Kurzbeschreibung usw.) hat immer genau einen Autor (Name, Vorname, Geburtstag). Der Autor wiederum hat genau eine Adresse (Straße, Hausnummer, Ort, PLZ).

In der Anwendung soll nun zuerst eine Liste mit allen Büchern angezeigt werden. Durch einen Klick auf einen Listeneintrag werden dann alle Informationen zum Buch (inklusive Autor und seine Adresse) angezeigt.

Liste mit allen Büchern

Liste mit allen Büchern

 

Für die Bücherliste selbst ist es zunächst nicht notwendig, die Daten zu den Autoren zu laden. Die in der Liste anzuzeigenden Daten sind alle im Buch-Objekt enthalten. Bei der Detailansicht müssen jedoch sämtliche verfügbaren Daten geladen werden.

Detailansicht eines Buches mit Autoren-Info

Detailansicht eines Buches mit Autoren-Info

Lösungsmöglichkeit 1: User Interface (UI) Objekte und Daten mehrfach laden

Müssen bei der Auslieferung der Webanwendung die Punkte Datenvolumen bzw. -geschwindigkeit und damit verbunden die Wartezeit nicht berücksichtigt werden (z.B. weil die Anwendung nicht auf mobile Endgeräte ausgerichtet ist), so können die Buchdaten ohne Probleme mehrfach geladen werden. Um ganz sicher Hibernate Session-Probleme zu vermeiden, werden die Datenbankdaten in UI-Objekte kopiert.

Daten werden in UI Objekte kopiert

Daten werden in UI Objekte kopiert

 

Das Laden der Daten für die Detailansicht erfolgt analog.

Diese Vorgehensweise ist recht umständlich, da die Daten aus der Datenbank in UI Objekte kopiert werden müssen. Möchte der Kunde beispielsweise in der Übersichtsliste zusätzlich noch den Namen des Autors sehen, müssen hierfür  das UI Datenobjekt erweitert und die Kopiervorgänge angepasst werden.

In dieser Lösungsversion wird das Buchobjekt zweimal geladen:

  1. für die Übersichtsliste
  2. für die Detailansicht

Lösungsmöglichkeit 2: Hibernate Session an HTTP Session binden

Möchte man die Buchdaten nicht wiederholt laden (schließlich hat man die Daten ja schon in der Liste) wird es etwas komplizierter. Entweder man initialisiert das Buchobjekt komplett mit den Daten für Autor und Adresse oder man lädt notwendige Daten nach Bedarf nach.

Eine vollständige Initialisierung (eager loading) ist nicht ratsam. Im gegebenen Beispiel ist die Datenmenge zwar noch recht überschaubar; in komplexeren Modellierungen ist die Datenmenge jedoch um ein Vielfaches größer. Durch ein eager loading würden initial zu viele Daten aus der Datenbank geladen und zum Browser geschickt. Dies wiederum beeinträchtigt die Performance der Anwendung, die in Folge langsam reagiert. Im Extremfall bleiben die Server wegen Speichermangel stehen.

Eine alternative Möglichkeit besteht darin, die Daten nur dann zu laden, wenn sie auch wirklich benötigt werden. Dieses Verfahren ist auch unter der Bezeichnung lazy loading bekannt. Mit Hibernate ist dies zwar auch möglich, allerdings muss gewährleistet sein, dass immer eine Hibernate Session zur Verfügung steht. Um die ständige Verfügbarkeit zu gewährleisten, wird in den meisten Fällen die Hibernate Session an die HTTP Session gebunden.

Das Lazy Loading erfolgt anschließend automatisch. Hibernate erkennt, falls ein Objekt noch nicht initialisiert wurde. Allerdings muss man sich selber um die Hibernate Session kümmern und es werden viele Ressourcen an die HTTP Session gebunden.

Wirklich empfehlenswert ist auch dieser Ansatz nicht – vor allem nicht für komplexe Web-Applikationen, die den ganzen Tag genutzt werden. Dadurch wird ein Leeren der HTTP Session verhindert.

Favorisierte Lösung: Verwendung von EclipseLink

Mit EclipseLink kann das Buchlistenbeispiel auf einfache Art gelöst werden. Zudem bringt diese Lösung die folgenden Vorteile mit sich:

  • keine manuelle Persistenz Sessionverwaltung
  • keine Ressourcenbindung an die HTTP Session
  • Unterstützung von LazyLoading

Verwendung von EclipseLink

EclipseLink kann in jeder Java Software verwendet werden. In einigen J2EE-Servern wird EclipseLink auch standardmäßig mitgeliefert. Grundsätzlich ist es leicht in jedes Java-Projekt integrierbar. Die Integration an sich soll hier nicht weiter beschrieben werden. Hierzu bietet die offizielle EclipseLink Dokumentation genügend Hilfestellung.

Weaving

Damit EclipseLink korrekt funktionieren kann, müssen die JPA Entities um diverse Methoden erweitert werden. Dies erledigt nicht der Entwickler, sondern das EclipseLink Framework. Dieser Vorgang nennt sich Weaving. Es gibt zwei Varianten für das Weaving.

Dynamic Weaving

Beim Dynamic Weaving werden die JPA Entities zur Laufzeit um die von EclipseLink benötigten Methoden erweitert. In manchen J2EE-Servern geschieht dies automatisch und unbemerkt. Im Tomcat jedoch erfordert dies beispielsweise einen speziellen Java Agenten, der beim Start mit übergeben werden muss. Das ist recht umständlich.

Zudem ist ein Problem mit Dynamic Weaving, dass JUnit Testfälle nicht korrekt funktionieren. Während der Tests findet kein Weaving statt, die Tests sind demnach nicht komplett aussagekräftig. Ein weiterer Nachteil ist, dass Dynamic Weaving zur Laufzeit geschieht und damit zu leichten Performance-Einbußen führt.

Static Weaving

Static Weaving kommt ohne die Nachteile des Dynamic Weaving aus. Hier werden die von EclipseLink benötigten Methoden direkt zur Kompilierungszeit hinzugefügt. Der Applikationscontainer, der Servletcontainer oder die Java Anwendung benötigen keinen speziellen Java Agenten. JUnit Tests sind aussagekräftig. Performance-Einbußen gibt es zur Laufzeit nicht, lediglich die Kompilierung benötigt ein wenig mehr Zeit.

Static Weaving kann durch unterschiedliche Aktionen angestoßen werden: in der Kommandozeile, als ANT Task oder als Maven Plugin. Wir bevorzugen hier ganz klar das Maven Plugin.

staticweave-maven-plugin – developed by empulse

EclipseLink selbst stellt kein offizielles Maven Plugin für das Static Weaving zur Verfügung. Unter https://code.google.com/p/eclipselink-staticweave-maven-plugin/ gibt es ein Plugin, das sich sehr gut für ältere Maven-, EclipseLink- und Javaversionen eignet. Allerdings standen wir nun vor dem Problem, dass besagtes Plugin nicht in Kombination mit Java 8, Maven 3 oder dem aktuellsten EclipseLink funktioniert.

Abhilfe kommt in Form eines von uns entwickelten staticweave-maven-plugin. Es ist stark von dem oben verlinkten Plugin inspiriert, ist im Gegensatz dazu aber zu den neuen Techniken kompatibel. Somit können wir die Vorteile von EclipseLink auch bei aktuellen Projekten nutzen.

Die Sourcen des Plugins befinden sich auf GitHub, direkt benutzbare Versionen werden auf Maven Central deployed und stehen allgemein zur Verfügung.

Benutzung staticweave-maven-plugin

Die Benutzung des Plugins haben wir bewusst einfach gehalten. Es muss lediglich in den build-Abschnitt der eigenen POM übernommen werden.

<build>
...
 <plugins>
  <plugin>
   <groupId>de.empulse.eclipselink</groupId>
   <artifactId>staticweave-maven-plugin</artifactId>
   <version>1.0.0</version>
   <executions>
    <execution>
     <phase>process-classes</phase>
     <goals>
      <goal>weave</goal>
     </goals>
     <configuration>
      <persistenceXMLLocation>META-INF/persistence.xml</persistenceXMLLocation>
      <logLevel>FINE</logLevel>
     </configuration>
    </execution>
   </executions>
   <dependencies>
    <dependency>
     <groupId>org.eclipse.persistence</groupId>
     <artifactId>org.eclipse.persistence.jpa</artifactId>
     <version>${eclipselink.version}</version>
    </dependency>
   </dependencies>
  </plugin>
  ...
 </plugins>
...
</build>

Bei jedem von Maven gestarteten Build wird das Plugin nun mit ausgeführt und somit das Static Weaving angestoßen. Benutzt man Eclipse als Integrierte Entwicklungsumgebung, so muss man vorher sicherstellen, dass die Maven Erweiterung m2e das Plugin während des Kompilierens ebenfalls ausführt. So ist sichergestellt, dass auch in JUnit Tests, die in Eclipse gestartet werden, das Static Weaving ebenfalls durchgeführt wird.

Fazit

  • EclipseLink als ORM eignet sich sehr gut für moderne One-Screen Webanwendungen, die z.B. mit GWT oder Vaadin erstellt werden
  • durch das Weaving wird LazyLoading ermöglicht, ohne dass man sich um manuell um jedwede Persistenz Session kümmern muss – keine Hibernate SessionExceptions mehr!
  • Static Weaving ist dem Dynamic Weaving vorzuziehen

Das von der empulse erstellte staticweave-maven-plugin ermöglicht problemloses Static Weaving mit modernen Technologien wie Java 8 und Maven 3 in jeder Java Anwendung. Moderne und performante One-Screen Webanwendungen lassen sich so spielend einfach erstellen, auch wenn die Business Daten in sehr komplexen Datenmodellen vorgehalten werden.

  • Interessieren Sie sich für eine individuelle Web-Entwicklung?

    Lassen sie uns die Einzelheiten am besten persönlich besprechen.
    Vereinbaren Sie einfach einen Beratungstermin mit uns.

    Fragen Sie uns

1 Kommentar

  1. Sascha Baumeister 08.02.2015 Antworten

    ich muss dem Autor zustimmen, wenn auch nicht so sehr aus den gegebenen Gründen. EclipseLink Weaving ist zwar gegenüber Hibernate-Proxies etwas mächtiger was das Lazy-Loading von ManyToOne-Relationen betrifft die optional sind (i.e wo das Gegenüber null werden kann), da Hibernate hier aufgibt und auf jeden Fall EAGER lädt. Dafür aber scheint es aktuell einen Bug in Jersey zu geben der die Verwendung des EntityFilteringFeatures mit EclipseLink Weaving unmöglich macht, was am Ende dazu führt dass man in beiden Umgebungen oft auf Proxies/Weaving verzichten muss. Am Ende haben daher beide Technologien ihre Eigenarten.

    Der Hauptvorteil den ich bei EclipseLink aktuell sehe ist die vollständige, bruchfreie und funktionierende Integration einer 2nd-Level Cache Implementierung, inklusive standardisierter Zugriff mittels EntityManagerFactory.getCache() zur gezielten Invalidierung von Entitäten deren Relationsmengen sich geändert haben. Damit wird es leicht möglich Anwendungen zu entwickeln die nach Erstzugriff praktisch nur noch nach Änderungen auf die Datenbank zugreifen müssen.

    Hibernate scheint mir in diesem Bereich deutlich in der Entwicklung zurück zu fallen. Ich habe massive Probleme in der aktuellen 4.3.x Version mit Stale Data erlebt, woran auch gezielte Invalidierung mittels EntityManagerFactory.getCache() nichts änderte. Selbst der (verzweifelte) Versuch den 2nd-Level Cache abschalten wurde ignoriert, die Stale-Data Probleme gingen unvermindert weiter, so dass ich am Ende auf die 4.2.x Version zurück setzen musste. Und selbst wenn man nicht ständig mit veralteten Daten in den Entitäten kämpfen müsste, wäre eine Hibernate-basierte Implementierung immer noch deutlich langsamer weil keine Relationsmengen gecached werden …

    Dazu kommt dass Hibernate den JPA-Standard weiterhin eher als lästiges Anhängsel denn als Hauptgeschäft zu betrachten scheint, was sich in jahrelang ungefixten Bugs mit der Begründung „minor specification violation and not a critical bug“ (siehe https://hibernate.atlassian.net/browse/HHH-9302) äußert. Für mich war jedenfalls die Reissleine erreicht, ich habe meine Applikationen von Hibernate auf EclipseLink umgestellt, und bin damit aktuell sehr glücklich.

Kommentar verfassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

*

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>