Da dies eine sehr häufige Frage ist, habe ich
diesen Artikel geschrieben , auf dem diese Antwort basiert.
Entitätszustände
JPA definiert die folgenden Entitätszustände:
Neu (vorübergehend)
Ein neu erstelltes Objekt, das noch nie einem Ruhezustand Session(aka Persistence Context) zugeordnet wurde und keiner Datenbanktabellenzeile zugeordnet ist, befindet sich im Status Neu (vorübergehend).
Um persistent zu werden, müssen wir entweder die EntityManager#persistMethode explizit aufrufen oder den transitiven Persistenzmechanismus verwenden.
Dauerhaft (verwaltet)
Eine persistente Entität wurde einer Datenbanktabellenzeile zugeordnet und wird vom aktuell ausgeführten Persistenzkontext verwaltet. Jede an einer solchen Entität vorgenommene Änderung wird erkannt und an die Datenbank weitergegeben (während der Sitzungsspülzeit).
Mit Hibernate müssen wir keine INSERT / UPDATE / DELETE-Anweisungen mehr ausführen. Hibernate verwendet einen Transaktions-Write-Behind- Arbeitsstil und Änderungen werden im allerletzten verantwortlichen Moment während des Stroms synchronisiertSession Spülzeit .
Freistehend
Sobald der aktuell ausgeführte Persistenzkontext geschlossen ist, werden alle zuvor verwalteten Entitäten getrennt. Aufeinanderfolgende Änderungen werden nicht mehr verfolgt und es findet keine automatische Datenbanksynchronisierung statt.
Übergänge des Entitätsstatus
Sie können den Entitätsstatus mit verschiedenen Methoden ändern, die durch das definiert sind EntityManager Schnittstelle .
Betrachten Sie das folgende Diagramm, um die Übergänge des JPA-Entitätsstatus besser zu verstehen:

Wenn Sie JPA verwenden, um eine getrennte Entität einer aktiven zuzuordnen EntityManager, können Sie die Zusammenführung verwenden verwenden.
Wenn Sie die native Hibernate-API verwenden, mergekönnen Sie außerdem eine getrennte Entität mithilfe der Aktualisierungsmethoden erneut einer aktiven Hibernate-Sitzung zuordnen, wie im folgenden Diagramm dargestellt:

Zusammenführen einer getrennten Einheit
Durch die Zusammenführung wird der Status der getrennten Entität (Quelle) in eine Instanz einer verwalteten Entität (Ziel) kopiert.
Angenommen, wir haben die folgende BookEntität beibehalten, und jetzt wird die Entität getrennt, da die EntityManagerzum Bestehen der Entität verwendete Entität geschlossen wurde:
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
entityManager.persist(book);
return book;
});
Während sich die Entität im getrennten Zustand befindet, ändern wir sie wie folgt:
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
Jetzt möchten wir die Änderungen an die Datenbank weitergeben, damit wir die mergeMethode aufrufen können :
doInJPA(entityManager -> {
Book book = entityManager.merge(_book);
LOGGER.info("Merging the Book entity");
assertFalse(book == _book);
});
Und Hibernate wird die folgenden SQL-Anweisungen ausführen:
SELECT
b.id,
b.author AS author2_0_,
b.isbn AS isbn3_0_,
b.title AS title4_0_
FROM
book b
WHERE
b.id = 1
-- Merging the Book entity
UPDATE
book
SET
author = 'Vlad Mihalcea',
isbn = '978-9730228236',
title = 'High-Performance Java Persistence, 2nd edition'
WHERE
id = 1
Wenn die fusionierende Entität im aktuellen keine Entsprechung hat EntityManager , wird ein neuer Entitätsschnappschuss aus der Datenbank abgerufen.
Sobald eine verwaltete Entität vorhanden ist, kopiert JPA den Status der getrennten Entität auf die aktuell verwaltete Entität. Während des Persistenzkontextsflush wird ein UPDATE generiert, wenn der Dirty-Checking-Mechanismus feststellt, dass sich die verwaltete Entität geändert hat.
Bei Verwendung mergebleibt die Instanz des getrennten Objekts auch nach dem Zusammenführungsvorgang weiterhin getrennt.
Erneutes Anhängen einer getrennten Entität
Ruhezustand, aber nicht JPA unterstützt das erneute Anhängen über die updateMethode.
Ein Ruhezustand Sessionkann nur ein Entitätsobjekt für eine bestimmte Datenbankzeile zuordnen. Dies liegt daran, dass der Persistenzkontext als speicherinterner Cache (Cache der ersten Ebene) fungiert und einem bestimmten Schlüssel (Entitätstyp und Datenbankkennung) nur ein Wert (Entität) zugeordnet ist.
Eine Entität kann nur dann erneut zugeordnet werden, wenn dem aktuellen Ruhezustand noch kein anderes JVM-Objekt (das mit derselben Datenbankzeile übereinstimmt) bereits zugeordnet ist Session.
In Anbetracht dessen Book, dass wir die BookEntität beibehalten haben und dass wir sie geändert haben, als sich die Entität im getrennten Zustand befand:
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
entityManager.persist(book);
return book;
});
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
Wir können die abgetrennte Entität wie folgt wieder anbringen:
doInJPA(entityManager -> {
Session session = entityManager.unwrap(Session.class);
session.update(_book);
LOGGER.info("Updating the Book entity");
});
Und Hibernate führt die folgende SQL-Anweisung aus:
-- Updating the Book entity
UPDATE
book
SET
author = 'Vlad Mihalcea',
isbn = '978-9730228236',
title = 'High-Performance Java Persistence, 2nd edition'
WHERE
id = 1
Die updateMethode erfordert, dass Sie in unwrapden EntityManagerRuhezustand wechseln Session.
Im Gegensatz dazu mergewird die bereitgestellte getrennte Entität dem aktuellen Persistenzkontext neu zugeordnet, und während des Flushs wird ein UPDATE geplant, unabhängig davon, ob die Entität geändert wurde oder nicht.
Um dies zu verhindern, können Sie die @SelectBeforeUpdateAnnotation Hibernate verwenden, die eine SELECT-Anweisung auslöst, die den geladenen Status abruft, der dann vom Dirty-Checking-Mechanismus verwendet wird.
@Entity(name = "Book")
@Table(name = "book")
@SelectBeforeUpdate
public class Book {
//Code omitted for brevity
}
Achten Sie auf die NonUniqueObjectException
Ein Problem, das auftreten kann, updatebesteht darin, dass der Persistenzkontext bereits eine Entitätsreferenz mit derselben ID und demselben Typ wie im folgenden Beispiel enthält:
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
Session session = entityManager.unwrap(Session.class);
session.saveOrUpdate(book);
return book;
});
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
try {
doInJPA(entityManager -> {
Book book = entityManager.find(
Book.class,
_book.getId()
);
Session session = entityManager.unwrap(Session.class);
session.saveOrUpdate(_book);
});
} catch (NonUniqueObjectException e) {
LOGGER.error(
"The Persistence Context cannot hold " +
"two representations of the same entity",
e
);
}
Wenn Sie den obigen Testfall ausführen, wird Hibernate a auslösen, NonUniqueObjectExceptionda der zweite EntityManagerbereits eine BookEntität mit derselben Kennung enthält wie die, an die wir übergeben update, und der Persistenzkontext nicht zwei Darstellungen derselben Entität enthalten kann.
org.hibernate.NonUniqueObjectException:
A different object with the same identifier value was already associated with the session : [com.vladmihalcea.book.hpjp.hibernate.pc.Book#1]
at org.hibernate.engine.internal.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:651)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:284)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:227)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:92)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:73)
at org.hibernate.internal.SessionImpl.fireSaveOrUpdate(SessionImpl.java:682)
at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:674)
Fazit
Die mergeMethode ist vorzuziehen, wenn Sie optimistische Sperren verwenden, um verlorene Aktualisierungen zu vermeiden. Weitere Informationen zu diesem Thema finden Sie in diesem Artikel .
Dies updateist gut für Stapelaktualisierungen, da es die zusätzliche SELECT-Anweisung verhindern kann, die durch die mergeOperation generiert wird , wodurch die Ausführungszeit für Stapelaktualisierungen verkürzt wird.
refresh()getrennte Entitäten nicht zulässt . Wenn ich mir die 2.0-Spezifikation ansehe, sehe ich keine Rechtfertigung. nur dass es nicht erlaubt ist.