Jak wyjaśniłem w tym artykule , powinieneś preferować metody JPA przez większość czasu i update
zadania 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 EntityManager
zapewnia następujące metody zmiany stanu encji.
Hibernacji Session
realizuje wszystkie JPA EntityManager
metody i zawiera kilka dodatkowych metod, takich jak zmiana stanu podmiotu save
, saveOrUpdate
i update
.
Trwać
Aby zmienić stan bytu z Przejściowy (Nowy) na Zarządzany (Utrwalony), możemy użyć persist
metody oferowanej przez JPA, EntityManager
która jest również dziedziczona przez Hibernację Session
.
persist
Sposób wyzwala PersistEvent
które są przetwarzane przez DefaultPersistEventListener
detektor 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 id
przypisuje się to przed dołączeniem Book
bytu do bieżącego kontekstu trwałości. Jest to konieczne, ponieważ jednostki zarządzane są przechowywane w Map
strukturze, 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 EntityManager
i HibernacjaSession
są znane jako pamięć podręczna pierwszego poziomu.
Podczas połączenia persist
jednostka 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ć
save
Metoda specyficzna dla Hibernacji poprzedza JPA i jest dostępna od początku projektu Hibernacja.
save
Sposób wyzwala SaveOrUpdateEvent
które są przetwarzane przez DefaultSaveOrUpdateEventListener
detektor zdarzeń hibernacji. Dlatego save
metoda jest równoważna z metodami update
i saveOrUpdate
.
Aby zobaczyć, jak save
dział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 persist
wywołaniem metody. Jednak w przeciwieństwie persist
dosave
metody zwraca identyfikator jednostki.
Aby uzyskać więcej informacji, sprawdź ten artykuł .
Aktualizacja
update
Metoda specyficzna dla hibernacji ma na celu ominięcie mechanizmu sprawdzania brudności i wymuszenie aktualizacji encji w czasie opróżniania.
update
Sposób wyzwala SaveOrUpdateEvent
które są przetwarzane przez DefaultSaveOrUpdateEventListener
detektor zdarzeń hibernacji. Dlatego update
metoda jest równoważna z metodami save
i saveOrUpdate
.
Aby zobaczyć, jak update
działa metoda, rozważ następujący przykład, który utrzymuje Book
jednostkę w jednej transakcji, a następnie modyfikuje ją, gdy jednostka jest w stanie odłączonym, i wymusza aktualizację SQL za pomocą update
wywoł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 UPDATE
operacja jest wykonywana podczas opróżniania kontekstu trwałego, tuż przed zatwierdzeniem, i dlatego Updating the Book entity
wiadomość jest najpierw logowana.
Używanie w @SelectBeforeUpdate
celu 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ć @SelectBeforeUpdate
adnotacji Hibernacja, która uruchomi SELECT
pobrane polecenieloaded state
która zostanie następnie wykorzystana przez mechanizm sprawdzania nieczystości.
Jeśli więc dodamy adnotację do Book
jednostki za pomocą @SelectBeforeUpdate
adnotacji:
@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 UPDATE
wykonywane, ponieważ mechanizm hibernacji „brudnego sprawdzania” wykrył, że jednostka nie została zmodyfikowana.
SaveOrUpdate
saveOrUpdate
Metoda specyficzna dla hibernacji jest tylko aliasem dla save
i update
.
saveOrUpdate
Sposób wyzwala SaveOrUpdateEvent
które są przetwarzane przez DefaultSaveOrUpdateEventListener
detektor zdarzeń hibernacji. Dlatego update
metoda jest równoważna z metodami save
i saveOrUpdate
.
Teraz możesz użyć, saveOrUpdate
gdy chcesz utrwalić byt lub wymusić element, UPDATE
jak 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
, update
i saveOrUpdate
jest, 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, NonUniqueObjectException
ponieważ drugi EntityManager
zawiera już Book
encję 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ć merge
metody oferowanej przez JPA EntityManager
i dziedziczonej również przez Hibernację Session
.
Jak wyjaśniono w tym artykule , merge
pobiera 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 merge
metody.
merge
Sposób wyzwala MergeEvent
które są przetwarzane przez DefaultMergeEventListener
detektor zdarzeń hibernacji.
Aby zobaczyć, jak merge
działa metoda, rozważ następujący przykład, który utrzymuje Book
byt w jednej transakcji, a następnie modyfikuje go, gdy jest on w stanie odłączonym, i przekazuje odłączony byt merge
w 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 merge
jest inne niż odłączone, które przekazaliśmy do merge
metody.
Teraz, chociaż powinieneś preferować używanie JPA merge
podczas kopiowania stanu odłączonej jednostki, dodatkowe SELECT
mogą być problematyczne podczas wykonywania zadania przetwarzania wsadowego.
Z tego powodu powinieneś preferować używanie, update
gdy 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ć persist
metody JPA . Aby skopiować stan odłączonej jednostki, merge
powinno być preferowane. Ta update
metoda jest przydatna tylko do zadań przetwarzania wsadowego. save
I saveOrUpdate
to tylko aliasy update
i nie należy ich używać prawdopodobnie w ogóle.
Niektórzy programiści dzwonią save
nawet 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ł .