GEDOPLAN
Jakarta EE (Java EE)

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

Jakarta EE (Java EE)

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

2 Kommentare. Hinterlasse eine Antwort

  • 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?

    Antworten
    • 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.

      Antworten

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Bitte füllen Sie dieses Feld aus.
Bitte füllen Sie dieses Feld aus.
Bitte gib eine gültige E-Mail-Adresse ein.
Sie müssen den Bedingungen zustimmen, um fortzufahren.

Autor

Diesen Artikel teilen

LinkedIn
Xing

Gibt es noch Fragen?

Fragen beantworten wir sehr gerne! Schreibe uns einfach per Kontaktformular.

Kurse

weitere Blogbeiträge

Webprogrammierung

Weihnachten 2020

GEDOPLAN wünscht Frohe Weihnachten 2020 https://gedoplan.github.io/ged-xmas2020/index.html source: https://github.com/GEDOPLAN/ged-xmas2020
IT-Training - GEDOPLAN
DevOps

Docker – “Build, Ship, and Run”

Problemstellung Ein großes Problem stellen beim Testen und Ausliefern von Software die leider häufig unterschiedlich konfigurierten Ablaufumgebungen dar. Die Entwicklerrechner,…

Work Life Balance. Jobs bei Gedoplan

We are looking for you!

Lust bei GEDOPLAN mitzuarbeiten? Wir suchen immer Verstärkung – egal ob Entwickler, Dozent, Trainerberater oder für unser IT-Marketing! Schau doch einfach mal auf unsere Jobseiten! Wir freuen uns auf Dich!

Work Life Balance. Jobs bei Gedoplan

We are looking for you!

Lust bei GEDOPLAN mitzuarbeiten? Wir suchen immer Verstärkung – egal ob Entwickler, Dozent, Trainerberater oder für unser IT-Marketing! Schau doch einfach mal auf unsere Jobseiten! Wir freuen uns auf Dich!