Java Persistence Trap: Embeddable bleibt null, wenn seine Komponenten null sind

Java Persistence erlaubt den Einsatz von Embeddables als persistente Eigenschaften von Entities, z. B. so:

@Embeddable
public class Adresse
{
  private String strasse;
  private String hausNummer;
  private String plz;
  private String ort;
  ...
}

@Entity
public class Person
{
  ...
  private Adresse   adresse;
  ...
}

Leider definiert die Spezifikation JPA 2.0 nicht eindeutig, wie beim Laden von Objekten verfahren werden soll, bei denen sämtliche Attribute des Embeddables null sind: Soll im Beispiel trotz leerer Adresse ein Adress-Objekt instanziert werden oder bleibt das Attribut
Person.adresse selbst null?

Eclipselink und Hibernate tun letzteres, d. h. vollständig leere eingebettete Objekte werden nicht instanziert.

Das Verfahren mag bei Betrachtung des Persistenzanteils einer Anwendung so in Ordnung sein, erzeugt aber für GUI-Frameworks Probleme, die ein Data Binding benutzen, also bspw. Eingabefelder direkt mit den entsprechenden Bean-Attribute verknüpfen. So könnte bspw. in einer JSF-View der folgende Ausdruck für die Eingabe der Postleitzahl – also eines Teils der eingebetteten Adresse – einer Person verwendet werden:

<h:inputText value="#{personModel.person.adresse.plz}" />

Für den oben skizzierten Fall einer bislang noch komplett leeren Adresse erzeugt der JSF-EL-Ausdruck eine NullPoinerException, da person.adresse noch null ist.
Nun könnte man auf die Idee kommen, das betroffene Attribut in einer Lifecycle-Methode nach dem Laden bei Bedarf zu instanzieren:

@Embeddable
public class Adresse
{
  ...
  @PostLoad
  private void postLoad()
  {
  if (this.adresse == null)
    this.adresse = new Adresse();
  }
}

Dadurch würde aber beim Speichern des Objektes stets ein unnötiger Update ausgelöst, da es aus Sicht des Persistenzproviders ‚dirty‘ ist. Würde man zur Behebung dieser Situation auch die umgekehrte Lifecycle-Methode vor dem Speichern so programmieren, dass das Attribut – soweit immer noch leer – wieder auf null gesetzt wird, käme man danach wieder in das Problem der NullPointerExceptions.

Eine funktionierende Lösung ist daher ein Late Initializing des Attributs in seiner Getter-Methode – nun allerdings threadsafe mittels Double Check Lock Idiom nach Joshua Bloch:

@Entity(name = "Person")
public class Person
{
  ...
  private volatile Adresse adresse;
  ...
  public Adresse getAdresse()
  {
    Adresse tmp = this.adresse;
    if (tmp == null)
    {
      synchronized (this)
      {
        tmp = this.adresse;
        if (tmp == null)
        {
          tmp = new Adresse();
          this.adresse = tmp;
        }
      }
    }
  return tmp;
  }

  ...

  @PrePersist
  @PreUpdate
  private void preSave()
  {
    if (this.adresse != null && this.adresse.isNull()) // Prüfen, ob Adresse komplett null
      this.adresse = null;
  }

Wichtig dafür: Das Attribut adresse muss volatile sein, sonst funktioniert das DCL-Idiom nicht zuverlässig!

Man sieht: Ziemlich vel Aufwand für so eine einfache Angelegenheit. Es wäre also schön, wenn JPA-Provider sind bei Embeddables anders verhalten würden. Man könnte ja bspw. in die entsprechende Annotation noch einen Parameter aufnehmen, der den Lademodus bestimmt:

@Embedded(loadIfNull=true)

EclipseLink bietet über sein Feature @Customizer eine ähnliche wirkende Konfigurationsmöglichkeit an (http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Entities/Embeddable).

Dirk

Advertisements

Über Dirk Weil
Dirk Weil ist seit 1998 als Berater im Bereich Java tätig. Als Geschäftsführer der GEDOPLAN GmbH in Bielefeld ist er für die Konzeption und Realisierung von Informationssystemen auf Basis von Java EE verantwortlich. Seine langjährige Erfahrung in der Entwicklung anspruchsvoller Unternehmenslösungen machen ihn zu einem kompetenten Ansprechpartner und anerkannten Experten auf dem Gebiet Java EE. Er ist Autor in Fachmagazinen, hält Vorträge und leitet Seminare und Workshops aus einem eigenen Java-Curriculum.

2 Responses to Java Persistence Trap: Embeddable bleibt null, wenn seine Komponenten null sind

  1. Matthias says:

    Ich habe mich heute eher zufällig mit der Initialisierung von @Embeddable-s beschäftigt und Dein Blog war der einzige informative Beitrag, den ich gefunden habe.
    Meine Frage: Was spricht dagegen die @Embeddable Objekte einfach gleich zu instanziieren?

    • dirkweil says:

      Das ist tatsächlich eine Möglichkeit – allerdings in der @PostLoad-Methode, nicht etwa im (NoArg)Konstruktor. Mit dem Lazy Initializing lässt sich verhindern, dass der Provider das Objekt unmittelbar als Dirty markiert und dann am Ende der Transaktion ein UPDATE ausführt. Das würde bei meinem Ansatz erst dann passieren, wenn tatschlich auf das Embeddable zugegriffen wird. Der Trick, in @PreUpdate das entsprechende Attribut wieder auf null zu setzen, wenn es nicht anderweitig besetzt wurde, und damit das unnötige UPDATE zu verhindern, funktioniert leider providerabhängig nur in manchen Fällen.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: