Jak wyjaśniłem w tym artykule , powinieneś preferować metody JPA przez większość czasu i updatezadania przetwarzania wsadowego.
WZP lub Hibernacja może znajdować się w jednym z następujących czterech stanów:
- Przejściowy (nowy)
- Zarządzane (trwałe)
- Oderwany
- Usunięte (usunięte)
Przejście z jednego stanu do drugiego odbywa się za pomocą metod EntityManager lub Session.
Na przykład WZP EntityManagerzapewnia następujące metody zmiany stanu encji.

Hibernacji Sessionrealizuje wszystkie JPA EntityManagermetody i zawiera kilka dodatkowych metod, takich jak zmiana stanu podmiotu save, saveOrUpdatei update.

Trwać
Aby zmienić stan bytu z Przejściowy (Nowy) na Zarządzany (Utrwalony), możemy użyć persistmetody oferowanej przez JPA, EntityManagerktóra jest również dziedziczona przez Hibernację Session.
persistSposób wyzwala PersistEventktóre są przetwarzane przez DefaultPersistEventListenerdetektor zdarzeń hibernacji.
Dlatego podczas wykonywania następującego przypadku testowego:
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()
);
});
Hibernacja generuje następujące instrukcje SQL:
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
)
Zauważ, że idprzypisuje się to przed dołączeniem Bookbytu do bieżącego kontekstu trwałości. Jest to konieczne, ponieważ jednostki zarządzane są przechowywane w Mapstrukturze, w której klucz jest tworzony przez typ jednostki i jej identyfikator, a wartością jest odwołanie do jednostki. To jest powód, dla którego JPA EntityManageri HibernacjaSession są znane jako pamięć podręczna pierwszego poziomu.
Podczas połączenia persistjednostka jest dołączona tylko do aktualnie działającego kontekstu trwałości, a WSTAW można odłożyć do momentuflush zostanie wywołane.
Jedynym wyjątkiem jest generator TOŻSAMOŚCI, który natychmiast uruchamia WSTAW, ponieważ jest to jedyny sposób, w jaki może uzyskać identyfikator jednostki. Z tego powodu Hibernacja nie może wsadowo wstawiać wstawek dla encji korzystających z generatora TOŻSAMOŚCI. Więcej informacji na ten temat można znaleźć w tym artykule .
Zapisać
saveMetoda specyficzna dla Hibernacji poprzedza JPA i jest dostępna od początku projektu Hibernacja.
saveSposób wyzwala SaveOrUpdateEventktóre są przetwarzane przez DefaultSaveOrUpdateEventListenerdetektor zdarzeń hibernacji. Dlatego savemetoda jest równoważna z metodami updatei saveOrUpdate.
Aby zobaczyć, jak savedziała metoda, rozważ następujący przypadek testowy:
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
);
});
Po uruchomieniu powyższego przypadku testowego Hibernacja generuje następujące instrukcje SQL:
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
)
Jak widać, wynik jest identyczny z persistwywołaniem metody. Jednak w przeciwieństwie persistdosave metody zwraca identyfikator jednostki.
Aby uzyskać więcej informacji, sprawdź ten artykuł .
Aktualizacja
updateMetoda specyficzna dla hibernacji ma na celu ominięcie mechanizmu sprawdzania brudności i wymuszenie aktualizacji encji w czasie opróżniania.
updateSposób wyzwala SaveOrUpdateEventktóre są przetwarzane przez DefaultSaveOrUpdateEventListenerdetektor zdarzeń hibernacji. Dlatego updatemetoda jest równoważna z metodami savei saveOrUpdate.
Aby zobaczyć, jak updatedziała metoda, rozważ następujący przykład, który utrzymuje Bookjednostkę w jednej transakcji, a następnie modyfikuje ją, gdy jednostka jest w stanie odłączonym, i wymusza aktualizację SQL za pomocą updatewywołania metody.
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");
});
Podczas wykonywania powyższego przypadku testowego Hibernacja generuje następujące instrukcje SQL:
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
Zauważ, że UPDATEoperacja jest wykonywana podczas opróżniania kontekstu trwałego, tuż przed zatwierdzeniem, i dlatego Updating the Book entitywiadomość jest najpierw logowana.
Używanie w @SelectBeforeUpdatecelu uniknięcia niepotrzebnych aktualizacji
Teraz UPDATE zawsze będzie wykonywane, nawet jeśli jednostka nie została zmieniona w stanie odłączonym. Aby temu zapobiec, możesz użyć @SelectBeforeUpdateadnotacji Hibernacja, która uruchomi SELECTpobrane polecenieloaded state która zostanie następnie wykorzystana przez mechanizm sprawdzania nieczystości.
Jeśli więc dodamy adnotację do Bookjednostki za pomocą @SelectBeforeUpdateadnotacji:
@Entity(name = "Book")
@Table(name = "book")
@SelectBeforeUpdate
public class Book {
//Code omitted for brevity
}
I wykonaj następujący przypadek testowy:
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);
});
Hibernacja wykonuje następujące instrukcje SQL:
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
Zauważ, że tym razem nie jest UPDATEwykonywane, ponieważ mechanizm hibernacji „brudnego sprawdzania” wykrył, że jednostka nie została zmodyfikowana.
SaveOrUpdate
saveOrUpdateMetoda specyficzna dla hibernacji jest tylko aliasem dla savei update.
saveOrUpdateSposób wyzwala SaveOrUpdateEventktóre są przetwarzane przez DefaultSaveOrUpdateEventListenerdetektor zdarzeń hibernacji. Dlatego updatemetoda jest równoważna z metodami savei saveOrUpdate.
Teraz możesz użyć, saveOrUpdategdy chcesz utrwalić byt lub wymusić element, UPDATEjak pokazano w poniższym przykładzie.
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);
});
Uważaj na NonUniqueObjectException
Jednym z problemów, który może wystąpić z save, updatei saveOrUpdatejest, jeśli kontekst trwałości zawiera już odwołanie do jednostki o tym samym identyfikatorze i tego samego typu, jak w poniższym przykładzie:
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
);
}
Teraz, wykonując powyższy przypadek testowy, Hibernacja rzuci a, NonUniqueObjectExceptionponieważ drugi EntityManagerzawiera już Bookencję o tym samym identyfikatorze, co ten, który przekazujemy update, a kontekst trwałości nie może przechowywać dwóch reprezentacji tej samej encji.
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)
Łączyć
Aby tego uniknąć NonUniqueObjectException, musisz użyć mergemetody oferowanej przez JPA EntityManageri dziedziczonej również przez Hibernację Session.
Jak wyjaśniono w tym artykule , mergepobiera nową migawkę encji z bazy danych, jeśli w kontekście trwałości nie znaleziono odwołania do encji, i kopiuje stan odłączonej encji przekazanej do mergemetody.
mergeSposób wyzwala MergeEventktóre są przetwarzane przez DefaultMergeEventListenerdetektor zdarzeń hibernacji.
Aby zobaczyć, jak mergedziała metoda, rozważ następujący przykład, który utrzymuje Bookbyt w jednej transakcji, a następnie modyfikuje go, gdy jest on w stanie odłączonym, i przekazuje odłączony byt mergew podsekwencji Kontekst trwałości.
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);
});
Podczas uruchamiania powyższego przypadku testowego Hibernacja wykonała następujące instrukcje SQL:
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
Zauważ, że zwrócone przez encję odwołanie do encji mergejest inne niż odłączone, które przekazaliśmy do mergemetody.
Teraz, chociaż powinieneś preferować używanie JPA mergepodczas kopiowania stanu odłączonej jednostki, dodatkowe SELECTmogą być problematyczne podczas wykonywania zadania przetwarzania wsadowego.
Z tego powodu powinieneś preferować używanie, updategdy masz pewność, że żadne odwołanie do encji nie jest już dołączone do aktualnie działającego kontekstu trwałości i że odłączony byt został zmodyfikowany.
Więcej informacji na ten temat można znaleźć w tym artykule .
Wniosek
Aby utrwalić jednostkę, należy użyć persistmetody JPA . Aby skopiować stan odłączonej jednostki, mergepowinno być preferowane. Ta updatemetoda jest przydatna tylko do zadań przetwarzania wsadowego. saveI saveOrUpdateto tylko aliasy updatei nie należy ich używać prawdopodobnie w ogóle.
Niektórzy programiści dzwonią savenawet wtedy, gdy jednostka jest już zarządzana, ale jest to błąd i wyzwala zdarzenie nadmiarowe, ponieważ dla jednostek zarządzanych UPDATE jest automatycznie obsługiwana w czasie opróżniania kontekstu Persistence.
Aby uzyskać więcej informacji, sprawdź ten artykuł .