Polyglotte Persistenz als Paradigma

EclipseLink als Persistenzframework für OSGi-Enterprise-Anwendungen

Alexander Grzesik, Bob Dannehl

© iStockphoto / mevans

In Enterprise-Anwendungen kann es sinnvoll sein, verschiedene Datenbanktechnologien für bestimmte Einsatzzwecke zu nutzen. Hierfür hat Martin Fowler schon 2011 den Begriff „Polyglotte Persistenz“ geprägt. Doch wie kann man NoSQL-Technologien einfach in seine Anwendung einbinden, um von den Vorteilen polyglotter Persistenz zu profitieren? Mit EclipseLink und OSGi hat man die Möglichkeit, dies unter Nutzung bekannter Konzepte wie JPA einfach und effektiv zu erreichen. Dieser Artikel ist der erste einer dreiteiligen Serie zur Programmierung von Enterprise-OSGi-Anwendungen und zeigt am Beispiel einer kleinen Anwendung, wie die polyglotte Persistenz für diese Anwendungen genutzt werden kann.

Viele Jahre war klar: Wer Persistenz innerhalb einer Enterprise-Anwendung benötigt, verwendet eine relationale Datenbank. Doch die rasante Entwicklung gerade der Cloud-Technologien in den letzten Jahren hat neuen Konzepten wie den NoSQL-Datenbanken einen erheblichen Schub gegeben. Für bestimmte Anwendungsgebiete eignen sich derartige Technologien mittlerweile erheblich besser als klassische relationale Datenbanken.

Artikelserie
Teil 1: EclipseLink als Persistenzframework für OSGi-Enterprise-Anwendungen
Teil 2: Anwendungsentwicklung: Backend-Implementierung mit OSGi Services, Nutzung von OSGi Services, Rich Web Applications
Teil 3: Test und Deployment: Provisionierung auf dem Karaf, Integrationstests

Als wir bei der Cloudyle GmbH mit der Entwicklung der PaaS+-Cloud-Plattform begannen, stellte sich die Frage, welche Persistenztechnologie unterstützt werden soll. Schnell wurde klar, dass relationale Datenbanken allein gerade im Cloud-Umfeld nur bedingt geeignet sind. Das CAP-Theorem besagt, dass in verteilten Datenbanksystemen nur zwei von drei Anforderungen nach Konsistenz (Consistency), Verfügbarkeit (Availability), Ausfalltoleranz (Partition Tolerance) erreichbar sind. Hinzu kommt, dass ein relationales Datenmodell relativ starr ist und sich daher nur schlecht für variable Datenstrukturen eignet. Spezialisierte NoSQL-Technologien können je nach Anforderungen eine deutlich bessere Wahl als Persistenzschicht für eine Anwendung sein. Besonders interessant wird es, wenn sich Anforderungen am besten durch Kombination von unterschiedlichen Technologien lösen ließen. Genau hier setzt das Konzept der polyglotten Persistenz an. Dabei werden verschiedene Persistenztechnologien in einer Anwendung kombiniert, um alle Anforderungen optimal abzudecken.

Abb. 1: Polyglotte Persistenz

Abb. 1: Polyglotte Persistenz

So ist beispielsweise für die Daten der Buchhaltung und des Berichtswesens eine transaktionale relationale Datenbank sinnvoll, während klinische Daten mit variablen Eigenschaften besser in einer dokumentenbasierten NoSQL-Datenbank wie MongoDB verwaltet werden. Für Logs wiederum bietet sich ein Key-Value-Store mit performanten Suchfunktionen wie Elasticsearch an (Abb. 1).

Die Herausforderung besteht nun darin, dass jede dieser Datenbanken unterschiedliche APIs besitzt und man daher alle Schnittstellen entsprechend implementieren muss. Es wäre deutlich einfacher, wenn stattdessen eine einheitliche Schnittstelle, idealerweise sogar die JPA-Spezifikation genutzt werden könnte.

JPA reloaded

