Hibernate: save, persist, update, merge, saveOrUpdate

wprowadzenie

w tym artykule omówimy różnice między kilkoma metodami interfejsu sesji: save, persist, update, merge, saveOrUpdate.

to nie jest wprowadzenie do Hibernate i powinieneś już znać podstawy konfiguracji, mapowania obiektowo-relacyjnego i pracy z instancjami encji. Aby zapoznać się z artykułem wprowadzającym do hibernacji, odwiedź nasz samouczek na temat Hibernate 4 with Spring.

Czytaj dalej:

Usuwanie obiektów z Hibernate

krótki przewodnik po usuwaniu encji w Hibernate.
Czytaj więcej →

procedury składowane z Hibernate

w tym artykule omówiono sposób wywoływania procedur przechowywania z Hibernate.
Czytaj więcej →

przegląd identyfikatorów w Hibernate / JPA

dowiedz się, jak mapować identyfikatory encji za pomocą Hibernate.
Czytaj więcej →

Session as a persistence Context Implementation

interfejs sesji ma kilka metod, które ostatecznie skutkują zapisaniem danych do bazy danych: persist, save, update, merge, saveOrUpdate. Aby zrozumieć różnicę między tymi metodami, musimy najpierw omówić cel sesji jako kontekst trwałości i różnicę między Stanami instancji encji w odniesieniu do sesji.

powinniśmy również zrozumieć historię rozwoju Hibernate, która doprowadziła do częściowo zduplikowanych metod API.

2.1. Instancje podmiotów zarządzających

oprócz samego mapowania obiektowo-relacyjnego, jednym z problemów, które miał rozwiązać Hibernate, jest problem zarządzania podmiotami w czasie wykonywania. Pojęcie „persistence context” jest rozwiązaniem Hibernate na ten problem. Kontekst trwałości można traktować jako kontener lub bufor pierwszego poziomu dla wszystkich obiektów załadowanych lub zapisanych w bazie danych podczas sesji.

sesja jest transakcją logiczną, której granice są określone przez logikę biznesową Twojej aplikacji. Podczas pracy z bazą danych w kontekście trwałości, a wszystkie instancje encji są do niego dołączone,zawsze należy mieć jedną instancję encji dla każdego rekordu bazy danych, z którym doszło do interakcji podczas sesji.

w Hibernate kontekst trwałości jest reprezentowany przez org.hibernacja.Instancja sesji. Dla JPA jest to javax.wytrwałość.EntityManager. Kiedy używamy Hibernate jako dostawcy JPA i operujemy poprzez interfejs EntityManager, implementacja tego interfejsu w zasadzie otacza bazowy obiekt sesji. Jednak Hibernate Session zapewnia bogatszy interfejs z większymi możliwościami, Więc czasami warto pracować bezpośrednio z sesją.

2.2. Stany wystąpień encji

każda instancja encji w aplikacji jest wyświetlana w jednym z trzech głównych stanów w odniesieniu do kontekstu trwałości sesji:

  • transient – ta instancja nie jest i nigdy nie była dołączona do sesji; ta instancja nie ma odpowiednich wierszy w bazie danych; zwykle jest to po prostu nowy obiekt, który został utworzony w celu zapisania do bazy danych;
  • persistent — ta instancja jest powiązana z unikalnym obiektem sesji; po spłukaniu sesji do bazy danych encja ta ma gwarancję posiadania odpowiedniego spójnego rekordu w bazie danych;
  • wolnostojący — ta instancja była kiedyś dołączona do sesji (w stanie stałym), ale teraz tak nie jest; instancja wchodzi w ten stan, jeśli usuniesz ją z kontekstu, wyczyścisz lub zamkniesz sesję lub umieścisz instancję w procesie serializacji/deserializacji.

oto uproszczony diagram stanu z komentarzami na temat metod sesji, które powodują przejście stanu.

2016-07-11_13-38-11

gdy instancja encji jest w stanie trwałym, wszystkie zmiany wprowadzone w zmapowanych polach tej instancji zostaną zastosowane do odpowiednich rekordów i pól bazy danych po wypłukaniu sesji. Instancja stała może być uważana za „online”, podczas gdy instancja odłączona została” offline ” i nie jest monitorowana pod kątem zmian.

oznacza to, że gdy zmieniasz pola trwałego obiektu, nie musisz wywoływać save, update ani żadnej z tych metod, aby wprowadzić te zmiany do bazy danych: wystarczy zatwierdzić transakcję lub spłukać lub zamknąć sesję, gdy skończysz z nią.

2.3. Zgodność ze specyfikacją JPA

Hibernate była najbardziej udaną implementacją Java ORM. Nic dziwnego, że na specyfikację Java persistence API (JPA) miał duży wpływ Hibernate API. Niestety, było też wiele różnic: niektóre poważne, niektóre bardziej subtelne.

aby działać jako implementacja standardu JPA, interfejsy API Hibernate musiały zostać zmienione. Kilka metod zostało dodanych do interfejsu sesji, aby pasowały do interfejsu EntityManager. Metody te służą temu samemu celowi, co metody” oryginalne”, ale są zgodne ze specyfikacją i w związku z tym mają pewne różnice.

