EnterpriseTales

Java Persistence API 2.1: Nur ein Minor Release?

Arne Limburg, Lars Röwekamp und Matthias Weßendorf

Eine neue Version der Java-Persistence-API-(JPA-)Spezifikation steht vor der Tür. Grund genug, den ersten „Early Draft“ innerhalb der EnterpriseTales-Kolumne genauer zu untersuchen. JPA-Experte und Apache OpenWebBeans Committer Arne Limburg stellt die wesentlichen Neuerungen vor! Mehr als zwei Jahre nach dem Release der Version 2.0 des JPA-Standards wurde im Januar der erste Early Draft der nächsten Version dieses Standards veröffentlicht. Dieser trägt die Versionsnummer 2.1, was darauf hindeutet, dass es sich „nur“ um ein Minor Release handelt. Aber ist das tatsächlich so? Was gibt es tatsächlich für neue Features, und für wen lohnt es sich, früh auf diesen „Versionszug“ aufzuspringen?

Was lange währt, wird gut?

Um das gleich vorwegzunehmen: Eine sensationelle Neuerung, wie es z. B. das Criteria-API bei JPA 2.0 war, wird es mit JPA 2.1 nicht geben. Dennoch gibt es einige interessante Erweiterungen, auf die sich ein Blick lohnt. Allen voran geht dabei die Integration von Context Dependent Interchangeability (CDI). Wer nun aber hofft, in Zukunft jede CDI Bean in jede Entity Bean injizieren zu können oder ohne Umweg eine Entity Bean im CDI Context zur Verfügung zu haben, der wird enttäuscht. Aufgrund der stark unterschiedlichen Scopes und Lebenszyklen von CDI Beans und JPA Entity Beans entschied sich das Spezifizierungskomitee gegen eine so weitgehende Integration. Stattdessen wird es möglich sein, jede beliebige CDI Bean in einen JPA EntityListener zu injizieren. Aber auch dieses Feature bietet Raum für einige interessante Patterns. Eines davon kommt einem sofort in den Sinn, wenn man an die letzte EnterpriseTales-Kolumne [1] zurückdenkt, in der wir den aktuell angemeldeten Benutzer im CDI Context verfügbar gemacht haben. Da ist es doch naheliegend, diesen zu Auditing-Zwecken in einen EntityListener injizieren zu lassen, um ihn dann beim Anlegen und Ändern einer Entität direkt hinein zu schreiben und so protokollieren zu können, wer sie angelegt oder verändert hat. Die in der Entität benötigten Methoden können über eine Oberklasse zur Verfügung gestellt werden, wie Listing 1 zeigt.

Listing 1
@EntityListeners(AuditingListener.class)
@MappedSuperclass
public class AuditableEntity {
    ...
    public void setCreatedBy(String user) {
        createdBy = user;
    }

    public void setModifiedBy(String user) {
        modifiedBy = user;
    }
}

Um einer solchen Entität nun bei Erzeugung und Veränderung den aktuellen Benutzer verpassen zu können, müssen wir den AuditingListener implementieren, uns dort den aktuell angemeldeten Benutzer injizieren lassen und diesen bei Bedarf in die Entität setzen, wie Listing 2 zeigt.

Listing 2
public class AuditingListener {

    @Inject @Authenticated
    private User user;

    @PrePersist
    public void auditPersist(AuditableEntity entity) {
        entity.setCreatedBy(user.getName());
    }

    @PreUpdate
    public void auditUpdate(AuditableEntity entity) {
        entity.setModifiedBy(user.getName());
    }
}

Nun können wir in den jeweiligen Methoden also hinterlegen, welcher Benutzer die Änderung an der Entität vorgenommen hat. Wenn der aktuell angemeldete Benutzer selbst eine JPA-Entität ist, könnte man sogar auf die Idee kommen, in der AuditableEntity nicht nur den Namen des Benutzers zu setzen, sondern direkt die Beziehung zu dem Nutzer herzustellen, also etwa entity.setModifiedBy(user) . Hier schiebt allerdings die JPA Specification einen Riegel vor, denn dort steht im Draft der Version 2.1 in Abschnitt 3.5.2 Folgendes:

In general, the lifecycle method of a portable application should not [.] access other entity instances, or modify relationships within the same persistence context. A lifecycle callback method may modify the non-relationship state of the entity on which it is invoked.

Auf Deutsch bedeutet das nichts anderes, als dass wir in den Methoden auditPersist(.)und auditUpdate(.)zwar das Auditable-Objekt selbst mit all seinen Attributen verändern dürfen, aber nicht seine Beziehungen zu anderen Objekten. Also dürfen wir auch keine neue Beziehung zu dem aktuell angemeldeten Benutzer herstellen. Würde es sich bei dem Benutzerobjekt anstatt einer Entität um ein Embedded Object handeln (dass z. B. nur Benutzername und Kennwort kapselt), wäre die Modifikation wieder erlaubt, weil Embedded Objects Teil des Zustands der Entität sind, zu der sie gehören (vgl. [2] Abschnitt 2.5). Zwar verbietet obige Formulierung aus der Specification nicht, dass einzelne Implementierungen damit umgehen können, dass in einem EntityListener auch Beziehungen zwischen Entitäten geändert werden. Zugesichert ist das allerdings nicht, auch wenn in einer Fußnote der Specification explizit in Aussicht gestellt wird, dass dieses Verhalten ggf. zu einem späteren Zeitpunkt standardisiert wird. Aber auch mit dieser Einschränkung eröffnet die Integration von CDI in JPA einige neue Möglichkeiten und ist folgerichtig, da auch viele andere Java-EE-Standards in ihren aktuellen Versionen die Integration von CDI vorantreiben.

Geschrieben von
Arne Limburg, Lars Röwekamp und Matthias Weßendorf
Kommentare

Schreibe einen Kommentar

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