© DrHitch/Shutterstock.com
JPA 2.1

4 Entity Manager


4.1 Dynamisches Lesen

Entity Graph

Man kann Entity Graphs definieren, die für das gezielte Lesen von Daten aus der Datenbank verwendet werden können. Damit ist es möglich, gezielt die für den Anwendungsfall benötigten Daten aus der Datenbank zu laden. Das schont die Ressourcen und führt zu schnelleren Datenbankzugriffen.

Das Problem in JPA 2.0

Ein Datenmodell kann als Graph dargestellt werden. Die Knoten repräsentieren die Entities und die Kanten die Beziehungen (1:1, 1:n, n:m) zwischen den Entitäten. Abbildung 4.1 zeigt beispielhaft den Graphen für die Entitäten Publisher, Book, Person und MailAddress und deren Beziehungen untereinander.

bild_4.1.jpg

Abbildung 4.1: Entity Graph

Damit nicht beim Laden einer Entität der gesamte Entity Graph geladen wird, kann für jede Beziehung zwischen den Entitäten eine Fetch-Strategie definiert werden. Die Fetch-Strategie wird über das Attribut fetch innerhalb der Beziehungsannotationen @OneToOne, @OneToMany, @ManyToMany festgelegt. Es sind die in Tabelle 4.1 beschriebenen Fetch-Strategien möglich.

Strategie

Beschreibung

LAZY

Verzögert laden

EAGER

Sofort laden

Tabelle 4.1: Fetch-Strategien

Listing 4.1 zeigt eine 1:n-Beziehung zwischen den Entitäten Publisher und Book mit der Fetch-Strategie LAZY. Damit werden beim Laden von Publisher die Books verzögert geladen, also nicht sofort, sondern erst beim Zugriff auf ein Book.

@OneToMany(fetch = FetchType.LAZY)
private List<Book> books;

Listing 4.1: Definition der Fetch-Strategie

Ohne Angabe einer expliziten Fetch-Strategie gelten die Default-Fetch-Strategien. Für Einzelbeziehungen @OneToOne, @ManytoOne ist die Fetch-Strategie EAGER und für alle Mehrfachbeziehungen @OneToMany, @ManyToMany ist die Fetch-Strategie LAZY.

Ein wesentlicher Nachteil von JPA 2.0 ist, dass die Fetch-Strategie nur statisch über die Annotationen für den gesamten Graphen zur Kompilierzeit vorgegeben werden kann. Das ist aber ein Problem! Denn von Anwendungsfall zu Anwendungsfall sollen unterschiedliche Entities mal EAGER, mal LAZY geladen werden. In einem Anwendungsfall interessieren nur die Publisher, alle anderen Entities sind nicht interessant. Hingegen kann in einem zweiten Anwendungsfall das Lesen der Publisher mit ihren Books von Interesse sein. Diese Problemstellung korreliert stark mit dem Thema Data Views. Der Client benötigt, abhängig vom Anwendungsfall, unterschiedliche Sichten auf das Datenmodell. Hier kommt das DTO-Pattern ins Spiel, das die verschiedenen Sichten repräsentiert.

Die Lösung in JPA 2.1

Hier bietet JPA 2.1 die Lösung „Entity Graph“. Grundsätzlich bietet JPA die Möglichkeit, Entity Graphs zu definieren, die dann in unterschiedlichen Kontexten unterschiedliche Wirkung haben. Es wird zwischen

  • Fetch Graph (DFG)
  • Load Graph (DLG)

unterschieden. In Java EE 8 sind zudem Copy Graphs und Merge Graphs geplant. Der Entity Graph ist immer der gleiche, allerdings wird er unterschiedlich interpretiert. Der DFG beschreibt, wie das Fetching, und der DLG, wie das Laden ausgeführt wird. Die Problemstellung haben wir im vorangegangenen Abschnitt an der Fetch-Problematik erläutert. Im weiteren Verlauf werden wir auch auf die Load-Problematik eingehen.

