Jaka jest różnica między persist, save, merge i update? Którego z nich powinieneś użyć?

JPA i Hibernate zapewniają różne metody utrzymywania nowych i aktualizacji istniejących podmiotów. Możesz wybrać pomiędzy metodami persist JPA oraz merge i Hibernate save I update.

wydaje się, że są 2 pary 2 metod, które robią to samo. Możesz użyć metod persist i save do przechowywania nowego elementu, a metod merge i update do przechowywania zmian oddzielonego elementu w bazie danych. Dlatego wielu programistów zastanawia się, której z tych metod powinni użyć. Przyjrzyjmy się bliżej szczegółom i małym różnicom tych metod.

Specjalne podziękowania dla Steve 'a Ebersole’ a (Lead Developer – Hibernate ORM), który przekazał swoje opinie i Świetne spostrzeżenia na temat niektórych ukrytych szczegółów implementacji Hibernate!

przejścia stanu encji

zanim przejdziemy do szczegółów tych 4 metod, muszę dać Ci szybkie wprowadzenie do Stanów cyklu życia encji JPA.

jeśli jednostka jest dołączona do bieżącego kontekstu trwałości, zarządzany jest jej stan cyklu życia. Oznacza to, że jest on mapowany do rekordu bazy danych. Dostawca trwałości generuje wymagane instrukcje SQL INSERT i UPDATE, aby propagować wszystkie zmiany. Zarządzany podmiot jest również przechowywany w pamięci podręcznej poziomu 1.

gdy tworzysz nową encję, jest ona w stanie przejściowym. Pozostaje w tym stanie, dopóki nie dołączysz go do bieżącego kontekstu trwałości. Pokażę Ci, jak możesz to zrobić za pomocą metody persist JPA i Save Hibernate, w poniższej sekcji. Dopóki jednostka jest w stanie przejściowym, nie jest mapowana do rekordu bazy danych i nie jest zarządzana przez żaden kontekst trwałości.

podmioty w stanie oddzielonego cyklu życia nie są już zarządzane przez kontekst trwałości. Może tak być, ponieważ zamknięto kontekst trwałości lub wyraźnie oddzielono encję od bieżącego kontekstu. W dalszej części tego postu omówię więcej szczegółów na temat tego, jak można ponownie podłączyć te podmioty za pomocą metod merge i Hibernate JPA.

i ostatni stan cyklu życia jest usuwany. Podmioty te były wcześniej zarządzane przez państwo, zanim zaplanowałeś ich usunięcie. Usuwanie podmiotów jest poza zakresem tego posta, więc nie będę wdawał się w zbyt wiele szczegółów na ten temat. Możesz zaplanować usunięcie encji, wywołując metodę remove w interfejsie EntityManager.

utrzymywanie nowej encji za pomocą funkcji persist lub save

podczas tworzenia nowego obiektu encji jest on w stanie przejściowego cyklu życia. Nie mapuje żadnego rekordu bazy danych.

Author a = new Author();a.setFirstName("Thorben");a.setLastName("Janssen");

musisz dołączyć encję do kontekstu trwałości, aby była zarządzana i była przechowywana w bazie danych. W tym celu możesz użyć metody persist JPA lub save Hibernate. Obie metody wydają się robić to samo, ale istnieje kilka różnic.

Specyfikacja vs. Własnościowe API

najbardziej oczywistą różnicą jest to, że specyfikacja JPA definiuje metodę persist. Można go używać we wszystkich implementacjach JPA. Z kolei metoda save jest specyficzna dla hibernacji. W związku z tym nie jest on dostępny w innych implementacjach JPA.

ale jest to istotne tylko wtedy, gdy chcesz być w stanie zastąpić Hibernate inną implementacją JPA, taką jak Eclipse Link lub OpenJPA.

typy zwracania i wykonywanie poleceń SQL

inną oczywistą różnicą między tymi metodami jest ich typ zwracania. Metoda persist JPA zwraca void, a metoda Save Hibernate zwraca klucz podstawowy encji.

to może wydawać się ogromną różnicą, zwłaszcza jeśli przyjrzysz się Javadoc Hibernate i specyfikacji JPA:

  • Javadoc metody zapisu Hibernate stwierdza, że najpierw generuje wartość klucza podstawowego:

    utrzymuje daną instancję przejściową, najpierw przypisując wygenerowany identyfikator.
    Sesja Javadoc.save (entity)

  • nie znajdziesz żadnych informacji na ten temat w specyfikacji JPA. Nie definiuje, kiedy ma być przypisana wartość klucza głównego. Tak więc dostawca trwałości może to zrobić w dowolnym momencie pomiędzy wywołaniem metody persist a flush kontekstu trwałości.

w większości przypadków wywołanie metody save lub persist nie ma znaczenia. Hibernate używa nazwy klasy encji i wartości klucza podstawowego do przechowywania encji w buforze pierwszego poziomu. W związku z tym potrzebuje wartości klucza podstawowego, gdy wykonuje metodę persist.

w prawie wszystkich sytuacjach Hibernate natychmiast generuje wartość klucza podstawowego i w razie potrzeby uruchamia polecenie SQL, gdy wywołujesz metodę persist lub save.

ale tak nie jest, jeśli używasz strategii tożsamości i próbujesz utrzymać podmiot bez aktywnej transakcji lub z FlushMode.Ręczne. Jeśli wywołasz metodę persist w jednej z tych sytuacji, Hibernate opóźni wykonanie instrukcji SQL INSERT i utworzy tymczasową wartość klucza podstawowego. Ale jeśli wywołasz metodę save, Hibernate natychmiast wykona instrukcję SQL INSERT i pobierze wartość klucza podstawowego z bazy danych.

następnie można go pobrać jako wartość zwracaną metody save.

Author a = new Author();a.setFirstName("Thorben");a.setLastName("Janssen");Long id = (Long) em.unwrap(Session.class).save(a);

lub możesz wywołać metodę getter głównego atrybutu klucza zarządzanej jednostki, jeśli używasz metody persist JPA.

Author a = new Author();a.setFirstName("Torben");a.setLastName("Janssen");em.persist(a);Long id = a.getId();

Hibernate wykonuje te same polecenia SQL podczas wywoływania metody persist lub save. Co i kiedy to się stanie, zależy od twojej podstawowej strategii generowania kluczy:

Nie generowane

jeśli ustawisz wartość klucza podstawowego programowo, np. na identyfikator naturalny, Hibernate wykona instrukcję SQL INSERT tylko wtedy, gdy usunie kontekst trwałości.

14:08:34,979 INFO TestPersistSaveMerge:237 - Save entity14:08:35,052 INFO TestPersistSaveMerge:240 - Commit transaction14:08:35,123 DEBUG SQL:92 - insert into Author (firstName, lastName, version, id) values (?, ?, ?, ?)
YouTube video

generowane za pomocą strategii tożsamości

jeśli używasz strategii tożsamości do generowania wartości klucza podstawowego, Hibernate musi wykonać instrukcję INSERT, gdy wywołujesz metodę save lub persist, aby pobrać wartość klucza podstawowego z bazy danych.

14:09:28,264 INFO TestPersistSaveMerge:237 - Save entity14:09:28,336 DEBUG SQL:92 - insert into Author (firstName, lastName, version) values (?, ?, ?)14:09:28,354 INFO TestPersistSaveMerge:240 - Commit transaction
YouTube video

generowane przy użyciu sekwencji

i jeśli używasz sekwencji, Hibernate wykonuje polecenie SQL SELECT, aby pobrać następną wartość z sekwencji bazy danych. Hibernate opóźnia następnie instrukcję INSERT, dopóki nie usunie kontekstu trwałości. W tym przykładzie kolor występuje, gdy transakcja zostanie zatwierdzona.

14:10:27,994 INFO TestPersistSaveMerge:237 - Save entity14:10:28,002 DEBUG SQL:92 - select nextval ('hibernate_sequence')14:10:28,042 INFO TestPersistSaveMerge:240 - Commit transaction14:10:28,096 DEBUG SQL:92 - insert into Author (firstName, lastName, version, id) values (?, ?, ?, ?)

generowane ze strategią tabeli

nie powinieneś używać strategii tabeli, ponieważ wymaga ona blokad na poziomie wiersza w tabeli klucza podstawowego i nie skaluje się dobrze. Jeśli jednak użyjesz tej strategii, Hibernate wykona polecenie SQL SELECT, aby pobrać następną wartość klucza podstawowego z bazy danych i zapisze nową wartość do tabeli bazy danych. Opóźnia wykonanie instrukcji SQL INSERT dla nowej jednostki, dopóki nie usunie kontekstu trwałości.

14:11:17,368 INFO TestPersistSaveMerge:237 - Save entity14:11:17,482 DEBUG SQL:92 - select tbl.next_val from hibernate_sequences tbl where tbl.sequence_name=? for update of tbl14:11:17,531 DEBUG SQL:92 - insert into hibernate_sequences (sequence_name, next_val) values (?,?)14:11:17,534 DEBUG SQL:92 - update hibernate_sequences set next_val=? where next_val=? and sequence_name=?14:11:17,584 INFO TestPersistSaveMerge:240 - Commit transaction14:11:17,655 DEBUG SQL:92 - insert into Author (firstName, lastName, version, id) values (?, ?, ?, ?)

który wybrać?

można się spodziewać, że metody save i persist zachowują się inaczej, ponieważ istnieje kilka różnic między specyfikacją JPA a Javadoc zastrzeżonych metod Hibernate.

ale prawie wszystkie te różnice znikają, gdy spojrzysz na wewnętrzną implementację. Pozostały tylko dwa przypadki, w których Hibernate może opóźnić odzyskanie klucza podstawowego, Typ zwracanej metody i wsparcie przez inne implementacje JPA.

w przypadku większości aplikacji nie ma znaczenia, czy otrzymasz wygenerowaną wartość klucza podstawowego jako typ zwracany przez metodę zapisu Hibernate lub z metody getter twojego atrybutu klucza podstawowego. Dopóki nie używasz rozszerzonego kontekstu trwałości i nie wykonujesz wszystkich operacji na bazie danych z aktywną transakcją, zalecam użycie metody persist JPA.

aktualizowanie odłączonego elementu

gdy zamkniesz bieżący kontekst trwałości lub wyraźnie usuniesz z niego element, wywołując metody clear lub detach w interfejsie EntityManager, element zostanie odłączony. Oznacza to, że nie jest już przechowywany w pamięci podręcznej pierwszego poziomu i że Hibernate nie powieli żadnej z zastosowanych zmian w bazie danych.

możesz użyć aktualizacji Hibernate lub metody merge JPA, aby powiązać odłączoną jednostkę z kontekstem trwałości. Po wykonaniu tej czynności Hibernate zaktualizuje bazę danych na podstawie wartości atrybutów encji.

efekt metody aktualizacji i scalania wydaje się być taki sam, ale jak zobaczysz w kolejnych sekcjach, istnieje ważna różnica.

metoda scalania JPA

metoda scalania JPA kopiuje stan odłączonego podmiotu do zarządzanej instancji tego samego podmiotu. Hibernate wykonuje zatem polecenie SQL SELECT w celu pobrania zarządzanej jednostki z bazy danych. Jeśli kontekst trwałości zawierał już zarządzaną instancję encji, Hibernate używa zamiast tego istniejącej. Następnie kopiuje wszystkie wartości atrybutów do zarządzanej jednostki i zwraca je do wywołującego.

Author managedAuthor = em.merge(a);

po aktywacji rejestrowania poleceń SQL, można zobaczyć wykonane instrukcje SELECT I UPDATE na wyjściu dziennika.

11:37:21,172 DEBUG SQL:92 - select books0_.bookId as bookId1_2_0_, books0_.authorId as authorId2_2_0_, book1_.id as id1_1_1_, book1_.fk_author as fk_autho6_1_1_, book1_.format as format2_1_1_, book1_.publishingDate as publishi3_1_1_, book1_.title as title4_1_1_, book1_.version as version5_1_1_, author2_.id as id1_0_2_, author2_.firstName as firstNam2_0_2_, author2_.lastName as lastName3_0_2_, author2_.version as version4_0_2_ from BookAuthor books0_ inner join Book book1_ on books0_.authorId=book1_.id left outer join Author author2_ on book1_.fk_author=author2_.id where books0_.bookId=?11:37:21,180 INFO TestPersistSaveMerge:82 - Before commit11:37:21,182 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?

kiedy Hibernate opróżnia kontekst trwałości po raz kolejny, jego mechanizm sprawdzania brudu sprawdza wszystkie zarządzane podmioty. Jeśli wykryje, że operacja scalania zmieniła wartość atrybutu encji, uruchomi wymaganą instrukcję SQL UPDATE.

jest jeden ważny szczegół, który musisz wiedzieć, kiedy używasz metody scalania JPA. Hibernate kopiuje wartości atrybutów jednostki odłączonej do jednostki zarządzanej. Nadpisuje to wszelkie zmiany wprowadzone w tej jednostce w bieżącej sesji.

metoda aktualizacji Hibernate

metoda aktualizacji Hibernate nie uruchamia instrukcji SQL SELECT. Po prostu dołącza encję do bieżącego kontekstu trwałości. W przeciwieństwie do metody merge JPA, nie można stracić żadnych zmian, wywołując metodę update. Jeśli kontekst trwałości zawiera już zarządzaną instancję encji, którą chcesz zaktualizować, wyświetli wyjątek.

em.unwrap(Session.class).update(a);

kiedy Hibernate wykonuje następny kolor, nie wykonuje żadnych brudnych kontroli. Nie jest to możliwe, ponieważ Hibernate nie odczytał najnowszej wersji encji z bazy danych. Po prostu wykonuje instrukcję SQL UPDATE dla ponownie dołączonej jednostki.

11:38:28,151 INFO TestPersistSaveMerge:121 - Before commit11:38:28,153 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?

brakujący dirty check powoduje niepotrzebne polecenie SQL UPDATE, gdy encja i odpowiadający jej rekord bazy danych zawierają te same wartości. Może to być problem, jeśli twój DBA zarejestrował WYZWALACZ aktualizacji dla tabeli bazy danych. W takich sytuacjach możesz przypisać swój obiekt za pomocą @SelectBeforeUpdate.

@Entity@SelectBeforeUpdatepublic class Author { ... }

, który mówi Hibernate, aby wybrał encję i wykonał brudne sprawdzenie przed wygenerowaniem instrukcji SQL UPDATE. Jak widać na wyjściu dziennika, zachowanie metody update jest teraz podobne do metody merge JPA.

19:08:16,530 INFO TestPersistSaveMerge:121 - Before commit19:08:16,531 DEBUG SQL:92 - select author_.id, author_.firstName as firstNam2_0_, author_.lastName as lastName3_0_, author_.version as version4_0_ from Author author_ where author_.id=?19:08:16,592 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?

ale jest znacząca różnica między tymi 2 metodami. Po wywołaniu metody update Hibernate wybierze tylko encję, którą podałeś jako parametr metody. Ale kiedy wywołujesz metodę merge JPA, Hibernate wybierze również wszystkie skojarzenia z CascadeType.Połącz. Powinieneś zatem preferować metodę scalania JPA, jeśli ponownie podłączysz ogromny Wykres jednostek.

który wybrać?

nie ma ogólnej odpowiedzi na te pytania. Jak widać, obie metody mają swoje zalety i wady. Musisz zdecydować dla konkretnego przypadku użycia, czy Hibernate musi wybrać encję, zanim uruchomi instrukcję SQL UPDATE. A jeśli tak jest, musisz również wziąć pod uwagę głębokość wykresu encji i implikacje wydajności dostarczonego zachowania pobierania.

aktualizacja podmiotu zarządzanego

JPA i Hibernate bardzo ułatwiają aktualizację podmiotu zarządzanego. Jeśli twój podmiot znajduje się w zarządzanym stanie cyklu życia, np. ponieważ pobrałeś go za pomocą zapytania JPQL lub metody find w Menedżerze EntityManager, wystarczy zmienić wartości atrybutów entity.

em = emf.createEntityManager();em.getTransaction().begin();a = em.find(Author.class, a.getId());a.setFirstName("Thorben");log.info("Before commit");em.getTransaction().commit();em.close();

gdy Hibernate zdecyduje się wypłukać kontekst trwałości, mechanizm sprawdzania brudu wykryje zmianę i wykona wymaganą instrukcję SQL UPDATE.

11:41:49,178 DEBUG SQL:92 - select author0_.id as id1_0_0_, author0_.firstName as firstNam2_0_0_, author0_.lastName as lastName3_0_0_, author0_.version as version4_0_0_ from Author author0_ where author0_.id=?11:41:49,191 INFO TestPersistSaveMerge:335 - Before commit11:41:49,193 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?

nie musisz i nie powinieneś wywoływać metody zapisu Hibernate po zaktualizowaniu encji. To powoduje dodatkowe Zdarzenie SaveOrUpdate bez dostarczania żadnych korzyści. Gdy Hibernate zdecyduje się opróżnić kontekst trwałości, wykona brudne sprawdzenie, aby wykryć wszystkie zmiany przed wykonaniem wymaganych instrukcji SQL UPDATE.

You might also like

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.