Mit der JPA-Spezifikation gibt es in Java schon seit Langem einen Standard für das Mapping von relationalen Daten auf Java-Objekte. Es gibt eine Reihe von Frameworks, die die JPA-Spezifikation für relationale Datenbanken umsetzen, zu den bekanntesten gehören Hibernate, OpenJPA und EclipseLink. Letzteres enthält einige interessante Features, die bei der Nutzung polyglotter Persistenz in einer Enterprise-OSGi-Anwendung hilfreich sein können. EclipseLink ist OSGi-fähig und unterstützt seit der Version 2.4 nicht nur die Nutzung von JPA mit relationalen Datenbanken, sondern auch ausgewählte NoSQL-Datenbanken wie MongoDB oder Oracle NoSQL. Hierbei ist zu beachten, dass die NoSQL-Integration aufgrund der technologischen Unterschiede zwischen den Datenbankplattformen nur einen Teil der JPA-Funktionalität abbilden kann. So unterstützt MongoDB z. B. keine Transaktionen oder Fremdschlüssel über mehrere Entitäten. Die Definition der Entitäten erfolgt dabei durch die gewohnten JPA-Annotationen mit einigen zusätzlichen NoSQL-spezifischen Erweiterungen.

Anhand eines einfachen Pet-Clinic-Beispiels werden nun die Möglichkeiten der Nutzung einer MongoDB über JPA gezeigt. Dabei wird eine einfache Verwaltungsanwendung für eine Tierklinik umgesetzt. Die Datenhaltung erfolgt in einer MongoDB, die über JPA in der Anwendung verwendet wird. Wie Listing 1 zeigt, kommt in der Definition einer Entity nur die @NoSql Annotation hinzu, der restliche Code entspricht dem einer SQL-Entität inklusive der Möglichkeit, Queries in JQL zu definieren.

@Entity
@NoSql(dataFormat = DataFormatType.MAPPED, dataType = "pets")
@NamedQueries({ @NamedQuery(name = "Pet.getByName",
                query = "Select p from Pet p where p.name like :name") })
public class Pet extends NamedEntity {
  @Field(name = "birth_date")
  @Temporal(TemporalType.DATE)
  private Date birthDate;

  @Basic
  private String type;

  @ManyToOne
  @JoinColumn(name = "owner_id")
  private Owner owner;

  @ElementCollection
  private Set<Visit> visits;
    }

Es kann somit wie gewohnt der JPA-Entity-Manager für den Zugriff auf die Datenbank genutzt werden. In der MongoDB wird in diesem Beispiel automatisch eine Collection „pets“ erzeugt und die Entity-Instanzen werden als Dokumente im JSON-Format abgelegt (Listing 2).

{
 "_id" : "54FD977B79D8DC5A56B9ABF2",
  "VERSION" : NumberLong(1),
  "birth_date" : ISODate("2015-03-08T23:00:00.000Z"),
  "name" : "Aunty",
  "owner_id" : "54FD977B79D8DC5A56B9ABF1",
  "TYPE" : "Dog",
  "VISITS" : [ 
    {
      "visit_date" : ISODate("2015-03-09T12:52:11.334Z"),
      "description" : "Illness of Aunty",
    }       ] 
}

Mit der @ElementCollection Annotation können eingebettete Dokumente erzeugt werden, wie die Visits Collection im Beispiel. Die Nutzung des JPA-API bietet neben dem Mapping auf Java-Entities eine Reihe von Vorteilen gegenüber des nativen MongoDB-API, z. B. Optimistic Locking, Referenzen auf andere Entitäten (über ManyToX- und OneToX-Beziehungen) und die Nutzung von Queries über JPA. Um einer JPA Persistence Unit (PU) mitzuteilen, dass eine MongoDB-Datenbank verwendet werden soll, reichen einige zusätzliche Parameter in der persistence.xml (Tabelle 1).

Property Wert
eclipselink.target-database org.eclipse.persistence.nosql.adapters.mongo.MongoPlatform
eclipselink.nosql.connection-spec org.eclipse.persistence.nosql.adapters.mongo.MongoConnectionSpec
transaction-type RESOURCE_LOCAL