różnice pomiędzy operacjami

ważne jest, aby od samego początku zrozumieć, że wszystkie metody (persist, save, update, merge, saveOrUpdate) nie powodują od razu odpowiednich instrukcji SQL UPDATE lub INSERT. Rzeczywisty zapis danych do bazy danych następuje po zatwierdzeniu transakcji lub spłukaniu sesji.

wspomniane metody zasadniczo zarządzają stanem instancji entity, przenosząc je między różnymi stanami w trakcie cyklu życia.

jako przykładowej encji użyjemy prostej osoby entity mapowanej adnotacją:

@Entitypublic class Person { @Id @GeneratedValue private Long id; private String name; // ... getters and setters}

3.1. Persist

metoda persist jest przeznaczona do dodania nowej instancji encji do kontekstu trwałości, tzn. do przejścia instancji ze stanu przejściowego do trwałego.

zwykle nazywamy go, gdy chcemy dodać rekord do bazy danych (persist instancja encji):

Person person = new Person();person.setName("John");session.persist(person);

co się dzieje po wywołaniu metody persist? Obiekt person przeszedł ze stanu przejściowego w stan trwały. Obiekt znajduje się obecnie w kontekście trwałości, ale nie został jeszcze zapisany do bazy danych. Generowanie poleceń INSERT nastąpi dopiero po zatwierdzeniu transakcji, spłukaniu lub zamknięciu sesji.

zauważ, że metoda persist ma typ void return. Działa na przekazanym obiekcie” na miejscu”, zmieniając jego stan. Zmienna person odwołuje się do rzeczywistego obiektu.

ta metoda jest późniejszym dodatkiem do interfejsu sesji. Główną cechą wyróżniającą tę metodę jest to, że jest ona zgodna ze specyfikacją JSR-220 (EJB persistence). Semantyka tej metody jest ściśle określona w specyfikacji, która zasadniczo stwierdza, że:

  • instancja przejściowa staje się trwała (a operacja kaskaduje wszystkie jej relacje z Cascade=PERSIST lub cascade=ALL),
  • jeśli instancja jest już trwała, to to wywołanie nie ma żadnego wpływu na tę konkretną instancję (ale nadal kaskaduje do swoich relacji z cascade=PERSIST lub cascade=ALL),
  • jeśli instancja jest odłączona, należy spodziewać się wyjątku, albo po wywołaniu tej metody, albo po zatwierdzeniu lub spłukiwanie sesji.

zauważ, że nie ma tu nic, co dotyczyłoby identyfikatora instancji. Specyfikacja nie stwierdza, że identyfikator zostanie wygenerowany od razu, niezależnie od strategii generowania identyfikatora. Specyfikacja metody persist pozwala implementacji na wydawanie poleceń generowania id na commit lub flush, a identyfikator nie jest gwarantowany jako inny niż null po wywołaniu tej metody, więc nie powinieneś na niej polegać.

możesz wywołać tę metodę na już trwałej instancji i nic się nie dzieje. Ale jeśli spróbujesz utrzymać odłączoną instancję, implementacja będzie musiała wyrzucić wyjątek. W poniższym przykładzie utrzymujemy byt, usuwamy go z kontekstu, aby stał się oderwany, a następnie próbujemy utrzymywać go ponownie. Drugie wezwanie do sesji.persist() powoduje wyjątek, więc poniższy kod nie będzie działał:

Person person = new Person();person.setName("John");session.persist(person);session.evict(person);session.persist(person); // PersistenceException!

3.2. Save

metoda save jest „oryginalną” metodą hibernacji, która nie jest zgodna ze specyfikacją JPA.

jego cel jest zasadniczo taki sam jak persist, ale ma inne szczegóły implementacji. Dokumentacja dla tej metody ściśle stwierdza, że utrzymuje ona instancję, „najpierw przypisując wygenerowany identyfikator”. Metoda gwarantuje, że zwróci Serializowalną wartość tego identyfikatora.

Person person = new Person();person.setName("John");Long id = (Long) session.save(person);

efekt zapisania już trwałej instancji jest taki sam jak w przypadku persist. Różnica pojawia się, gdy próbujesz zapisać odłączoną instancję:

Person person = new Person();person.setName("John");Long id1 = (Long) session.save(person);session.evict(person);Long id2 = (Long) session.save(person);

zmienna id2 będzie się różnić od id1. Wywołanie save na odłączonej instancji tworzy nową trwałą instancję i przypisuje jej nowy identyfikator, co skutkuje duplikatem rekordu w bazie danych po zatwierdzeniu lub przepłukaniu.

3.3. Merge

główną intencją metody merge jest zaktualizowanie trwałej instancji encji o nowe wartości pól z instancji oddzielonej encji.

Załóżmy na przykład, że masz RESTful interface z metodą pobierania seryjnego obiektu JSON przez jego id do wywołującego i metodą, która odbiera zaktualizowaną wersję tego obiektu od wywołującego. Encja, która przeszła przez taką serializację / deserializację, pojawi się w stanie odłączonym.

po deserializacji tej instancji encji, musisz uzyskać stałą instancję encji z kontekstu trwałości i zaktualizować jej pola o nowe wartości z tej odłączonej instancji. Więc metoda merge robi dokładnie to:

  • znajduje instancję encji za pomocą id pobranego z przekazanego obiektu (pobiera się istniejącą instancję encji z kontekstu trwałości lub nową instancję załadowaną z bazy danych);
  • kopiuje pola z przekazanego obiektu do tej instancji;
  • zwraca nowo zaktualizowaną instancję.

w poniższym przykładzie usuwamy (odłączamy) zapisaną encję z kontekstu, zmieniamy pole nazwa, a następnie scalamy odłączoną encję.

Person person = new Person(); person.setName("John"); session.save(person);session.evict(person);person.setName("Mary");Person mergedPerson = (Person) session.merge(person);

zauważ, że metoda merge zwraca obiekt — jest to obiekt mergedPerson, który został załadowany do kontekstu trwałości i zaktualizowany, a nie obiekt person, który został przekazany jako argument. Są to dwa różne obiekty, a obiekt person zwykle musi zostać odrzucony (w każdym razie nie licz na to, że zostanie przywiązany do kontekstu trwałości).

podobnie jak w przypadku metody persist, metoda scalania jest określona przez JSR-220, aby miała pewną semantykę, na której możesz polegać:

  • jeśli encja jest odłączona, jest kopiowana na istniejącą trwałą encję;
  • jeśli encja jest przejściowa, jest kopiowana na nowo utworzoną trwałą encję;
  • Ta operacja kaskaduje dla wszystkich relacji z mapowaniem cascade=MERGE lub cascade=ALL;
  • jeśli encja jest trwała, to wywołanie tej metody nie ma na nią wpływu (ale kaskadowanie nadal ma miejsce).

3.4. Update

podobnie jak w przypadku persist i save, metoda update jest „oryginalną” metodą hibernacji, która była obecna na długo przed dodaniem metody merge. Jej semantyka różni się w kilku kluczowych punktach:

  • działa na przekazany obiekt( jego zwracany typ to void); metoda update przenosi przekazany obiekt ze stanu odłączonego do trwałego;
  • ta metoda wyrzuca wyjątek, jeśli przekażesz mu encję przejściową.

w poniższym przykładzie zapisujemy obiekt, następnie usuwamy (odłączamy) go z kontekstu, następnie zmieniamy jego nazwę i wywołujemy update. Zauważ, że nie umieszczamy wyniku operacji aktualizacji w osobnej zmiennej, ponieważ aktualizacja odbywa się na samym obiekcie person. Zasadniczo ponownie dołączamy istniejącą instancję encji do kontekstu trwałości — coś, na co Specyfikacja JPA nam nie pozwala.

Person person = new Person();person.setName("John");session.save(person);session.evict(person);person.setName("Mary");session.update(person);

próba wywołania update na instancji przejściowej spowoduje wyjątek. Poniższe nie będą działać:

Person person = new Person();person.setName("John");session.update(person); // PersistenceException!

3.5. SaveOrUpdate

ta metoda pojawia się tylko w API hibernacji i nie ma swojego standaryzowanego odpowiednika. Podobnie jak w przypadku update, może być również używany do ponownego dołączania instancji.

w rzeczywistości wewnętrzna Klasa DefaultUpdateEventListener, która przetwarza metodę update, jest podklasą DefaultSaveOrUpdateListener, po prostu nadpisując niektóre funkcje. Główną różnicą metody saveOrUpdate jest to, że nie wyrzuca WYJĄTKÓW, gdy jest zastosowana do instancji transient; zamiast tego sprawia, że ta instancja transient jest trwała. Poniższy kod utrzyma nowo utworzoną instancję Person:

Person person = new Person();person.setName("John");session.saveOrUpdate(person);

możesz myśleć o tej metodzie jako o uniwersalnym narzędziu do utrzymywania obiektu niezależnie od jego stanu, niezależnie od tego, czy jest on przejściowy czy odłączony.

czego użyć?

jeśli nie masz żadnych SPECJALNYCH WYMAGAŃ, z reguły powinieneś trzymać się metod persist i merge, ponieważ są one znormalizowane i gwarantują zgodność ze specyfikacją JPA.

są również przenośne, jeśli zdecydujesz się przełączyć na innego dostawcę trwałości, ale czasami mogą wydawać się nie tak przydatne jak „oryginalne” metody hibernacji, save, update i saveOrUpdate.

podsumowanie

omówiliśmy cel różnych metod sesji hibernacji w odniesieniu do zarządzania trwałymi podmiotami w środowisku wykonawczym. Dowiedzieliśmy się, w jaki sposób te metody instancjonują encję tranzystora w ich cyklu życia i dlaczego niektóre z tych metod mają zduplikowaną funkcjonalność.



+