6.1 Tiefgreifendes Abhängigkeitsmanagement
Und noch einige weitere nützliche und interessante Dinge zu @OneToMany- Annotationen und dergleichen. Sie alle verfügen über vier häufig verwendete Optionen:
- cascade = CascadeType.ALL
- orphanRemoval = true
- fetch = FetchType.LAZY
Jetzt werden wir sie genauer analysieren. Und wir beginnen mit dem Interessantesten – CascadeType . Dieser Parameter bestimmt, was mit abhängigen Entitäten passieren soll, wenn wir die Hauptentität ändern.
Die JPA-Spezifikation hat die folgenden Werte für diesen Parameter:
- ALLE
- FORTDAUERN
- VERSCHMELZEN
- ENTFERNEN
- AKTUALISIERUNG
- ABLÖSEN
Allerdings erweitert Hibernate diese Spezifikation um drei weitere Optionen:
- REPLIZIEREN
- SAVE_UPDATE
- SPERREN
Es gibt natürlich eine starke Parallele zur Datenbank und deren CONSTRANIS. Allerdings gibt es auch Unterschiede. Hibernate versucht, die eigentliche Arbeit mit der Datenbank so weit wie möglich zu verbergen, daher geht es bei diesen Hibernate-Kaskaden genau um Entity-Objekte.
6.2 CascadeType
Der Cascade-Parameter beschreibt, was mit abhängigen Objekten passieren soll, wenn wir ihr übergeordnetes Objekt (Masterobjekt) ändern. Am häufigsten wird dieser Parameter zusammen mit Anmerkungen verwendet, die Objektabhängigkeiten beschreiben:
Beispiel:
OneToOne(cascade = CascadeType.ALL)
Oder so:
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
Es kann auch als separate Anmerkung geschrieben werden:
@Cascade({ org.hibernate.annotations.CascadeType.ALL })
Lassen Sie uns nun mehr darüber sprechen, was diese Anmerkungen bedeuten.
6.3 ALLE, PERSIST, MERGE
CascadeType.ALL
bedeutet, dass alle Aktionen , die wir mit dem übergeordneten Objekt ausführen, für seine abhängigen Objekte wiederholt werden müssen.
CascadeType.PERSIST
bedeutet, dass, wenn wir das übergeordnete Objekt in der Datenbank speichern, das Gleiche auch mit seinen abhängigen Objekten erfolgen muss. Beispiel:
@Entity
@Table(name="employee")
class Employee {
@Column(name="id")
public Integer id;
@OneToOne(cascade = CascadeType.PERSIST, mappedBy="task")
private EmployeeTask task;
}
Ein Beispiel für die Arbeit mit dieser Klasse:
Employee director = new Employee();
EmployeeTask task = new EmployeeTask();
director.task = task;
session.persist(director);
session.flush();
Wir speichern nur ein Objekt vom Typ Employee, sein abhängiges Objekt EmployeeTask wird automatisch in der Datenbank gespeichert.
CascadeType.MERGE
bedeutet, dass, wenn wir das übergeordnete Objekt in der Datenbank aktualisieren, das Gleiche auch mit seinen abhängigen Objekten erfolgen muss.
6.4 ENTFERNEN, LÖSCHEN, ENTFERNEN
CascadeType.REMOVE
bedeutet, dass, wenn wir ein übergeordnetes Objekt in der Datenbank löschen, das Gleiche auch mit seinen abhängigen Objekten geschehen muss.
CascadeType.DELETE
Bedeutet das gleiche. Das sind Synonyme. Nur aus unterschiedlichen Spezifikationen.
CascadeType.DETACH
bedeutet, dass, wenn wir das übergeordnete Objekt aus der Sitzung entfernen, dasselbe mit seinen abhängigen Objekten geschehen muss. Beispiel:
@Entity
@Table(name="employee")
class Employee {
@Column(name="id")
public Integer id;
@OneToOne(cascade = CascadeType.DETACH, mappedBy="task")
private EmployeeTask task;
}
Ein Beispiel für die Arbeit mit dieser Klasse:
Employee director = new Employee();
EmployeeTask task = new EmployeeTask();
director.task = task;
director.task = task;
session.flush();
assertThat(session.contains(director)).isTrue();
assertThat(session.contains(task)).isTrue();
session.detach(director);
assertThat(session.contains(director)).isFalse();
assertThat(session.contains(task)).isFalse();
CascadeType.REFRESH
und CascadeType.SAVE_UPDATE
funktionieren auf die gleiche Weise, wie wir es erwarten – sie duplizieren die Aktionen, die mit dem übergeordneten Objekt ausgeführt werden, auf sein abhängiges Objekt.
6.5 Option zum Entfernen von Waisen
Manchmal stoßen Sie auch auf den Parameter orphan
. Dies ist die Abkürzung für Orphan Removal. Es wird verwendet, um sicherzustellen, dass es keine untergeordneten Entitäten ohne übergeordnete Entitäten gibt.
OneToOne(orphan = true)
Wenn dieser Parameter auf „true“ gesetzt ist, wird die untergeordnete Entität gelöscht, wenn sie verschwunden istalle Links. Es ist nicht genau dasselbe wie Cascade.REMOVE
.
Es kann vorkommen, dass mehrere übergeordnete Entitäten auf ein untergeordnetes Element verweisen. Dann ist es von Vorteil, dass es nicht zusammen mit der Löschung der übergeordneten Entität gelöscht wird, sondern nur, wenn alle Verweise darauf ungültig gemacht werden.
Nehmen wir an, Sie haben eine Klasse:
@Entity
@Table(name="user")
class Employee {
@Column(name="id")
public Integer id;
@OneToMany(cascade = CascadeType.ALL, orphan = true)
@JoinColumn(name = "employee_id")
private Set<EmployeeTask> tasks = new HashSet<EmployeeTask>();
}
Employee director = session.find(Employee.class, 4);
EmployeeTask task = director.tasks.get(0);
director.tasks.remove(task)
session.persist(director);
session.flush();
Das EmployeeTask-Objekt wird gelöscht, da keine Verweise mehr darauf vorhanden sind. Gleichzeitig hat niemand das übergeordnete Objekt gelöscht.
6.6 Abrufoption
Mit der Abrufoption können Sie steuern, wie abhängige Objekte geladen werden. Es nimmt normalerweise einen von zwei Werten an:
FetchType.LAZY
FetchType.EAGER
Das ist ein sehr interessantes Thema mit diversen Fallstricken, deshalb sollte ich besser in einem separaten Vortrag darüber sprechen.