Tabelle 1: NoSQL Properties für EclipseLink

Multikulti

Neben der einfachen Nutzung von NoSQL-Datenbanken über JPA bringt EclipseLink aber noch ein weiteres Feature mit, um polyglotte Persistenz zu realisieren. Mithilfe einer so genannten Composite Persistence Unit kann man mehrere JPA Persistence Units, so genannte Member PUs, zu einer virtuellen PU gruppieren (Abb. 2). Die Composite PU stellt dabei einen Entity-Manager bereit, der Zugriff auf alle Entitäten der Member PUs ermöglicht. Der Clou dabei ist, dass man Beziehungen zwischen Entitäten in unterschiedlichen Persistence Units erstellen kann und diese automatisch durch EclipseLink aufgelöst werden. So entsteht mithilfe der Entity-Definitionen ein Metamodell, das sich über mehrere Datenbanken erstreckt.

Abb. 2: Composite PU

Abb. 2: Composite PU

Ob es sich bei einer Persistence Unit um eine Composite PU handelt, wird über das Property eclipselink.composite-unit=true in der PU-Konfiguration bestimmt. Die Member PUs werden in die Composite PU als JAR-Dateien eingebettet und über entsprechende Tags in der persistence.xml referenziert (Listing 3). Der Transaktionstyp der Composite PU gilt dabei für alle Member PUs. Wenn alle eingebetteten Persistence Units Transaktionen unterstützen, sind diese datenbankübergreifend verfügbar, andernfalls werden unabhängige Transaktionen erstellt.

<persistence xmlns=http://java.sun.com/xml/ns/persistence ...>
  <persistence-unit name="composite-pu" transaction-type="RESOURCE_LOCAL">
      <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
      <jar-file>relational-pu.jar</jar-file>
      <jar-file>nosql-pu.jar</jar-file>
      <properties>
    <property name="eclipselink.composite-unit" value="true" />

Integration in die OSGi-Welt

Bis jetzt haben wir gesehen, wie polyglotte Persistenz mit EclipseLink genutzt werden kann. Aber JPA ist eine Java-EE-Spezifikation, wie kann dies in einer OSGi-Anwendung genutzt werden? Der OSGi-Standard beschreibt in der JPA-Service-Spezifikation, wie JPA-Persistenz als OSGi-Service genutzt werden kann (siehe: OSGi Enterprise Spezifikation R5, Abschnitt 127). Dazu muss ein so genanntes Persistence Bundle erstellt werden. Es unterscheidet sich von einem normalen Bundle durch eine integrierte persistence.xml und den zusätzlichen Tag Meta Persistence im Bundle-Manifest, das darauf verweist. Es gibt mehrere Implementierungen der JPA-Service-Spezifikation, z. B. die Referenzimplementierung Eclipse Gemini JPA und Apache Aries JPA. Unser Beispiel nutzt Aries JPA, da Gemini JPA einige gravierende Einschränkungen hat und Aries hervorragend in Apache Karaf integriert ist – einen leichtgewichtigen und flexiblen OSGi-Server, der in der vorherigen Ausgabe ausführlich vorgestellt wurde. Der Aries-JPA-Provider erkennt automatisch alle Persistence Bundles, die im Container bereitgestellt werden. Für jede Persistence Unit wird eine EntityManagerFactory als OSGi Service bereitgestellt. Die Blueprint-Spezifikation ist ein Spring-ähnliches Komponentenmodell für OSGi. Mit der Blueprint-Erweiterung von Aries JPA kann man sich die EntityManagerFactory mithilfe des Tags jpa:unit in seine DAO-Klasse injizieren. Mit dem Aries-JPA-Container-Management kann man die Erstellung und Verwaltung des Entity-Managers auch vom Framework übernehmen und Transaktionen über JTA steuern lassen. Dazu nutzt man analog das jpa:context-Tag, Listing 4 zeigt ein entsprechendes Beispiel. Nun kann man die gewohnten JPA-Funktionen in seinem OSGi-Modul nutzen. Für NoSQL oder Composite PUs muss man auf ein automatisches Transaktionsmanagement verzichten, das heißt, Entity-Manager erstellen, eine lokale Transaktion starten, die eigentlichen Operationen auf dem Entity-Manager ausführen, Commit vornehmen, bei Fehlern ein Rollback usw. Das ist eine Menge Glue-Code, daher bietet es sich an, die Persistenzlogik mithilfe des Domain-Store-Patterns zu abstrahieren. Diesen Weg sind wir bei der Entwicklung des PaaS+-Enterprise-API gegangen und kapseln den JPA-Code wie die EntityManager-Erstellung oder Commit- und Rollback-Logik in einem generischen PersistenceManager. Dieser bietet dann gleichzeitig erweiterte Funktionen wie datenbankunabhängiges Auditing, Entity-Verschlüsselung, Stornierfunktionen statt einfachem Löschen, Entity-Versionierung und vieles mehr an. Dies erlaubt es dem Entwickler, sich auf die Businesslogik zu konzentrieren und entkoppelt die Persistenzschicht, sodass man noch einfacher zwischen relationaler und NoSQL-Welt wechseln kann.