Entity Graphs können statisch über Annotationen oder dynamisch über das Entity Graph API definiert werden. Beide Varianten werden im Folgenden beschrieben.

Ein Entity Graph kann einer Entity-Klasse mithilfe der Annotation @NamedEntityGraph hinzugefügt werden. Sollen mehrere Entity Graphs definiert werden, muss, wie schon an anderen Stellen, eine „Plural-Annotation“ verwendet werden, hier @NamedEntityGraphs.

Der Entity-Graph-Name muss für die Persistence Unit eindeutig sein; der Vorgabewert ist der Entity-Name selbst. Mit dem Parameter attributeNodes werden die Attribute angegeben, die bei Benutzung des Entity Graphen geladen werden sollen. Das Beispiel in Listing 4.2 zeigt einen einfachen Entity Graph für die Klasse Publisher namens Publisher_books, der das Relationsattribut books umfasst. Mit dem Parameter subGraphs können Entity Graphs für referenzierte Objekte angegeben werden. Dies zeigt das zweite Beispiel in Listing 4.2: Der Entity Graph Publisher_booksAndAuthors enthält über das Attribut books hinaus das Relationsattribut authors der Klasse Book.

@Entity
@NamedEntityGraphs({
@NamedEntityGraph(
name = "Publisher_books",
attributeNodes = @NamedAttributeNode("books")),
@NamedEntityGraph(
name = "Publisher_booksAndAuthors",
attributeNodes = @NamedAttributeNode(value = "books",
subgraph = "Book_authors"),
subgraphs = @NamedSubgraph(
name = "Book_authors",
attributeNodes = @NamedAttributeNode("authors"))) })

public class Publisher
{

@OneToMany(fetch = FetchType.LAZY, …)
private List<Book> books;

Listing 4.2: Entity Graph

Zur Nutzung von Entity Graphs bedient man sich sog. Hints, die u. a. der Methode EntityManager.find als dritter Parameter übergeben werden können. Diese Hints sind Key-Value-Paare in einem Map-Objekt. Im Zusammenhang mit Entity Graphs definiert JPA 2.1 zwei Hints:

  • javax.persistence.fetchgraph: Nur die im genannten Graph enthaltenen Attribute werden geladen.
  • javax.persistence.loadgraph: Die im genannten Graph enthaltenen Attribute werden zusätzlich zu den ohnehin mit EAGER konfigurierten Attributen geladen.

Aus technischen Gründen werden ID- und Versionsattribute in jedem Fall geladen.

Der Beispielcode in Listing 4.3 würde somit in dem gefundenen Publisher neben dem ID-Attribut auch die dem Publisher zugeordneten books laden. Die Book-Attribute selbst würden mit dem Standardverfahren geladen.

Map<String, Object> hints = new HashMap<>();
hints.put("javax.persistence.fetchgraph", "Publisher_books");
Publisher publisher = em.find(Publisher.class, someId, hints);

Listing 4.3: Entity Graph als Fetch Graph

Im Unterschied dazu verwendet Listing 4.4 den Hint javax.persistence.loadgraph und einen Entity Graph, der auch die Entity Book beeinflusst: Neben den standardmäßig geladenen Attributen von Publisher und Book würden hier auf jeden Fall Publisher.books und Book.authors geladen.

Map<String, Object> hints = new HashMap<>();
hints.put("javax.persistence.loadgraph", "Publisher_booksAndAuthors");
Publisher publisher = em.find(Publisher.class, someId, hints);

Listing 4.4: Entity Graph als Load Graph

Neben dieser statischen Definitionsweise besteht auch die Möglichkeit, Entity Graphs mittels API zu beschreiben. Das Entity Graph API besteht aus Methoden zur Erzeugung eines Graphen, repräsentiert durch das Interface EntityGraph<T> und das Interface EntityGraph<T> selbst, auf dem unterschiedliche Methoden zum Aufbau des Graphen aufgerufen werden können. Zudem steht die Klasse Subgraph<T> zur Verfügung, über die...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang