JPA – Lazy Loading und Eager Loading

Eclipselink 2.1 ist eine Persistenzanbieter-Laufzeitumgebung für Spezifikation der Java Persistence API 2.1N. Die JPA-Spezifikation definiert zwei Hauptstrategien zum Laden von Daten (Lazy und Eager).. Die EAGER-Strategie ist eine Anforderung an die Laufzeit des Persistenzanbieters, dass Daten eifrig abgerufen werden müssen. Die LAZY-Strategie ist ein Hinweis für die Laufzeit des Persistenzanbieters, dass Daten beim ersten Zugriff verzögert abgerufen werden sollten.

Das Lazy Loading wird üblicherweise verwendet, um das Laden der Attribute oder Assoziationen einer oder mehrerer Entitäten bis zu dem Punkt zu verschieben, an dem sie benötigt werden. Das Eager Loading hingegen ist ein entgegengesetztes Konzept, bei dem die Attribute und Assoziationen einer oder mehrerer Entitäten abgerufen werden explizit und ohne die Notwendigkeit, darauf hinzuweisen.

Bestimmen Sie die Ladestrategie

EclipseLink 2.1 bietet Ihnen verschiedene Möglichkeiten, Ihre gewünschte Ladestrategie anzugeben.

  • Grundlegende Anmerkung: Es stellt ein fetchType-Attribut bereit, das zur Bestimmung der Strategie verwendet werden kann, nach der die Attribute eifrig oder verzögert geladen werden sollen.
  • @OneToOne, @ManyToOne, @OneToMany und @ManyToMany: Alle Assoziationsanmerkungen stellen ein fetchType-Attribut bereit, das zur Bestimmung der Strategie verwendet werden kann, nach der die Attribute eifrig oder träge geladen werden sollen.

Lazy Fetch-Typ mit @OneToOne und @ManyToOne

Wenn Sie sich das @OneToOne-Zuordnungsbeispiel schon einmal angesehen haben, haben Sie sicher gesehen, dass die Zuordnung zwischen den Entitäten „Employee“ und „Address“ eine bidirektionale OneToOne-Verbindung darstellt. Es ist an der Zeit, dem Eager- und Lazy-Loading in diesem Verband mehr Aufmerksamkeit zu schenken. Sehen wir uns an, welche Rolle das fetchType-Attribut in dieser Zuordnung spielen kann, wenn es auf der Mitarbeiterseite und auf der Adressseite verwendet wird. (siehe EclipseLink-Tutorials)

Der Standardwert des fetchType-Attributs innerhalb der OneToOne-Annotation ist ein Eager-Wertund ob der Wert des Felds oder der Eigenschaft langsam geladen werden soll oder eifrig abgerufen werden muss. Die EAGER-Strategie ist eine Anforderung an die Laufzeit des Persistenzanbieters, dass der Wert eifrig abgerufen werden muss, und LAZILY ist nur ein Hinweis an die Laufzeit des Persistenzanbieters.

Standardmäßig betrachtet EclipseLink die OneToOne- und ManyToOne-Assoziationen nicht als Lazily-Assoziationen. Wenn wir also OneTOne- oder ManyToOne-Assoziationen auf Lazy gesetzt haben, ohne EclipseLink-Weaving zu verwenden, haben wir sicherlich eine eifrige Assoziation. Sehen wir uns Abbildung 1.0 an, die Ihnen eine Employee-Entität zeigt, die mit einer Address-Entität in einer OneToOne-Zuordnung verknüpft ist.

Employee.java


package net.javabeat.eclipselink.data;

import java.util.List;

import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="EmployeeType")
@DiscriminatorValue(value="EMP")
@EntityListeners(value={PersistenceContextListener.class})
public class Employee {

 @Id
 private int employeeId;

 @Basic(optional=false)
 private String employeeName;

 @Embedded
 private EmployeePeriod employeePeriod;

 @OneToMany(mappedBy="employee", cascade=CascadeType.ALL,fetch=FetchType.EAGER)
 private List phones;

 @ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
 @JoinTable(
 name="employee_project",
 joinColumns={@JoinColumn(name="employeeId")},
 inverseJoinColumns={
 @JoinColumn(table="project",name="projectId"),
 @JoinColumn(table="localproject",name="projectId"),
 @JoinColumn(table="globalproject",name="projectId")}
 )
 private List projects;

 @OneToOne(cascade={CascadeType.ALL},fetch=FetchType.LAZY,targetEntity=Address.class)
 @JoinColumns({
 @JoinColumn(name="AddressId",referencedColumnName="AddressId"),
 @JoinColumn(name="EmployeeAddressCountryId",referencedColumnName="AddressCountryId"),
 @JoinColumn(name="EmployeeAddressCityId",referencedColumnName="AddressCityId")}
 )
 private Address address; // Note: OneToOne annotation is configured as lazy loading

public Address getAddress() {
 return address;
 }

public void setAddress(Address address) {
 this.address = address;
 }

public int getEmployeeId() {
 return employeeId;
 }

public void setEmployeeId(int employeeId) {
 this.employeeId = employeeId;
 }

 public String getEmployeeName() {
 return employeeName;
 }

public void setEmployeeName(String employeeName) {
 this.employeeName = employeeName;
 }

 public List getPhones() {
 return phones;
 }

public void setPhones(List phones) {
 this.phones = phones;
 }

public List getProjects() {
 return projects;
 }

public void setProjects(List projects) {
 this.projects = projects;
 }

public EmployeePeriod getEmployeePeriod() {
 return employeePeriod;
 }

public void setEmployeePeriod(EmployeePeriod employeePeriod) {
 this.employeePeriod = employeePeriod;
 }

}

Adresse.java


package net.javabeat.eclipselink.data;

import javax.persistence.CascadeType;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.FetchType;
import javax.persistence.OneToOne;
import javax.persistence.PrePersist;

@Entity(name="address")
@EntityListeners(value={PersistenceContextListener.class})
public class Address {
 @EmbeddedId
 private AddressPK addressId;
 private String addressCountry;
 private String addressCity;

 @OneToOne(cascade=CascadeType.ALL,mappedBy="address",fetch=FetchType.LAZY)
 private Employee employee; // Note: The OneToOne association is configured as lazy loading

 public Employee getEmployee() {
 return employee;
 }

 public void setEmployee(Employee employee) {
 this.employee = employee;
 }

public String getAddressCountry() {
 return addressCountry;
 }
 public void setAddressCountry(String addressCountry) {
 this.addressCountry = addressCountry;
 }
 public String getAddressCity() {
 return addressCity;
 }
 public void setAddressCity(String addressCity) {
 this.addressCity = addressCity;
 }

public AddressPK getAddressId() {
 return addressId;
 }

public void setAddressId(AddressPK addressId) {
 this.addressId = addressId;
 }

 @PrePersist
 public void prePersist(){
 System.out.println("Address Entity :: Method PrePersist Invoked Upon Entity "+this);
 }

}

Lassen Sie uns den folgenden Codeausschnitt ausführen:


Employee emp = em.find(Employee.class, 1);

  • Der ausgeführte Beispielcode ging davon aus, dass eine Mitarbeiterzeileninstanz zuvor beibehalten wurde. (Siehe OneToOne Association).
  • Die Eclipselink-Implementierung verwirft den von Ihnen festgelegten Hinweis, dass der Abruftyp „Lazy“ ist.
  • Die Adress-OneToOne-Zuordnung wird als Eager-Ladevorgang abgerufen. Siehe Abbildung 1.0, die Ihnen die Instanz von Employee und seine abhängigen Objekte zeigt.

Mitarbeiterinstanz Lazy Loading verwerfen

Abbildung 1.0

Wie Sie in Abbildung 1.0 bemerkt haben

  • Die Entität „Adresse“ wurde von der EclipseLink-JPA abgerufen, obwohl der Abruftyp „Adresse“ verzögert ist.
  • Standardmäßig verwirft EclipseLink den Abruftyp Lazy, falls wir @OneToOne und @ManyToOne verwendet haben.

Erzwingen Sie EclipseLink, um Lazy Associations zu berücksichtigen

Um EclipseLink unter Berücksichtigung des Lazy-Fetch-Typs zu erzwingen, der im vorherigen Employee-Beispiel übergeben wurde, müssen wir das verwenden, was Eclipse „Weaved Indirection“ nennt.

Wenn wir festlegen, dass OneToOne- oder ManyToOne-Assoziationen verzögert sind, aktivieren wir Weaving. Der EclipseLink JPA-Persistenzanbieter verwendet Weaving, um die Werthalterindirektion für diese Assoziationen zu ermöglichen. Bevor wir mit den nächsten Zeilen fortfahren, wollen wir sehen, wie wir das Weben mithilfe der Eclipse-IDE und unter Verwendung derselben bereitgestellten Beispiele ermöglichen können.

  • Bestimmen Sie die Datei eclipselink.jar, die die erforderliche Weaving-API enthält (siehe EclipseLink-Installation und -Konfiguration mit Eclipse).
  • Öffnen Sie EclipseIDE.
  • Öffnen Sie das EclipseLink – JPA-Installationspaket
  • Bestimmen Sie Ihre ausführbare Klasse (in diesem Beispiel wird es so sein). JPAImpl.java).
  • Setzen Sie einen Haltepunkt an dem Zeilencode, der die Employee-Instanz aus einer Datenbank abgerufen hat.
  • Klicken Sie im oberen Hauptmenü auf das Menü „Ausführen“.
  • Wählen Sie Debug-Konfiguration aus.
  • Wählen Sie im geöffneten Fenster auf der linken Seite die Java-Anwendung aus, die Sie zum Weben verwenden möchten.
  • Wählen Sie im selben Fenster, aber auf der rechten Seite, die Registerkarte mit dem Titel (Argumente) aus.
  • Geben Sie im Feld „VM-Argumente“ (-javaagent:eclipselink.jar) ein.
  • Wählen Sie im Arbeitsverzeichnis unter dem Feld „VM-Argumente“ die Option „Andere“ aus.
  • Geben Sie die Datei eclipselink.jar an, indem Sie zur installierten EclipseLink-Installationsdatei navigieren. Sehen Sie sich Abbildung 1.1 an, die Ihnen das endgültige Bild dessen zeigt, was Sie in Ihrer lokalen Umgebung haben sollten.

Endgültiges Webkonfigurationsbild

Abbildung 1.1

  • Schließen Sie den Dialog, indem Sie auf Debug klicken.
  • Sie sollten das sehen JPAImpl.java ausgeführt wird und das Debug an dem bereits definierten Haltepunkt gestoppt wurde.
  • Klicken Sie auf F6, um einen Schritt vorwärts fortzufahren.
  • Lassen Sie Ihren Mauszeiger über die emp-Instanz fahren.
  • Die EclipseIDE muss die emp-Instanz und alle ihre abhängigen Objekte anzeigen, aber das ist Zeit für die entsprechenden Zuordnungen, die als LAZY LOADING definiert sind. Siehe Abbildung 1.2.

Faule Ladung

Abbildung 1.2

  • Eclipselink verwendet die Indirektion; Bei der Indirektion handelt es sich wahrscheinlich um ein Diagramm persistenter Objekte, die (UN-ausgelöste) Indirektionsobjekte enthalten.
  • EclipseLink verwendet Weaving, um Lazy Fetching für OneToOne- und ManyToOne-Assoziationen zu implementieren.
  • Wenn Sie den Werthalter ausgewählt haben, der die Adressinstanz mit dem Namen (_persistence_address_vh=UnitOfWorkQueryValueHolder) darstellt, sollte die folgende Meldung angezeigt werden: {UnitOfWorkQueryValueHolder: nicht instanziiert}
  • Versuchen Sie, den Adresswerthalter zu untersuchen, um andere Attribute wie isLazy (true), isInstantiated(false) und andere zu sehen.
  • Außerdem verweist das Adressattribut, das zur Mitarbeiterinstanz gehört, auf null. Siehe Abbildung 1.3.

Adresse ist Null

Abbildung 1.3

  • Die Adressinstanz, die zu „Mitarbeiter“ gehört, ist Null, da der Abruftyp „Lazy Load“ ausgewählt wurde, um eine solche Zuordnung zu erreichen. Die einzige Möglichkeit, auf die Adresse zuzugreifen, besteht darin, sie direkt aufzurufen. Siehe Abbildung 1.4.

Greifen Sie auf die Lazy Load Association zu

Abbildung 1.4

  • Wie Sie bemerkt haben, antwortet die Adressinstanz, die zu Employee gehört, jetzt.

Standardmäßig unterstützt EclipseLink Lazy Load für @OneToMany und @ManyToMany

Schauen wir uns die Mitarbeiterentität und ihre abhängigen Objekte (Projekte und Telefone) an, wobei der Mitarbeiter die Projekte als ManyToMany und die Telefone als OneToMany verknüpft. Sehen wir uns an, wie man Lazy Load im Gegensatz zur Verwendung mit OneToOne und ManyToOne einfach erreichen kann.

Employee.java


package net.javabeat.eclipselink.data;

import java.util.List;

import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="EmployeeType")
@DiscriminatorValue(value="EMP")
@EntityListeners(value={PersistenceContextListener.class})
public class Employee {

 @Id
 private int employeeId;

 @Basic(optional=false)
 private String employeeName;

 @Embedded
 private EmployeePeriod employeePeriod;

 @OneToMany(mappedBy="employee", cascade=CascadeType.ALL,fetch=FetchType.LAZY)
 private List phones; // OneToMany lazy Load Association

 @ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
 @JoinTable(
 name="employee_project",
 joinColumns={@JoinColumn(name="employeeId")},
 inverseJoinColumns={
 @JoinColumn(table="project",name="projectId"),
 @JoinColumn(table="localproject",name="projectId"),
 @JoinColumn(table="globalproject",name="projectId")}
 )
 private List projects; // ManyToMany Lazy Load Association

 @OneToOne(cascade={CascadeType.ALL},fetch=FetchType.LAZY,targetEntity=Address.class)
 @JoinColumns({
 @JoinColumn(name="AddressId",referencedColumnName="AddressId"),
 @JoinColumn(name="EmployeeAddressCountryId",referencedColumnName="AddressCountryId"),
 @JoinColumn(name="EmployeeAddressCityId",referencedColumnName="AddressCityId")}
 )
 private Address address;

public Address getAddress() {
 return address;
 }

public void setAddress(Address address) {
 this.address = address;
 }

public int getEmployeeId() {
 return employeeId;
 }

public void setEmployeeId(int employeeId) {
 this.employeeId = employeeId;
 }

 public String getEmployeeName() {
 return employeeName;
 }

public void setEmployeeName(String employeeName) {
 this.employeeName = employeeName;
 }

 public List getPhones() {
 return phones;
 }

public void setPhones(List phones) {
 this.phones = phones;
 }

public List getProjects() {
 return projects;
 }

public void setProjects(List projects) {
 this.projects = projects;
 }

public EmployeePeriod getEmployeePeriod() {
 return employeePeriod;
 }

public void setEmployeePeriod(EmployeePeriod employeePeriod) {
 this.employeePeriod = employeePeriod;
 }

}

  • Die Mitarbeiterentität berücksichtigt zwei zusätzliche Zuordnungen, eine vom Typ OneToMany mit der Telefonentität und die zweite vom Typ ManyToMany mit der Projektentität.
  • Standardmäßig berücksichtigt EclipseLink das verzögerte Laden dieser Zuordnungen, sodass keine weitere Konfiguration erforderlich ist, wie wir bei OneToOne und ManyToOne gesehen haben. Sehen Sie sich Abbildung 1.6 an, die zeigt, wie wir auf einfache Weise eine Lazy Load erreichen können.

ManyToMany und OneToMany Lazy Load

Abbildung 1.6

  • EclipseLink ist in der Lage, entweder ManyToMany und OneToMany verzögert zu laden, ohne dass ein Weben erforderlich ist.
  • Die Liste der Phone-Entitäten wird verzögert geladen, da ihre Zuordnung in der Employee-Entität als Lazy-Fetch-Typ markiert ist.
  • Die Liste der Projektentitäten wird verzögert geladen, da ihre Zuordnung in der Mitarbeiterentität als Lazy-Fetch-Typ markiert ist.
  • In jeder Lazy-Loading-Zuordnung kann durch Zugriff auf das Attribut navigiert werden, zu dem die Zuordnung gehört.

Navigieren durch ManyToMany und OneToMany Lazy Load

Durch Ausführen des folgenden Codes haben Sie die Möglichkeit, durch diese träge geladenen Zuordnungen zu navigieren:


Employee employee = em.find(Employee.class, 1);
 System.out.println("Phones Size :: "+ employee.getPhones().size());
 System.out.println("Projects Size :: "+ employee.getProjects().size());

  • Sobald Sie auf das Attribut zugegriffen haben, das sich auf die Zuordnung bezieht, ruft EclipseLink die abhängigen Objekte direkt ab.

Das nächste kommende Ausgabefragment zeigt Ihnen die Ergebnisse der Ausführung des letzten Codes:


Phones Size :: 1
Projects Size :: 2

Eifrig geladen

Wie bereits erwähnt, ist die Eager-Strategie eine Anforderung an die Laufzeit des Persistenzanbieters, da der Wert eifrig abgerufen werden muss. Schauen wir uns die Entität „Employee“ an, falls die Annotationen „ManyToMany“ und „OneToMany“ so geändert wurden, dass sie sofort abgerufen werden können.

Employee.java


package net.javabeat.eclipselink.data;

import java.util.List;

import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="EmployeeType")
@DiscriminatorValue(value="EMP")
@EntityListeners(value={PersistenceContextListener.class})
public class Employee {

