Wie ich in diesem Artikel erklärt habe , sollten Sie die JPA-Methoden die meiste Zeit bevorzugen, und dieupdate für Stapelverarbeitungsaufgaben .
Eine JPA- oder Hibernate-Entität kann sich in einem der folgenden vier Zustände befinden:
- Vorübergehend (neu)
- Verwaltet (dauerhaft)
- Freistehend
- Entfernt (gelöscht)
Der Übergang von einem Status in den anderen erfolgt über die Methoden EntityManager oder Session.
Beispielsweise stellt der JPA EntityManagerdie folgenden Entitätsstatusübergangsmethoden bereit.

Die Ruhe Sessionimplementiert alle JPA EntityManagerMethoden und bietet einige zusätzliche Entität Zustandsübergangsverfahren wie save, saveOrUpdateund update.

Fortdauern
Um den Status einer Entität von Transient (Neu) in Verwaltet (Persistent) zu ändern, können Sie die persistvon der JPA angebotene Methode verwenden, die auch vom Ruhezustand EntityManagergeerbt wird Session.
Die persistMethode löst eine aus, PersistEventdie vom DefaultPersistEventListenerHibernate-Ereignis-Listener verarbeitet wird.
Daher bei der Ausführung des folgenden Testfalls:
doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
entityManager.persist(book);
LOGGER.info(
"Persisting the Book entity with the id: {}",
book.getId()
);
});
Der Ruhezustand generiert die folgenden SQL-Anweisungen:
CALL NEXT VALUE FOR hibernate_sequence
-- Persisting the Book entity with the id: 1
INSERT INTO book (
author,
isbn,
title,
id
)
VALUES (
'Vlad Mihalcea',
'978-9730228236',
'High-Performance Java Persistence',
1
)
Beachten Sie, dass das idzugewiesen wird, bevor die BookEntität an den aktuellen Persistenzkontext angehängt wird. Dies ist erforderlich, da die verwalteten Entitäten in einer MapStruktur gespeichert sind, in der der Schlüssel aus dem Entitätstyp und seiner Kennung besteht und der Wert die Entitätsreferenz ist. Dies ist der Grund, warum die JPA EntityManagerund der WinterschlafSession als First-Level-Cache bezeichnet werden.
Beim Aufruf persistwird die Entität nur an den aktuell ausgeführten Persistenzkontext angehängt, und das INSERT kann bis zum verschoben werdenflush aufgerufen wird.
Die einzige Ausnahme ist der IDENTITY-Generator, der das INSERT sofort auslöst, da nur so die Entitätskennung abgerufen werden kann. Aus diesem Grund kann Hibernate mithilfe des IDENTITY-Generators keine Batch-Einfügungen für Entitäten durchführen. Weitere Informationen zu diesem Thema finden Sie in diesem Artikel .
sparen
Die Hibernate-spezifische saveMethode ist älter als JPA und seit Beginn des Hibernate-Projekts verfügbar.
Die saveMethode löst eine aus, SaveOrUpdateEventdie vom DefaultSaveOrUpdateEventListenerHibernate-Ereignis-Listener verarbeitet wird. Daher entspricht die saveMethode den Methoden updateund saveOrUpdate.
saveBetrachten Sie den folgenden Testfall, um zu sehen, wie die Methode funktioniert:
doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
Session session = entityManager.unwrap(Session.class);
Long id = (Long) session.save(book);
LOGGER.info(
"Saving the Book entity with the id: {}",
id
);
});
Wenn Sie den obigen Testfall ausführen, generiert Hibernate die folgenden SQL-Anweisungen:
CALL NEXT VALUE FOR hibernate_sequence
-- Saving the Book entity with the id: 1
INSERT INTO book (
author,
isbn,
title,
id
)
VALUES (
'Vlad Mihalcea',
'978-9730228236',
'High-Performance Java Persistence',
1
)
Wie Sie sehen können, ist das Ergebnis identisch mit dem persistMethodenaufruf. Im Gegensatz dazu persistgibt die saveMethode jedoch die Entitätskennung zurück.
Weitere Informationen finden Sie in diesem Artikel .
Aktualisieren
Die Hibernate-spezifische updateMethode soll den Dirty-Checking-Mechanismus umgehen und eine Entitätsaktualisierung zum Flush-Zeitpunkt erzwingen.
Die updateMethode löst eine aus, SaveOrUpdateEventdie vom DefaultSaveOrUpdateEventListenerHibernate-Ereignis-Listener verarbeitet wird. Daher entspricht die updateMethode den Methoden saveund saveOrUpdate.
Um zu sehen, wie die updateMethode funktioniert, betrachten Sie das folgende Beispiel, in dem eine BookEntität in einer Transaktion beibehalten wird. Anschließend wird sie geändert, während sich die Entität im getrennten Zustand befindet, und das SQL-UPDATE wird mithilfe des updateMethodenaufrufs erzwungen.
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
entityManager.persist(book);
return book;
});
LOGGER.info("Modifying the Book entity");
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
doInJPA(entityManager -> {
Session session = entityManager.unwrap(Session.class);
session.update(_book);
LOGGER.info("Updating the Book entity");
});
Bei der Ausführung des obigen Testfalls generiert Hibernate die folgenden SQL-Anweisungen:
CALL NEXT VALUE FOR hibernate_sequence
INSERT INTO book (
author,
isbn,
title,
id
)
VALUES (
'Vlad Mihalcea',
'978-9730228236',
'High-Performance Java Persistence',
1
)
-- Modifying the Book entity
-- Updating the Book entity
UPDATE
book
SET
author = 'Vlad Mihalcea',
isbn = '978-9730228236',
title = 'High-Performance Java Persistence, 2nd edition'
WHERE
id = 1
Beachten Sie, dass das UPDATEwährend des Persistence Context Flushs unmittelbar vor dem Festschreiben ausgeführt wird. Deshalb wird die Updating the Book entityNachricht zuerst protokolliert.
Verwenden @SelectBeforeUpdate, um unnötige Updates zu vermeiden
Jetzt wird das UPDATE immer ausgeführt, auch wenn die Entität im getrennten Zustand nicht geändert wurde. Um dies zu verhindern, können Sie die @SelectBeforeUpdateAnnotation Hibernate verwenden, die eine SELECTabgerufene Anweisung auslöstloaded state die dann vom Dirty-Checking-Mechanismus verwendet wird.
Wenn wir also die BookEntität mit der @SelectBeforeUpdateAnnotation versehen:
@Entity(name = "Book")
@Table(name = "book")
@SelectBeforeUpdate
public class Book {
//Code omitted for brevity
}
Führen Sie den folgenden Testfall aus:
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
entityManager.persist(book);
return book;
});
doInJPA(entityManager -> {
Session session = entityManager.unwrap(Session.class);
session.update(_book);
});
Hibernate führt die folgenden SQL-Anweisungen aus:
INSERT INTO book (
author,
isbn,
title,
id
)
VALUES (
'Vlad Mihalcea',
'978-9730228236',
'High-Performance Java Persistence',
1
)
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
Beachten Sie, dass diesmal keine UPDATEAusführung erfolgt, da der Mechanismus für die Überprüfung des Ruhezustands im Ruhezustand festgestellt hat, dass die Entität nicht geändert wurde.
SaveOrUpdate
Die Hibernate-spezifische saveOrUpdateMethode ist nur ein Alias für saveund update.
Die saveOrUpdateMethode löst eine aus, SaveOrUpdateEventdie vom DefaultSaveOrUpdateEventListenerHibernate-Ereignis-Listener verarbeitet wird. Daher entspricht die updateMethode den Methoden saveund saveOrUpdate.
Jetzt können Sie verwenden, saveOrUpdatewenn Sie eine Entität beibehalten oder eine erzwingen möchten, UPDATEwie im folgenden Beispiel dargestellt.
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");
doInJPA(entityManager -> {
Session session = entityManager.unwrap(Session.class);
session.saveOrUpdate(_book);
});
Vorsicht vor dem NonUniqueObjectException
Ein Problem, das bei und auftreten savekann update, saveOrUpdatebesteht 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 die 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)
Verschmelzen
Um dies zu vermeiden NonUniqueObjectException, müssen Sie mergedie von der JPA angebotene EntityManagerund auch vom Ruhezustand geerbte Methode verwenden Session.
Wie in diesem Artikel erläutert , mergeruft der Benutzer einen neuen Entitätsschnappschuss aus der Datenbank ab, wenn im Persistenzkontext keine Entitätsreferenz gefunden wird, und kopiert den Status der getrennten Entität, die an die mergeMethode übergeben wurde.
Die mergeMethode löst eine aus, MergeEventdie vom DefaultMergeEventListenerHibernate-Ereignis-Listener verarbeitet wird.
Um zu sehen, wie die mergeMethode funktioniert, betrachten Sie das folgende Beispiel, in dem eine BookEntität in einer Transaktion beibehalten wird. Anschließend wird sie geändert, während sich die Entität im getrennten Zustand befindet, und die getrennte Entität mergein einem Persistenzkontext der Teilsequenz übergeben.
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
entityManager.persist(book);
return book;
});
LOGGER.info("Modifying the Book entity");
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
doInJPA(entityManager -> {
Book book = entityManager.merge(_book);
LOGGER.info("Merging the Book entity");
assertFalse(book == _book);
});
Beim Ausführen des obigen Testfalls hat Hibernate die folgenden SQL-Anweisungen ausgeführt:
INSERT INTO book (
author,
isbn,
title,
id
)
VALUES (
'Vlad Mihalcea',
'978-9730228236',
'High-Performance Java Persistence',
1
)
-- Modifying the Book entity
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
Beachten Sie, dass sich die von zurückgegebene Entitätsreferenz von der getrennten mergeunterscheidet, die wir an die mergeMethode übergeben haben.
Obwohl Sie mergebeim Kopieren des Status der getrennten Entität die Verwendung von JPA bevorzugen sollten , kann das Extra SELECTbeim Ausführen einer Stapelverarbeitungsaufgabe problematisch sein.
Aus diesem Grund sollten Sie die Verwendung bevorzugen, updatewenn Sie sicher sind, dass dem aktuell ausgeführten Persistenzkontext noch keine Entitätsreferenz zugeordnet ist und die getrennte Entität geändert wurde.
Weitere Informationen zu diesem Thema finden Sie in diesem Artikel .
Fazit
Um eine Entität beizubehalten, sollten Sie die JPA- persistMethode verwenden. Das Kopieren des Status der getrennten Entität mergesollte bevorzugt werden. Die updateMethode ist nur für Stapelverarbeitungsaufgaben nützlich. Die saveund saveOrUpdatesind nur Aliase updateund Sie sollten sie wahrscheinlich überhaupt nicht verwenden.
Einige Entwickler rufen saveauch dann auf, wenn die Entität bereits verwaltet wird. Dies ist jedoch ein Fehler und löst ein redundantes Ereignis aus, da bei verwalteten Entitäten das UPDATE automatisch zum Zeitpunkt des Löschens des Persistenzkontexts behandelt wird.
Weitere Informationen finden Sie in diesem Artikel .