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 EntityManager
die folgenden Entitätsstatusübergangsmethoden bereit.
Die Ruhe Session
implementiert alle JPA EntityManager
Methoden und bietet einige zusätzliche Entität Zustandsübergangsverfahren wie save
, saveOrUpdate
und update
.
Fortdauern
Um den Status einer Entität von Transient (Neu) in Verwaltet (Persistent) zu ändern, können Sie die persist
von der JPA angebotene Methode verwenden, die auch vom Ruhezustand EntityManager
geerbt wird Session
.
Die persist
Methode löst eine aus, PersistEvent
die vom DefaultPersistEventListener
Hibernate-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 id
zugewiesen wird, bevor die Book
Entität an den aktuellen Persistenzkontext angehängt wird. Dies ist erforderlich, da die verwalteten Entitäten in einer Map
Struktur 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 EntityManager
und der WinterschlafSession
als First-Level-Cache bezeichnet werden.
Beim Aufruf persist
wird 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 save
Methode ist älter als JPA und seit Beginn des Hibernate-Projekts verfügbar.
Die save
Methode löst eine aus, SaveOrUpdateEvent
die vom DefaultSaveOrUpdateEventListener
Hibernate-Ereignis-Listener verarbeitet wird. Daher entspricht die save
Methode den Methoden update
und saveOrUpdate
.
save
Betrachten 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 persist
Methodenaufruf. Im Gegensatz dazu persist
gibt die save
Methode jedoch die Entitätskennung zurück.
Weitere Informationen finden Sie in diesem Artikel .
Aktualisieren
Die Hibernate-spezifische update
Methode soll den Dirty-Checking-Mechanismus umgehen und eine Entitätsaktualisierung zum Flush-Zeitpunkt erzwingen.
Die update
Methode löst eine aus, SaveOrUpdateEvent
die vom DefaultSaveOrUpdateEventListener
Hibernate-Ereignis-Listener verarbeitet wird. Daher entspricht die update
Methode den Methoden save
und saveOrUpdate
.
Um zu sehen, wie die update
Methode funktioniert, betrachten Sie das folgende Beispiel, in dem eine Book
Entitä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 update
Methodenaufrufs 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 UPDATE
während des Persistence Context Flushs unmittelbar vor dem Festschreiben ausgeführt wird. Deshalb wird die Updating the Book entity
Nachricht 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 @SelectBeforeUpdate
Annotation Hibernate verwenden, die eine SELECT
abgerufene Anweisung auslöstloaded state
die dann vom Dirty-Checking-Mechanismus verwendet wird.
Wenn wir also die Book
Entität mit der @SelectBeforeUpdate
Annotation 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 UPDATE
Ausfü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 saveOrUpdate
Methode ist nur ein Alias für save
und update
.
Die saveOrUpdate
Methode löst eine aus, SaveOrUpdateEvent
die vom DefaultSaveOrUpdateEventListener
Hibernate-Ereignis-Listener verarbeitet wird. Daher entspricht die update
Methode den Methoden save
und saveOrUpdate
.
Jetzt können Sie verwenden, saveOrUpdate
wenn Sie eine Entität beibehalten oder eine erzwingen möchten, UPDATE
wie 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 save
kann update
, saveOrUpdate
besteht 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, NonUniqueObjectException
da die zweite EntityManager
bereits eine Book
Entitä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 merge
die von der JPA angebotene EntityManager
und auch vom Ruhezustand geerbte Methode verwenden Session
.
Wie in diesem Artikel erläutert , merge
ruft 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 merge
Methode übergeben wurde.
Die merge
Methode löst eine aus, MergeEvent
die vom DefaultMergeEventListener
Hibernate-Ereignis-Listener verarbeitet wird.
Um zu sehen, wie die merge
Methode funktioniert, betrachten Sie das folgende Beispiel, in dem eine Book
Entitä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 merge
in 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 merge
unterscheidet, die wir an die merge
Methode übergeben haben.
Obwohl Sie merge
beim Kopieren des Status der getrennten Entität die Verwendung von JPA bevorzugen sollten , kann das Extra SELECT
beim Ausführen einer Stapelverarbeitungsaufgabe problematisch sein.
Aus diesem Grund sollten Sie die Verwendung bevorzugen, update
wenn 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- persist
Methode verwenden. Das Kopieren des Status der getrennten Entität merge
sollte bevorzugt werden. Die update
Methode ist nur für Stapelverarbeitungsaufgaben nützlich. Die save
und saveOrUpdate
sind nur Aliase update
und Sie sollten sie wahrscheinlich überhaupt nicht verwenden.
Einige Entwickler rufen save
auch 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 .