Teil 3: Transaktionssteuerung für Enterprise-Module

Modulare Enterprise-OSGi-Anwendungen: Let’s transact!

Jan Stamer

Die Basis für eine modulare Enterprise Anwendung haben wir in den vorherigen Artikeln gelegt. Angefangen haben wir mit der Persistenzschicht. Darauf haben wir Services und Bundles gesetzt und per Dependency Injection verknüpft. Arbeit kann unsere Enterprise-Anwendung aber noch nicht verrichten. Dazu fehlt nämlich etwas Essenzielles -Transaktionen. Machen wir uns an die Arbeit!

OSGi goes Enterprise

Der Artikel „Modulare Enterprise-OSGi-Anwendungen: Let’s transact!“ ist Teil der Serie „OSGi goes Enterprise, erstmals erschienen im Eclipse Magazin.

Verschaffen wir uns zunächst einen Überblick über den aktuellen Stand unserer Enterprise-Anwendung. Die Persistenzschicht bildet eine relationale Datenbank, auf der JPA-Entitäten aufsetzen. Dazu nutzen wir Gemini JPA und DBAccess aus dem Eclipse-Gemini-Projekt [1], wie im ersten Artikel dieser Serie beschrieben. Im zweiten Artikel haben wir mit Gemini Blueprint eine elegante Möglichkeit kennen gelernt, Services und Bundles per Dependency Injection zu verknüpfen. An unserem Beispielprojekt [2] haben wir erste praktische Erfahrungen damit gesammelt. Im Beispielprojekt werden Kunden und Konten über eine Webanwendung verwaltet, die Daten werden in einer relationalen Datenbank gespeichert. Bis dato steht davon jedoch nur die Persistenzschicht. Sie besteht aus dem Anschluss an die Datenbank mittels Gemini DBAccess [3]. Darauf setzen die JPA-Entitäten auf, die mit Gemini JPA zu einer JPA Persistence Unit gebündelt werden. Um über JPA Daten in die Datenbank zu schreiben, brauchen wir eine Transaktion. Wie wir an eine Transaktion kommen und wie sie gesteuert wird, das ist unsere Aufgabe für diesen Artikel.

Der richtige Platz für Transaktionen

Wo kommen die Transaktionen ins Spiel? Dazu steigen wir direkt in unser Beispielprojekt ein. Auf die Schicht mit den JPA-Entitäten setzen wir eine Schicht aus so genannten Repositories. Ein Repository [4] ist ein Design-Pattern aus dem Katalog der Patterns of Enterprise Application Architecture von Martin Fowler. Es schlägt die Brücke zwischen den Domain-Objekten und der Datenzugriffsschicht. In unserer Beispielanwendung sind die Domain-Objekte durch die Interfaces Customer und Account definiert. Die Datenzugriffsschicht bilden die JPA-Entitäten. Die Repository-Schicht abstrahiert vom eigentlichen Zugriff auf die Daten. So können wir die darauf aufbauenden Schichten ausschließlich auf Basis der Domain-Objekte realisieren. Wollten wir die relationale Datenbank durch eine NoSQL-Datenbank ersetzen, betrifft das nur die Repository-Schicht und darunter liegende Schichten. Auch das Testen wird einfacher, da wir für Tests eine einfache In-Memory-Implementierung der Repository-Schicht verwenden können. Wie sieht nun die Repository-Schicht unserer Enterprise-Anwendung aus? Sie besteht aus den Interfaces AccountRepository und CustomerRespository, die im Bundle com.pe-international.sample.repository.api liegen. Diese Interfaces gilt es nun mit Leben zu füllen, das heißt, eine konkrete Implementierung zu entwickeln. Das tun wir im nächsten Abschnitt.

Do-it-yourself-Transaktionen

Fangen wir zunächst klein an, nämlich mit Do-it-yourself-Transaktionen. Das heißt, wir steuern die Transaktionen selbst durch Aufruf der Methoden zu Beginn und zum Ende der Transaktion im Code. Was brauchen wir, um JPA-Transaktionen von Hand zu steuern? Richtig, die EntityManagerFactory von JPA. Wissen Sie noch, wie wir an sie herankommen? Dazu spicken wir kurz in den vorherigen Artikeln dieser Serie: Im ersten Teil haben wir die EntityManagerFactory der JPA Persistence Unit mithilfe von Gemini JPA als OSGi Service veröffentlicht. Dann haben wir gesehen, wie wir diesen OSGi Service per Dependency Injection mit Gemini Blueprint in einem anderen Bundle verwenden können. Im Folgenden gehen wir davon aus, dass wir die EntityManagerFactory per Dependency Injection injiziert haben. Die Do-it-yourself-Transaktion sieht dann wie in Listing 1 aus.

Listing 1
public  void createEntity(T entity) {
  EntityManager entityManager = entityManagerFactory.createEntityManager();
  EntityTransaction transaction = entityManager.getTransaction();
  try {
    transaction.begin();
    entityManager.persist( entity );
    transaction.commit();
  } catch( RuntimeException ex ) {
    if (transaction != null && transaction.isActive()) transaction.rollback();
    throw ex;
  } finally {
    entityManager.close();
  }
}

Transaktionen brauchen wir aber nicht nur an einer Stelle, sondern immer wieder. Unser einfaches Beispiel CustomerRepository braucht Transaktionen für die drei Operationen findAll, count und save. Der Code aus Listing 1 müsste also dreimal per Copy-and-Paste kopiert und leicht abgewandelt werden. Das ist ganz eindeutig Boilerplate-Code! Den wollen wir vermeiden. Schauen wir also nach einer besseren Lösung, bevor wir unsere Do-it-yourself-Transaktionen verwenden.

Geschrieben von
Jan Stamer
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.