Services – mit Spring und JPA Implementierungen

Montag, 24. Oktober 2011 von  
unter Fachartikel Architektur

Services mit Spring und JPA realisieren – die Basics

JPA Grundlagen:

Dies beinhaltet JPA Basics wie JPA Implementierungen, wie Entities aufgebaut sind (mit einem Entity Bspl., der “Customer“ Entity), die Zustände einer Entity und der Entity Lifecycle, die Persistierung der Entities mit und ohne JavaEE Unterstützung (Application Server), der EntityManager und seine Methoden, Optimistic und Pessimistic Locking Mechanismen, Details zur EntityManager Konfiguration mittels Persistence Unit und zu XML Mappings.

Die Roots der JPA (“Java Persistence API“) liegen innerhalb der JSR 220 als Vereinfachung der Persistence für EJB 3.0 und wurden zur Standard Persistence Technology für simple POJOs (“Plain Old Java Objects“) erweitert, wobei JPA sowohl auf JavaEE Servern und in den entsprechenden Umgebungen einsetzbar ist, als auch in JavaSE Applikationen, also, im Gegensatz zu EJBs, auch ohne Application Server funktioniert.

Waren die Kernpunkte von JPA 1.0 noch die Spezifikation eines Persistence Modells für POJOs, vereinheitlichte O/R XML Mappings und deren äquivalente Annotations, Unterstützung der üblichen Entwurfsstrategien (Polymorphie, Vererbung, etc.) u. Entwurfsmuster-Bestandteile (Komposition, Assoziationen, etc.), Verwendbarkeit verschiedener Persistenz-Provider, sowie die verbesserte Query Sprache “JPQL“, so waren es im JPA 2.0 die Criteria API, eine Bean Validation (vgl. Spring/Hibernate), verbesserte XML Mappings (z.B. für kaskadierendes Löschen) und die entsprechenden Annotations, als auch weitere Properties für Caches, Persistence Provider et al.

Implementierungen von JPA 2.0 sind z.B. Kodo (BEA Persistenz Framework, EJB 3.0 Implementierung für Weblogic), Open JPA (Apaches Open Source Implementierung, EJB 3.0 Implementierung für Geronimo), Hibernate (JBoss Group Persistenz Framework mit über JPA hinausgehenden Funktionalitäten, EJB 3.0 Implementierung für Geronimo), Toplink Essentials (Sun JPA Referenz-Implementierung, Bestandteil von GlassFish).

Eine Entity ist in JPA ein simples POJO, welches natürlich von anderen POJOs erben kann bzw. Interfaces implementieren kann und das Serializable Interface (z.B. für Primary Key Ids, oder Transfer Objects) implementieren kann. Jede Entity muß irgendein Primary Key Attribut haben (z.B. eine Id), damit Rows (desselben Primary Keys) in der entsprechenden DB-Tabelle, z.B. für die C-R-U-D (Create, Read, Update, Delete) Operationen auf dieser Tabelle, mit dem Entity Object matchen können, zwecks Suchen, Finden, Verknüpfen und dem Caching von Datensätzen.

Formale Anforderungen an eine Entity-Definition ist deren Annotation mit der @Entity Annotation und die Annotation des Primärschlüssel-Attributs mit der @Id Annotation. Danach gelten die Default Persistence-Mechanismen für JPA Entities nach denen alle Attribute der Entity persistiert werden, der Class Name dem Table Name entspricht und jeder Attributname einem Column Name in der entsprechenden Tabelle entspricht. Hier die Implementierung einer simplen Beispiel Entity “Customer“.