<bean id="applicationManagedPU"
  class="com.cloudyle.paasplus.persistence.ApplicationManagedPersistenceUnit">
  <jpa:unit property="entityManagerFactory" unitname="petclinic-nosql" />
</bean>
<bean id="containerManagedPU"
  class="com.cloudyle.paasplus.persistence.ContainerManagedPersistenceUnit">
    <tx:transaction method="*" value="Required" /> 
    <jpa:context property="entityManager" unitname=" petclinic-nosql" />
</bean>

Bitte Zuhause nachmachen

Dieser Artikel konnte nur eine kleine Einführung in die polyglotte Persistenz in einer OSGi-Anwendung geben. Wer die beschriebene Integration von NoSQL selber ausprobieren möchte, der kann sich das Pet-Clinic-Tutorial im Detail anschauen, den Sourcecode über GitHub beziehen oder das Beispiel direkt auf der Cloudyle-PaaS+-Plattform ausführen. Diese wendet sich speziell an OSGi-Entwickler und bietet vielfältige Unterstützung für den gesamten Application Life Cycle. Hier lässt sich mit wenigen Klicks ein Karaf-OSGi-Server mit einer MongoDB- und einer PostgreSQL-Datenbank erstellen, um direkt in die Welt der polyglotten Persistenz einzutauchen. Neben den APIs für die Persistenznutzung bietet Cloudyle PaaS+ eine Vielzahl an weiteren Enterprise-APIs an, um die Anwendungsentwicklung zu vereinfachen. Dies reicht von APIs für Nutzerverwaltung, Reporting oder Katalogmanagement über Sicherheits- und Verschlüsselungsfunktionen bis hin zu einem modularen Web-UI Framework.

Im nächsten Teil der Serie zeigen wir, welche Vorteile OSGi bei der Enterprise-Entwicklung mitbringt, gehen detaillierter auf die OSGi-Komponentenmodelle Blueprint und Declarative Services ein und zeigen einige „Lessons learned“ aus der Entwicklung unserer Enterprise-OSGi-APIs.

Geschrieben von
Alexander Grzesik
Alexander Grzesik
Alexander Grzesik ist Entwicklungsleiter bei der Cloudyle GmbH und Softwarearchitekt der PaaS+ OSGi Cloud Platform. Als Experte im Bereich Java, PaaS und OSGi hält er auf Konferenzen regelmäßig Vorträge zu diesen Themen.
Bob Dannehl
Bob Dannehl
Bob Dannehl ist Senior-Entwickler bei der Cloudyle GmbH und dort Pionier für NoSQL-Integration und polyglotte Persistenz.
Kommentare

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
4000
  Subscribe  
Benachrichtige mich zu: