Skip to content

1. Einführung

1.1. Ziele

Die PDF des Dokuments ist verfügbar |HIER|.

Die Beispiele des Dokuments sind unter |HIER| verfügbar.

Hier sollen die wichtigsten Konzepte der Datenpersistenz mit der API JPA (Java Persistence API) vorgestellt werden. Nach der Lektüre dieses Dokuments und dem Ausprobieren der Beispiele sollte der Leser über die notwendigen Grundlagen verfügen, um anschließend auf eigenen Beinen stehen zu können.

Die API JPA ist relativ neu. Sie ist erst seit JDK 1.5 verfügbar. Die JPA-Schicht hat ihren Platz in einer mehrschichtigen Architektur. Betrachten wir eine solche, recht verbreitete Architektur, nämlich die dreischichtige:

  • Die Schicht [1], hier als [ui] (User Interface) bezeichnet, ist die Schicht, die über eine grafische Swing-Oberfläche, eine Konsolenoberfläche oder eine Weboberfläche mit dem Benutzer kommuniziert. Ihre Aufgabe besteht darin, vom Benutzer stammende Daten an die Schicht [2] weiterzuleiten oder dem Benutzer Daten anzuzeigen, die von der Schicht [2] bereitgestellt werden.
  • Die Schicht [2], hier als [metier] bezeichnet, ist die Schicht, die die sogenannten Geschäftsregeln (c.a.d) anwendet. die anwendungsspezifische Logik, ohne sich darum zu kümmern, woher die ihr übergebenen Daten stammen oder wohin die von ihr erzeugten Ergebnisse gelangen.
  • Die Schicht [3], hier als [dao] (Data Access Object) bezeichnet, ist die Schicht, die der Schicht [2] vorab gespeicherte Daten (Dateien, Datenbanken, ...) und die einige der von der Schicht [2] gelieferten Ergebnisse speichert.
  • Die Schicht [JDBC] ist die in Java verwendete Standardschicht für den Zugriff auf Datenbanken. Sie wird üblicherweise als JDBC-Treiber von SGBD bezeichnet.

Es wurden zahlreiche Anstrengungen unternommen, um den Entwicklern das Schreiben dieser verschiedenen Schichten zu erleichtern. Unter diesen zielt JPA darauf ab, die Entwicklung der Schicht [dao] zu vereinfachen, die die sogenannten persistenten Daten verwaltet – daher auch der Name API (Java Persistence API). Eine Lösung, die sich in den letzten Jahren in diesem Bereich durchgesetzt hat, ist Hibernate:

Die Schicht [Hibernate] befindet sich zwischen der vom Entwickler geschriebenen Schicht [dao] und der Schicht [Jdbc]. Hibernate ist ein ORM (Object-Relational Mapping), ein Tool, das eine Brücke zwischen der relationalen Welt der Datenbanken und der Welt der von Java verarbeiteten Objekte schlägt. Der Entwickler der Schicht [dao] sieht weder die Schicht [Jdbc] noch die Datenbanktabellen, deren Inhalt er nutzen möchte. Er sieht lediglich das Objektabbild der Datenbank, das von der Schicht [Hibernate] bereitgestellt wird. Die Verbindung zwischen den Datenbanktabellen und den von der Schicht [dao] verarbeiteten Objekten wird hauptsächlich auf zwei Arten hergestellt:

  • über Konfigurationsdateien vom Typ XML
  • durch Java-Annotationen im Code, eine Technik, die erst ab JDK 1.5 verfügbar ist

Die Schicht [Hibernate] ist eine Abstraktionsschicht, die so transparent wie möglich sein soll. Das angestrebte Ideal ist, dass der Entwickler der [dao]-Schicht völlig ignorieren kann, dass er mit einer Datenbank arbeitet. Dies ist möglich, wenn er nicht selbst die Konfiguration schreibt, die die Brücke zwischen der relationalen und der Objektwelt schlägt. Die Konfiguration dieser Brücke ist recht knifflig und erfordert eine gewisse Übung.

Die Objektschicht [4], die der Schicht BD entspricht, wird als „Persistenzkontext“ bezeichnet. Eine auf Hibernate basierende Schicht [dao] führt Persistenzaktionen (CRUD: create – read – update – delete) an den Objekten des Persistenzkontexts durch, wobei diese Aktionen von Hibernate in Befehle SQL übersetzt werden. Für Abfragen an der Datenbank (SQL Select) stellt Hibernate dem Entwickler eine Sprache (Hibernate Query Language) zur Verfügung, um den Persistenzkontext abzufragen und nicht die Datenbank selbst.

Hibernate ist beliebt, aber schwer zu beherrschen. Die Lernkurve, die oft als einfach dargestellt wird, ist in Wirklichkeit ziemlich steil. Sobald man eine Datenbank mit Tabellen hat, die Eins-zu-Viele- oder Viele-zu-Viele-Beziehungen aufweisen, ist die Konfiguration der Relational-Objekt-Brücke nicht mehr für jeden Anfänger geeignet. Konfigurationsfehler können dann zu leistungsschwachen Anwendungen führen.

In der kommerziellen Welt gab es ein Produkt namens Toplink, das Hibernate entsprach:

Angesichts des Erfolgs der ORM-Produkte beschloss Sun, der Entwickler von Java, eine ORM-Schicht über eine Spezifikation namens JPA zu standardisieren, die zeitgleich mit Java 5 erschien. Die Spezifikation JPA wurde sowohl von Toplink als auch von Hibernate implementiert. Toplink, das ursprünglich ein kommerzielles Produkt war, ist inzwischen zu einem Open-Source-Produkt geworden. Mit JPA sieht die bisherige Architektur nun wie folgt aus:

Die Schicht [dao] kommuniziert nun mit der Spezifikation JPA, einer Sammlung von Schnittstellen. Der Entwickler profitiert dadurch von einer besseren Standardisierung. Früher musste er, wenn er seine Schicht ORM änderte, auch seine Schicht [dao] ändern, die für die Kommunikation mit einer bestimmten ORM-Schicht geschrieben worden war. Nun schreibt er eine Schicht [dao], die mit einer Schicht JPA kommuniziert. Unabhängig davon, welches Produkt diese Schicht implementiert, bleibt die Schnittstelle der Schicht JPA, die der Schicht [dao] zur Verfügung gestellt wird, unverändert.

Dieses Dokument stellt Beispiele für JPA in verschiedenen Bereichen vor:

  • Zunächst befassen wir uns mit der Relational-Objekt-Brücke, die die Schicht ORM aufbaut. Diese wird mithilfe von Java-5-Annotationen für Datenbanken erstellt, in denen Beziehungen zwischen Tabellen vom Typ
    • Eins-zu-Eins
    • Eins-zu-Viele
    • mehrere zu mehreren

Zur Veranschaulichung dieses Bereichs werden wir folgende Testarchitekturen erstellen:

Unsere Testprogramme werden Konsolenanwendungen sein, die direkt auf die Schicht JPA zugreifen. Dabei werden wir die wichtigsten Methoden der Schicht JPA kennenlernen. Wir befinden uns in einer sogenannten „Java SE“-Umgebung (Standard Edition). JPA läuft sowohl in einer Java SE- als auch in einer Java EE5-Umgebung (Enterprise Edition).

  • Sobald wir sowohl die Konfiguration der Relational-Objekt-Brücke als auch die Verwendung der Methoden der JPA-Schicht beherrschen, werden wir zu einer klassischeren mehrschichtigen Architektur zurückkehren:

Der Zugriff auf die Schicht [JPA] erfolgt über eine zweischichtige Architektur aus [metier] und [dao]. Das Spring-Framework [7] sowie anschließend der Container EJB3 von JBoss werden verwendet, um diese Schichten miteinander zu verknüpfen.

Wir haben oben erwähnt, dass JPA in den Umgebungen SE und EE5 verfügbar ist. Die Java-Umgebung EE5 stellt zahlreiche Dienste im Bereich des Zugriffs auf persistente Daten bereit, darunter insbesondere Verbindungspools, Transaktionsmanager usw. Für Entwickler kann es interessant sein, diese Dienste zu nutzen. Die Java-Umgebung EE5 ist (Stand Mai 2007) noch nicht sehr verbreitet. Sie ist derzeit auf dem Sun Application Server 9.x (Glassfish) zu finden. Ein Anwendungsserver ist im Wesentlichen ein Webanwendungsserver. Wenn man eine eigenständige grafische Anwendung vom Typ Swing erstellt, kann man nicht auf die Umgebung EE und die damit verbundenen Dienste zurückgreifen. Das ist ein Problem. Es tauchen allmählich „Stand-alone“-Umgebungen wie EE und c.a.d auf. die außerhalb eines Anwendungsservers genutzt werden können. Dies gilt beispielsweise für JBos und EJB3, die wir in diesem Dokument verwenden werden.

In einer EE5-Umgebung werden die Schichten durch Objekte namens EJB (Enterprise Java Bean) implementiert. In früheren Versionen von EE galten die EJB (EJB, 2.x) als schwer zu implementieren, zu testen und manchmal als leistungsschwach. Man unterscheidet zwischen den „Entity“-EJB2.x und den „Session“-EJB2.x. Kurz gesagt: Ein EJB2.x „Entity“ entspricht einer Zeile in einer Datenbanktabelle, und ein EJB2.x „Session“ ist ein Objekt, das zur Implementierung der Schichten [metier], [dao] einer mehrschichtigen Architektur. Einer der Hauptkritikpunkte an den mit EJB implementierten Schichten ist, dass sie nur innerhalb von EJB-Containern verwendet werden können, einem Dienst, der von der EE-Umgebung bereitgestellt wird. Dies erschwert das Durchführen von Unit-Tests. So sind im obigen Schema würden Unit-Tests der Schichten [metier] und [dao], die mit EJB erstellt wurden, die Einrichtung eines Anwendungsservers erfordern – ein recht aufwendiger Vorgang, der den Entwickler nicht gerade dazu motiviert, häufig Tests durchzuführen.

Das Spring-Framework entstand als Reaktion auf die Komplexität von EJB2. Spring stellt in einer SE-Umgebung eine Vielzahl der Dienste bereit, die üblicherweise von EE-Umgebungen bereitgestellt werden. So stellt Spring im Bereich „Datenpersistenz“, der uns hier interessiert, die Verbindungspools und Transaktionsmanager bereit, die Anwendungen benötigen. Das Aufkommen von Spring hat die Kultur der Unit-Tests gefördert, deren Umsetzung auf einen Schlag wesentlich einfacher wurde. Spring ermöglicht die Implementierung der Schichten einer Anwendung durch klassische Java-Objekte (POJO, Plain Old/Ordinary Java Object), wodurch deren Wiederverwendung in einem anderen Kontext möglich wird. Schließlich integriert es zahlreiche Tools von Drittanbietern auf recht transparente Weise, insbesondere Persistenz-Tools wie Hibernate, iBatis, ...

Java EE5 wurde entwickelt, um die Mängel der vorherigen Spezifikation EE zu beheben. Die EJB und 2.x wurden zu EJB3. Dabei handelt es sich um POJOs, die mit Annotationen versehen sind, wodurch sie zu speziellen Objekten werden, wenn sie sich in einem EJB3-Container befinden. In diesem Container kann das EJB3 die Dienste des Containers nutzen (Verbindungspool, Transaktionsmanager usw.). Außerhalb des Containers EJB3 wird das EJB3 zu einem normalen Java-Objekt. Seine Annotationen EJB werden ignoriert.

Oben haben wir Spring und JBoss EJB3 als mögliche Infrastruktur (Framework) unserer mehrschichtigen Architektur dargestellt. Diese Infrastruktur stellt die von uns benötigten Dienste bereit: einen Verbindungspool und einen Transaktionsmanager.

  • Mit Spring werden die Schichten mithilfe von POJOs implementiert. Diese haben Zugriff auf die Dienste von Spring (Verbindungspool, Transaktionsmanager) durch Abhängigkeitsinjektion in diese POJOs: Beim Erstellen dieser Komponenten injiziert Spring ihnen Referenzen auf die Dienste, die sie benötigen werden.
  • JBoss EJB3 ist ein EJB-Container, der außerhalb eines Anwendungsservers betrieben werden kann. Seine Funktionsweise (aus Sicht des Entwicklers) entspricht der für Spring beschriebenen. Es gibt kaum Unterschiede.

  • Wir schließen das Dokument mit einem einfachen, aber dennoch repräsentativen Beispiel für eine dreischichtige Webanwendung ab:

1.2. Literaturhinweise

[ref1]: „Java Persistence with Hibernate“ von Christian Bauer und Gavin King, erschienen bei Manning.

[ref1] ist das Dokument, das als Grundlage für die folgenden Ausführungen diente. Es handelt sich um ein umfassendes, über 800 Seiten starkes Buch über die Verwendung von Hibernate in zwei verschiedenen Kontexten: mit oder ohne JPA. Die Verwendung von Hibernate ohne JPA ist in der Tat für Entwickler, die JDK 1.4 oder niedriger verwenden, nach wie vor aktuell, da JPA erst mit JDK 1.5 eingeführt wurde.

Nachdem ich mehr als drei Viertel des Buches gelesen und den Rest überflogen habe, ist mir klar geworden, dass alles in diesem Dokument nützlich ist. Der versierte Hibernate-Anwender dürfte fast alle Informationen kennen, die auf den 800 Seiten enthalten sind. Christian Bauer und Gavin King haben sich umfassend mit dem Thema befasst, ohne jedoch Situationen zu beschreiben, denen man niemals begegnen wird. Es lohnt sich, alles zu lesen. Das Buch ist didaktisch verfasst: Es ist deutlich erkennbar, dass nichts im Dunkeln gelassen werden soll. Die Tatsache, dass es für den Einsatz von Hibernate sowohl mit als auch ohne JPA geschrieben wurde, stellt eine Herausforderung für diejenigen dar, die sich nur für die eine oder die andere dieser Technologien interessieren. So beschreiben die Autoren beispielsweise anhand zahlreicher Beispiele die Relational-Objekt-Brücke in beiden Kontexten. Die verwendeten Konzepte sind sehr ähnlich, da sich JPA stark an Hibernate orientiert hat. Es gibt jedoch einige Unterschiede. So kann es vorkommen, dass etwas, das für Hibernate gilt, für JPA nicht mehr zutrifft, was letztendlich beim Leser Verwirrung stiftet.

Die Autoren zeigen Beispiele für dreischichtige Anwendungen im Kontext eines EJB3-Containers. Sie gehen nicht auf Spring ein. Anhand eines Beispiels werden wir sehen, dass Spring jedoch einfacher zu verwenden und umfassender ausgelegt ist als der in [ref1] verwendete Container JBoss EJB3. Dennoch ist „Java Persistence with Hibernate“ ein ausgezeichnetes Buch, das ich wegen all der Grundlagen, die man darin über die ORM lernt, empfehle.

Die Verwendung eines ORM ist für Anfänger komplex.

  • Man muss bestimmte Konzepte verstehen, um die Relational-Objekt-Brücke zu konfigurieren.
  • Da ist der Begriff des Persistenzkontexts mit seinen Objekttypen in den Zuständen „persistent“, „detached“ und „neue“
  • Es gibt die Mechanismen rund um die Persistenz (Transaktionen, Verbindungspools), in der Regel Dienste, die von einem Container bereitgestellt werden
  • Es müssen Einstellungen zur Leistungsoptimierung vorgenommen werden (Level-2-Cache).
  • ...

Wir werden diese Konzepte anhand von Beispielen vorstellen. Wir werden dabei nur wenige theoretische Ausführungen dazu anstellen. Unser Ziel ist es lediglich, dem Leser jedes Mal zu ermöglichen, das Beispiel zu verstehen und sich so anzueignen, dass er in der Lage ist, selbst Änderungen daran vorzunehmen oder es in einem anderen Kontext anzuwenden.

1.3. Verwendete Tools

Die Beispiele in diesem Dokument verwenden die folgenden Werkzeuge. Einige davon werden in den Anhängen beschrieben (Download, Installation, Konfiguration, Verwendung). In diesem Fall werden die Absatznummer und die Seitenzahl angegeben.

  • JDK 1.6 (Absatz 5.1)
  • das Java-Entwicklungstool IDE für Eclipse 3.2.2 (Absatz 5.2)
  • Eclipse-Plugin WTP (Web Tools Package) (Absatz 5.2.3)
  • Eclipse-Plugin SQL Explorer (Abschnitt 5.2.6)
  • Eclipse-Plugin „Hibernate Tools“ (Abschnitt 5.2.5)
  • Eclipse-Plugin TestNG (Abschnitt 5.2.4)
  • Tomcat 5.5.23 Servlet-Container (Abschnitt 5.3)
  • SGBD Firebird 2.1 (Absatz 5.4)
  • SGBD MySQL5 (Absatz 5.5)
  • SGBD PosgreSQL (Absatz 5.6)
  • SGBD Oracle 10g Express (Absatz 5.7)
  • SGBD SQL Server 2005 Express (Absatz 5.8)
  • SGBD HSQLDB (Absatz 5.9)
  • SGBD Apache Derby (Abschnitt 5.10)
  • Spring 2.1 (Abschnitt 5.11)
  • EJB3-Container von JBoss (Abschnitt 5.12)

1.4. Beispiel- en herunterladen

Auf der Website dieses Dokuments können die behandelten Beispiele als ZIP-Datei heruntergeladen werden, die nach dem Entpacken den folgenden Ordner erzeugt:

  • in [1]: die Verzeichnisstruktur der Beispiele
  • in [2]: Der Ordner <annexes> enthält Elemente, die im Abschnitt ANNEXES, Absatz 5, vorgestellt werden. Insbesondere enthält der Ordner <jdbc> die JDBC-Treiber aus SGBD, die für die Beispiele des Tutorials verwendet werden.
  • in [3]: Der Ordner <lib> fasst die verschiedenen .jar-Dateien, die im Tutorial verwendet werden, in 5 Ordnern zusammen
  • in [4]: Der Ordner <lib/divers> enthält die folgenden Archive: - die JDBC-Treiber von SGBD - des Unit-Test-Tools [testNG] - des Protokollierungstools [log4j]
  • in [5]: die Archive der Hibernate-Implementierung JPA/Hibernate und der für Hibernate erforderlichen Tools von Drittanbietern
  • in [6]: die Archive der Implementierung JPA/Toplink
  • in [7]: die Archive von Spring (2.x) und der für Spring erforderlichen Tools von Drittanbietern
  • in [8]: die Container-Archive EJB3 und JBoss
  • in [9]: Der Ordner <hibernate> enthält die Beispiele, die mit der Persistenzschicht JPA/Hibernate bearbeitet wurden
  • in [10]: Der Ordner <hibernate/direct> enthält die Beispiele, bei denen die Schicht JPA direkt mit einem Programm vom Typ [Main] genutzt wird.
  • in [11] und [12]: Beispiele, bei denen die Schicht JPA über die Schichten [metier] und [dao] in einer mehrschichtigen Architektur genutzt wird, was dem normalen Einsatzfall entspricht. Die Dienste (Verbindungspool, Transaktionsmanager) , die von den Schichten [metier] und [dao] genutzt werden, werden entweder von Spring [11] oder von JBoss, EJB3 und [12] bereitgestellt.
  • in [13]: Der Ordner <toplink> enthält die Beispiele aus dem Ordner <hibernate> [9], diesmal jedoch mit einer Persistenzschicht JPA/Toplink anstelle von JPA/Hibernate. Esgibt es in [13] keinen Ordner <jbossejb3>, da es nicht möglich war, ein Beispiel zum Laufen zu bringen, bei dem die Persistenzschicht von Toplink und die Dienste vom Container EJB3 von JBoss bereitgestellt werden.
  • In [14]: Ein Ordner <web> enthält drei Beispiele für Webanwendungen mit einer Persistenzschicht JPA:
  • [15]: Ein Beispiel mit Spring / JPA / Hibernate
  • [16]: dasselbe Beispiel mit Spring / JPA / Toplink
  • [17]: dasselbe Beispiel mit JBoss, EJB3 und JPA / Hibernate. Dieses Beispiel funktioniert nicht, wahrscheinlich aufgrund eines ungeklärten Konfigurationsproblems. Es wurde dennoch beibehalten, damit sich der Leser damit befassen und möglicherweise eine Lösung für dieses Problem finden kann.

Das Tutorial bezieht sich häufig auf diesen Verzeichnisbaum, insbesondere beim Testen der behandelten Beispiele. Der Leser wird gebeten, diese Beispiele herunterzuladen und zu installieren. Im Folgenden bezeichnen wir den oben beschriebenen Verzeichnisbaum der Beispiele als <Beispiele>.

1.5. Konfiguration der Eclipse- -Projekte für die Beispiele

Die Beispiele verwenden „Benutzer“-Bibliotheken. Dabei handelt es sich um .jar-Archive, die unter einem gemeinsamen Namen zusammengefasst sind. Wenn eine solche Bibliothek in den classpath eines Java-Projekts aufgenommen wird, werden alle darin enthaltenen Archive in diesen Classpath aufgenommen. Sehen wir uns an, wie dies in Eclipse funktioniert:

  • in [1]: [Window / Preferences / Java / Buld Path / User Libraries]
  • in [2]: Man erstellt eine neue Bibliothek
  • in [3]: Man gibt ihr einen Namen und bestätigt
  • in [4]: Wir wählen die JAR-Dateien aus, die Teil der Bibliothek sein sollen [jpa-divers]
  • in [5]: Wir wählen alle JAR-Dateien aus dem Ordner <exemples>/lib/divers aus
  • in [6]: Die Benutzerbibliothek [jpa-divers] wurde definiert
  • in [7]: Wir wiederholen den Vorgang, um vier weitere Bibliotheken zu erstellen:
Bibliothek
Verzeichnis der Bibliotheksdateien
jpa-hibernate
<Beispiele>/lib/hibernate
jpa-toplink
<Beispiele>/lib/toplink
jpa-spring
<Beispiele>/lib/spring
jpa-jbossejb3
<Beispiele>/lib/jbossejb3