Der Entity Lifecycle ist anhand des Zustandsdiagramms einer Entity schnell erklärt. Die Zustände einer Entity entsprechen den annotierten Entity Methoden, welche (analog dem Einsatz von Pointcuts und Joinpoints bei AOP) vor bzw. nach der Ausführung einer bestimmten Operation (hier: einer C-R-U-D DB-Operation) als Event Handler Methoden aufgerufen werden, falls sie noch implementiert werden müssen, wenn dies noch nicht in einer sinnvollen Basisklasse geschah. Die per Annotationen @PrePersist, @PostPersist, @PreRemove, @PostRemove, @PreUpdate, @PostUpdate annotierten Methoden werden dann innerhalb des Entity Lifecycles aufgerufen. In den Methoden mit Prefix “Post“, die nach einer erfolgten DB-Änderung aufgerufen werden, können z.B. die noch nicht commiteten Änderungen per Rollback zurückgerollt werden, oder ein Log-Datei Eintrag über die erfolgte Datensatz-Änderung geschrieben werden, wie im folgenden Beispiel hier, wobei eine Stateful Session Bean als DAO verwendet wird, mit den Attributen Customer, dem EntityManager mit dem injecteten PersistenceContext und einem zu loggenden LogEntry.

Vergleicht man den Code und die verwendeten Klassen mit den bei Hibernate 3.x und Hibernate 4.x verwendeten, so entspricht die EntityManagerFactory (JPA) der HibernateSessionFactory und der EntityManager (JPA) der HibernateSession. Entsprechend kann ein JPA Beispiel zur Speicherung eines Customers ohne JavaEE Umgebung (Application Server) implementiert werden.

Hier das EntityManager API (JPA 1.0). Und hier die zusätzlichen Neuerungen im EntityManager API (JPA 2.0).
Die PersistenceUnit hat den Namen der standardisierten JPA Konfiguration bzw. deren Implementierung (z.B. Hibernate, EJB 3.x, etc.) für den EntityManager, damit dieser von der EntityManagerFactory (BeanFactory für den EntityManager) allokiert und mit den konfigurierten Attributen u. Properties instanziiert werden kann, bevor er in die entsprechende SFSB (Stateful Session Bean) als DAO Pattern Implementierung oder die entsprechende SLSB (Stateless Session Bean) als Facade Pattern Implementierung per DI (Dependency Injection) injectet werden kann.

Dabei verwendet die EntityManagerFactory zur Konfiguration des EntityManagers die erste in der persistence.xml auffindbare Unit, falls der Parameter unitName fehlt. Properties, welche in der persistence.xml typischerweise definiert werden, sind die eingesetzte JPA Version, der Name der PersistenceUnit, die Art der eingesetzten Transaktionskontrolle (RESOURCE_LOCAL, z.B. für einen DB-lokalen TranactionManager oder JTA für einen JTA-TransactionManager), die Implementierungs Class des Persistence Providers der Engine der jeweiligen JPA Implementierung, den JNDI-Namen der benötigten DataSource, evtl. die Namen benötigter Mapping Dateien für Entities und Queries und das .jar File mit den darin enthaltenen Klassen, evtl. weitere Properties, wie z.B. den Log Level oder die Cache Size eines Logging Providers. Hier nun eine typische persistence.xml mit einer typischen PersistenceUnit. Die Engine typischen Properties der JPA Implementierung werden zweckmäßigerweise, z.B. bei Hibernate in eine hibernate.cfg.xml ausgelagert, hier ein Beispiel, können aber auch direkt in die persistence.xml eingetragen werden.

Per JPA 2.0 Spezifikation standardisierte Properties Namen sind javax.persistence.lock.timeout (als Pessimistic Locking Timeout eines Locks), javax.persistence.query.timeout (als Timeout eine Query), javax.persistence.jdbc.driver (für den vollqualifizierten Klassennamen des konfigurierten DB-Treibers), javax.persistence.jdbc.user (für den Benutzernamen) und javax.persistence.jdbc.password (für das Passwort).

Auf einige besondere Elemente der PersistenceUnit soll kurz eingegangen werden. Wie im Beispiel zu sehen war, können Entity Mappings in XML Dateien ausgelagert werden, falls dafür keine Annotationen verwendet werden sollen, dasselbe gilt für (named und native) Queries. Ebenso ist die direkte Einbindung von Persistenz Klassen möglich oder die Einbindung derjenigen .jar Dateien, welche die benötigten Entity Klassen enthalten, wobei es zusätzlich die Möglichkeit gibt, nicht explizit aufgeführte Klassen zu excludieren. In extra Mapping Dateien (diese im META-INF-Verzeichnis der eingebundenen .jar Dateien positionieren) deklarierte Entities oder annotierte Entity Klassen ohne explizite Deklarations-Dateien sollten dann im Wurzelverzeichnis bzw. im classpath der Applikation veröffentlicht jederzeit auffindbar sein.

Als Alternative zur Verwendung von Annotations für die JPA Entities können in der persistence.xml entsprechende PersistenceUnit Defaults, globale Properties für alle Entity-Mappings und auch für Entities definiert werden. Hier ein Beispiel dazu.

Unter dem PersistenceContext wird der Cache aller in diesem Kontext verwendeten Entities verstanden, weshalb er oft direkt mit der laufenden Datenbank Transaktion gekoppelt ist, die darin enthaltenen Entities werden auch als “Managed Entities“ bezeichnet. Der Aufruf von drei verschiedenen EntityManager Methoden fügt eine Entity dem PersistenceContext hinzu: entweder wurde eine neue Entity soeben persistiert mittels em.persist(customer), oder eine existierende Entity wurde per Query gesucht und gefunden mittels em.find(Customer.class, 4711), oder aus einem detachten Object wurde die Entity mittels em.merge(customer) in den PersistenceContext aufgenommen, d.h. sie wurde zu einer Managed Entity.
Ein PersistenceContext existiert dabei jeweils auf Basis einer (stateful oder stateless) Session Bean, wobei über das LocalInterface referenzierte und wird zwecks Konfiguration über die @PersistenceContext(type=…) Annotation dem EntityManager von der EntityManagerFactory zur Verfügung gestellt, wobei in Stateful Session Beans per @EJB Annotation wiederum injectete EJBs den PersistenceContext der Stateful Session Bean erben können und somit in derselben laufenden Transaktion (z.B. JTA Transaction) verwendet werden können, hierzu ein Beispiel. Für die Definition des PersistenceContexts gibt es zwei Werte für das type Attribut, type=“PersistenceContext.TRANSACTION“ bedeutet, dass je Anwendungstransaktion ein neuer PersistenceContext generiert wird und nach Beendung der laufenden Transaktion (durch commit() oder rollback()) die verwendeten Entity Objekte detacht werden. Der type= “PersistenceContext.EXTENDED“ bedeutet, dass exklusiv ein PerssistenceContext für eine beliebige Anzahl von Anwendungstransaktionen verwendet wird und die Entity Objekte nach Beendung der Transaktion nicht detacht werden, nur durch Aufruf einer mit @Remove annotierten Methode wird der Context ungültig und ausgelagert. Zu beachten ist hierbei noch, dass über das Local Interface aufgerufene Stateful Session Beans ebenfalls immer den PersistenceContext der aufrufenden Bean erben.

Zum Aufbau einer Entity Klasse ist noch anzumerken, dass die zu Beginn genannten Lifecycle Methoden, wie z.B. die Methoden, welche mit den @PrePersist oder @PostPersist Annotationen annotiert wurden, sinnvollerweise in eine Listener Klasse ausgelagert werden, deren Name das Suffix “CallbackListener“ haben muß, damit die Entity Customer anschließend per Annotation alle Lifecycle Callback Methoden erben kann per @EntityListeners(CustomerCallbackListener.class) Annotation auf Class Ebene.

Die gängigen Properties für die Hibernate JPA Implementierung finden sich hier. Ebenfalls detailliertere Informationen zu den verfügbaren Caches in Hibernate gibt es hier. Und Informationen über die Eigenschaften des häufig für Hibernate verwendeten EhCache sind hier ebenfalls zu finden.

Als Novum gibt es in JPA 2.0 Implementierungen nun folgende Second Level Caching Konfigurationsmöglichkeiten in der persistence.xml. Hierfür wird in der für den EntityManager relevanten PersistenceUnit das Element “shared-cache-mode“ mit dem entsprechenden Wert konfiguriert. Zur Auswahl gibt es den Wert ALL, um alle Entity Klassen zu cachen, den Wert NONE um keine Entity Klasse zu cachen, den Wert ENABLE_SELECTIVE, um nur die Klassen zu berücksichtigen, die zum Caching freigegeben wurden, den Wert DISABLE_SELECTIVE, um nur die Klassen zu cachen, die nicht vom Caching ausgenommen wurden und den Wert UNSPECIFIED für eine rein JPA Provider-gesteuerte Caching Strategy. Die Entity muß mittels Annotation @Cacheable(true) auf Class Ebene für das Caching mittels ENABLE_SELECTIVE markiert werden bzw. muß mittels Annotation @Cacheable(false) auf Class Ebene für das Caching mittels DISABLE_SELECTIVE markiert werden.

Auf die Entity Annotationen und Queries, Primary Key Strategien und Relationen zwischen Entities, sowie CMT u. BMT, also Transaktionen mit JPA 2.0 wird in einem weiteren Blog Eintrag eingegangen. Das Ziel der weiteren Erklärungen ist nun, einen Weg aufzuzeigen, um möglichst direkt einen eigenen WebService mit Hilfe einer JPA Stateless Session Bean zu erstellen, die Teil einer JavaEE Applikation ist, welche auf einem Application Server läuft.

WebServices Grundlagen:

Es ist naheliegend, die Methoden einer Stateless Session Bean auch als WebService anzubieten. Der JavaEE Standard sieht dies für Stateless Session Beans vor, deren Methoden aus dem Remote Interface als WebService Methoden definiert werden können. Bei EJB 2.1 waren die Aufwendungen hierfür noch etwas größer, haben sich durch die Unterstützung von Annotations inzwischen aber auf ein Minimum reduziert.
Es gibt WebServices im RPC und im DOCUMENT Style, wobei letztere im JavaEE Standard nicht erwähnt werden, obwohl sie moderner sind, als die RPC WebServices. DOCUMENT Style WebServices erweitern den Methodenaufruf, indem bei ihnen nicht einzelne Parameter, sondern ein XML Document übergeben wird, welches von der aufgerufenen Methode ausgewertet wird. Auch der Rückgabewert ist ein XML Document mit dem noch komplexere Informationen ausgetauscht werden können, wobei der Aufruf einer Servicemethode auch eine Menge von Ergebnissen liefern kann, anstatt nur einen einzelnen Wert.

SOAP:

SOAP ist nun ein Protokoll für die WebService-Methodenaufrufe, ähnlich wie DCOM oder IIOP von CORBA, mit dem wichtigen Unterschied, dass es sich dabei immer um eine XML-Datei handelt, die entweder manuell erstellt wurde, oder aber besser automatisch generiert werden sollte. Soll eine WebService Methode aufgerufen werden, erzeugt der Client eine SOAP Datei, in welcher der Name der aufzurufenden Methode und ihre Parameter enthalten sind. Der Server interpretiert die SOAP-Nachricht, ruft die gewünschte Methode mit den übergebenen Parametern auf und liefert dann das Ergebnis wieder als SOAP-Nachricht an den Client zurück. Dabei ist es nicht nötig, die SOAP-Nachricht selbst aufzubauen oder zu interpretieren. Dafür gibt es fertige Java Bibliotheken, wie JAX-RPC und JAX-WS. Es ist aber für das Verständnis von WebServices gut, sich mit SOAP auseinanderzusetzen, und es wird deutlich, dass diese Response auch von beliebigen anderen SOAP-Clients gelesen werden kann.

Das Remote Interface der als WebService angebotenenen Stateless Session Bean sieht man nun hier.

