Montag, 12. März 2012

JPA Vererbung SINGLE_TABLE JOINED TABLE_PER_CLASS

Der Nutzen des Einsatzes eines O/R-Mappers wie z.B. Hibernate unter Verwendnung der JPA-Spezifikation ist ja, dass db-unabhängig entwickelt werden kann und man den Kontext der objektorientieren Welt für Persistenzthemen nicht verlassen muss.
Somit möchte man natürlich Vererbung auch per JPA ausdrücken können.
Hierzu gibt es diverse Vererbungsstrategien:
Default ist SINGLE_TABLE, wenn lediglich die Annotation @Inheritance angegeben wurde.
Es gibt daneben noch 2 andere:
@Inheritance(strategy=InheritanceType.JOINED) und
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS).

Was bedeuten diese Strategien?
Beim Standard(SINGLE_TABLE) werden alle Unterklassen der Vererbungshierarchie auf eine Datenbank-Tabelle gemapped.
Bei JOINED werden alle abstrakten Klassen und konkreten Klassen der Vererbungshierarchie in einer separaten Tabelle persistiert.
Bei TABLE_PER_CLASS wird für jede konkrete Klasse der Vererbungshierarchie eine eigene dedizierte Tabelle angelegt.

Welche Strategie sollte man situationsbedingt auswählen?
Die Antwort auf diese Frage ergibt sich aus vornehmlich 2 Aspekten:
  • Polymorphie
  • Performance
Jeder möchte hohe Performance - es gibt jedoch Situationen, in denen Flexibilität und Erweiterbarkeit noch vor der Performance eingestuft wird: bspw. neue Features, die Konfigurationen oder untergeordnete UseCases betreffen.
Es gibt Situationen, in der die Vererbungshierarchie wichtige Bewegungsdaten betrifft und im Kern von wichtigen UseCases steht - in dem Fall ist Performance wichtiger als Flexibilität.
SINGLE_TABLE hat den Vorteil des perfomanten Zugriffs, da alles in einer Tabelle gespeichert wird.
Unterscheiden sich die konkreten Unterklassen stark und ist die Anzahl hoch, ergibt sich eine sehr breite Tabelle, so dass sich wiederum ein ungünstiger Tablespace-Bereich ergibt und weniger von der Tabelle von der Datenbank gecacht werden kann. In diesem Fall ist SINGLE_TABLE eine schlechte Wahl.
Nachteil Datenintegrität:
Bei SINGLE_TABLE müssen alle nicht PK-Felder zudem nullable sein, da alle Unterklassen in dieser Tabelle gespeichert werden und einige Typen diverse Spalten nicht verwenden.
Weiß man, dass man in den meisten und wichtigsten Zugriffsfällen konkrete Typen aus dem Persistenzkontext haben möchte, bietet sich TABLE_PER_CLASS an. Bei dieser Strategie sind polymorphe Abfragen sehr unperformant aufgrund von resultierenden UNION-Abfragen und Nutzung polymorpher Beziehung ist nicht möglich, da die abstrakten Typen nicht in der Datenbanktabelle gespeichert sind.
Bei JOINED sind polymorphe Abfragen aufgrund vorliegendem @DescriminatorValue in der Tabelle möglich - realisiert durch outer joins (besser als UNIONS). Konkrete Typenabfrage werden über inner joins realisert - daher der Name.

Realisierung
Es gibt eine abstrakte Klasse. Die Subklassen erben davon und sind @Entity.
Bei SINGLE_TABLE und JOINED muss nach JPA-Standard eine @DiscriminatorValue angegeben werden. Dieser ist vom Typ String und trägt im Default den Namen dtype und dient dazu die konkreten Klassen voneinander zu unterscheiden: In der Datenbank wird in der Tabelle der jeweilige String in die Spalte dtype geschrieben.

@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public abstract class BaseClass implements Serializable {
@Id
@GeneratedValue
private Long id;
private String common;
 ...
}

@Entity
@DiscriminatorValue(value="A")
public class A extends BaseClass {
 private String specificA;
...
}
@Entity
@DiscriminatorValue(value="B")

public class B extends BaseClass {
 private String specificB;
 ...
}
Ergibt folgende Tabelle:
dtype|id|common|specificA|specificB

Bei einer Instanz A wird die Spalte specificB immer null sein.
Auf der anderen Seite sind polymorphe Abfragen (select * from BaseClass) oder nach konkrete Subklasse (select * from A ...) hochperformant.

Wäre es TABLE_PER_CLASS sind polymorphe Abfragen nicht möglich.
Bei JOINED werden bei Abfragen nach konkreten Subklassen inner joins generiert,
bei polymorphen Abfragen outer joins.








Keine Kommentare:

Kommentar veröffentlichen