 @Id
 private int employeeId;

 @Basic(optional=false)
 private String employeeName;

 @Embedded
 private EmployeePeriod employeePeriod;

 @OneToMany(mappedBy="employee", cascade=CascadeType.ALL,fetch=FetchType.EAGER)
 private List phones; // OneToMany Eager Load Association

 @ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
 @JoinTable(
 name="employee_project",
 joinColumns={@JoinColumn(name="employeeId")},
 inverseJoinColumns={
 @JoinColumn(table="project",name="projectId"),
 @JoinColumn(table="localproject",name="projectId"),
 @JoinColumn(table="globalproject",name="projectId")}
 )
 private List projects; // ManyToMany EAGER Load Association

 @OneToOne(cascade={CascadeType.ALL},fetch=FetchType.LAZY,targetEntity=Address.class)
 @JoinColumns({
 @JoinColumn(name="AddressId",referencedColumnName="AddressId"),
 @JoinColumn(name="EmployeeAddressCountryId",referencedColumnName="AddressCountryId"),
 @JoinColumn(name="EmployeeAddressCityId",referencedColumnName="AddressCityId")}
 )
 private Address address;

public Address getAddress() {
 return address;
 }

public void setAddress(Address address) {
 this.address = address;
 }

public int getEmployeeId() {
 return employeeId;
 }

public void setEmployeeId(int employeeId) {
 this.employeeId = employeeId;
 }

 public String getEmployeeName() {
 return employeeName;
 }

public void setEmployeeName(String employeeName) {
 this.employeeName = employeeName;
 }

 public List getPhones() {
 return phones;
 }

public void setPhones(List phones) {
 this.phones = phones;
 }

public List getProjects() {
 return projects;
 }

public void setProjects(List projects) {
 this.projects = projects;
 }

public EmployeePeriod getEmployeePeriod() {
 return employeePeriod;
 }

public void setEmployeePeriod(EmployeePeriod employeePeriod) {
 this.employeePeriod = employeePeriod;
 }

}

Sehen Sie sich Abbildung 1.7 an, die Ihnen die Auswirkungen der Verwendung von Eager zeigt.

Auswirkungen der Verwendung von Eager

Abbildung 1.7

  • Alle abhängigen Objekte werden eifrig abgerufen, ohne dass auf die Attribute zugegriffen werden muss.
  • Die Adresse wird bereits eifrig abgerufen, da wir das Eclipselink-Weben entfernt haben, das zum Aktivieren des OneToOne- und ManyToOne-Lazy-Loadings verwendet werden könnte. Immer wenn Weaving weggelassen wird, lädt der Eclipselink-Persistenzanbieter diese Zuordnungen (OneToOne und ManyToOne) eifrig.

Verwenden von @Baisc zur Bereitstellung des Abruftyps

Typischerweise ist die Verwendung von @Basic zum Bereitstellen eines Abruftypwerts identisch mit dem, was Sie gesehen haben, wenn es darum geht, einen solchen Wert mithilfe einer der Assoziationsanmerkungen bereitzustellen. Denken Sie jedoch daran, dass @Basic nicht für nicht-primitive Datentypen verwendet werden kann (auch die Verwendung anderer Typen mit @Basic ist zulässig).

Sie können die Verwendung von @Basic für den Mitarbeiternamen ausprobieren und die Unterschiede sehen, wenn Sie das Weben aktiviert haben. Sie werden auf jeden Fall das gleiche Ergebnis erhalten, wie Sie es gesehen haben.

Zusammenfassung

Beim Lazy- und Eager-Load handelt es sich um Strategien, die der Persistence Provider in Betracht zieht, um die Leistung der entwickelten Anwendungen zu optimieren, insbesondere solcher, die eine große Menge an Objekten und deren Zuordnungen verwalten können. Die Lazy-Load-Strategie ist ein Hinweis für die Laufzeit des Persistenzanbieters, das Abrufen der zugehörigen Objekte zu verschieben, bis der Zugriff auf die zugehörigen Attribute erfolgt ist. Andererseits die Eager-Strategie, bei der die zugehörigen Objekte direkt abgerufen werden. Die Java Persistence API bietet Ihnen zwei verschiedene Möglichkeiten, den Abruftyp zu bestimmen, entweder @Basic oder mithilfe einer der Assoziationsanmerkungen (OneToOne, OneToMany, ManyToOne und ManyToMany).

Kommentar verfassen

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

Nach oben scrollen