Dabei kommt der Parameter endpointInterface nur zum Einsatz, wenn man für die WebService Methode ein Interface erzeugt, welches dann von der eigentlichen WebService Bean (SLSB) implementiert wird. Ein solches WebService Interface mit der per @WebService Annotation annotierten Methode validCreditCard(String number, String provider) sieht man hier.

Die Bean Klasse, mit der eigentlichen Implementierung sieht man hier. Auch die implementierende Methode verfügt über die Annotation @WebService, muß sich allerdings zusätzlich mittels Parameter endpointInterface auf ihr WebService Interface beziehen.

Zur praktischen Umsetzung von SOAP- und REST Webservices (mit oder ohne Spring) kann ebenfalls auf bereits existierende Blog-Einträge in diesem Blog verwiesen werden.

Die Deklaration und Bean Definitionen per Spring Application Context XML Datei und die Verwendung der @Transactional Annotation auf JPA- und Hibernate-DAO Ebene erklärt der bereits vorhandene Artikel „Spring – Mapper Templates und Transaktions Manager nutzen“ in diesem Blog. Hier ein paar praktische Beispiele und auch hier.

Die praktische Anwendung von WebServices (REST und SOAP) mit Spring erklärt der bereits vorhandene Artikel „Services – mit Spring, JAX-RS und JAX-WS“ in diesem Blog.

Einige praktische Erklärungen zu Service-Architekturen mit Spring, Hibernate als JPA Implementierung und WebServices finden sich hier in diesem Blog.

Alle in diesem Blog-Beitrag verwendeten Beispiel finden sich übrigens hier.

Quelle dieses Blog Eintrags sind die bei SpringSource frei verfügbare Spring Referenz, das Buch “Pro JPA 2“ aus dem Apress-Verlag, das Buch „Spring im Einsatz“ von Craig Walls aus dem Hanser Verlag, das Buch “Spring In Practice”, das Buch „Hibernate” von Sebastian Hennebrüder, das Buch „Spring & Hibernate” aus dem Hanser Verlag, sowie eigene Erfahrungen mit dem Spring Framework, mit SOAP und REST, mit dem HibernateTemplate, ORM-Mappern, mit HibernateDaoSupport und JpaDaoSupport Klassen und mit Transaktionen.

Kommentare

4 Kommentare zu “Services – mit Spring und JPA Implementierungen”

  1. Von Services – mit Spring, JPA und SOAP : binaris informatik GmbH am Mittwoch, 9. November 2011 02:20

    […] Annotationen lauten @PrePersist, @PostPersist, @PreRemove, @PostRemove, hierzu wird auf den Blog-Eintrag “Services – mit Spring und JPA Implementierungen“ in diesem Blog verwiesen, wo die solcherart annotierten Lifecycle Methoden schonmal erläutert […]

  2. Von Eclipse RCP – Datenbank Viewer mit Swing, JPA : binaris informatik GmbH am Freitag, 30. November 2012 17:33

    […] angeht, existieren dazu in diesem Blog bereits mehrere Artikel bzw. Blog-Einträge, z.B. hier, hier, hier, und hier, weshalb hier nur auf das sehr gute “Pro JPA 2“-Buch aus dem […]

  3. Von Services – mit Hibernate 4, Vaadin 6, JBoss 7 : binaris informatik GmbH am Freitag, 28. Februar 2014 22:12

    […] gestartet werden können) zur Unterstützung von Persistenz-Frameworks, wie z.B. Hibernate. Über Services mit JPA und Hibernate und Service-Architekturen mit Hibernate gibt es bereits Blog-Einträge in diesem Blog. Da Hibernate […]

  4. Von Architekturen – mit Spring CDI, JPA, JBoss, JavaEE : binaris informatik GmbH am Dienstag, 1. Juli 2014 22:38

    […] Hibernate und JPA gibt es bereits ein paar Blog-Einträge in diesem Blog hier, hier, und […]

Einen Kommentar hinzufügen...

Sie müssen registriert und angemeldet sein um einen Kommentar zu schreiben.