JPA EntityManager: Dlaczego warto korzystać z funkcji persist () zamiast scalania ()?

Odpowiedzi:

1615

Tak czy inaczej doda encję do PersistenceContext, różnica polega na tym, co zrobisz z encją później.

Persist pobiera instancję encji, dodaje ją do kontekstu i zarządza tą instancją (tzn. Przyszłe aktualizacje encji będą śledzone).

Opcja Scal zwraca instancję zarządzaną, z którą stan został scalony. Zwraca coś, co istnieje w PersistenceContext lub tworzy nową instancję twojej encji. W każdym razie skopiuje stan z podanego obiektu i zwróci kopię zarządzaną. Przekazywana instancja nie będzie zarządzana (wszelkie wprowadzone zmiany nie będą częścią transakcji - chyba że ponownie wywołasz scalanie). Możesz poprzez użycie zwróconej instancji (zarządzanej).

Może pomoże przykładowy kod.

MyEntity e = new MyEntity();

// scenario 1
// tran starts
em.persist(e); 
e.setSomeField(someValue); 
// tran ends, and the row for someField is updated in the database

// scenario 2
// tran starts
e = new MyEntity();
em.merge(e);
e.setSomeField(anotherValue); 
// tran ends but the row for someField is not updated in the database
// (you made the changes *after* merging)

// scenario 3
// tran starts
e = new MyEntity();
MyEntity e2 = em.merge(e);
e2.setSomeField(anotherValue); 
// tran ends and the row for someField is updated
// (the changes were made to e2, not e)

Scenariusz 1 i 3 są w przybliżeniu równoważne, ale istnieją sytuacje, w których warto użyć scenariusza 2.

Mikrofon
źródło
3
@dma_k: Wygląda na to, że używasz hibernacji. Jestem mniej zaznajomiony z Hibernacją niż JPA - ale w JPA, jeśli wywołasz EntityManager.persist () i przekażesz odłączony byt, będziesz: a) natychmiast uzyskiwał wyjątek EntityExistsException lub b) uzyskał inny wyjątek PersistenceException w czasie opróżniania / zatwierdzania. Może źle zrozumiałem pytanie tutaj?
Mike
49
Ta odpowiedź mogłaby zostać ulepszona, gdyby obejmowała również przypadki, w których scalany / utrwalany byt już istnieje w trwałym kontekście (lub przynajmniej wyjaśnił, że opisuje zachowanie tylko wtedy, gdy trwały / scalany byt jeszcze nie istnieje)
Henry
2
Czy jedna metoda jest bardziej wydajna? Być może mergepełna kopia obiektu przed zarządzaniem ma wpływ na wydajność?
Kevin Meredith,
2
Co z identyfikatorami? Jeśli mam, czy @GeneratedIdmogę to zdobyć w scenariuszu 2?
rascio
7
Mike: „Scalanie tworzy nową instancję ...”: nie zawsze jest to prawdą. Jeśli EntityManager znajdzie już zarządzaną jednostkę w swoim kontekście, zwraca tę instancję (po zaktualizowaniu pól). Edytuj swoją odpowiedź, a następnie zagłosuję na nią.
Heri
181

Utrwalanie i łączenie służą dwóm różnym celom (wcale nie są alternatywami).

(edytowane w celu rozszerzenia informacji o różnicach)

trwać:

  • Wstaw nowy rejestr do bazy danych
  • Dołącz obiekt do menedżera encji.

łączyć:

  • Znajdź dołączony obiekt o tym samym identyfikatorze i zaktualizuj go.
  • Jeśli istnieje, zaktualizuj i zwróć już dołączony obiekt.
  • Jeśli nie istnieje, wstaw nowy rejestr do bazy danych.

wydajność persist ():

  • Może być bardziej wydajne przy wstawianiu nowego rejestru do bazy danych niż merge ().
  • Nie powiela oryginalnego obiektu.

semantyka persist ():

  • Zapewnia to, że wstawiasz i nie aktualizujesz przez pomyłkę.

Przykład:

{
    AnyEntity newEntity;
    AnyEntity nonAttachedEntity;
    AnyEntity attachedEntity;

    // Create a new entity and persist it        
    newEntity = new AnyEntity();
    em.persist(newEntity);

    // Save 1 to the database at next flush
    newEntity.setValue(1);

    // Create a new entity with the same Id than the persisted one.
    AnyEntity nonAttachedEntity = new AnyEntity();
    nonAttachedEntity.setId(newEntity.getId());

    // Save 2 to the database at next flush instead of 1!!!
    nonAttachedEntity.setValue(2);
    attachedEntity = em.merge(nonAttachedEntity);

    // This condition returns true
    // merge has found the already attached object (newEntity) and returns it.
    if(attachedEntity==newEntity) {
            System.out.print("They are the same object!");
    }

    // Set 3 to value
    attachedEntity.setValue(3);
    // Really, now both are the same object. Prints 3
    System.out.println(newEntity.getValue());

    // Modify the un attached object has no effect to the entity manager
    // nor to the other objects
    nonAttachedEntity.setValue(42);
}

W ten sposób istnieje tylko 1 dołączony obiekt dla dowolnego rejestru w menedżerze encji.

merge () dla encji o identyfikatorze przypomina coś:

AnyEntity myMerge(AnyEntity entityToSave) {
    AnyEntity attached = em.find(AnyEntity.class, entityToSave.getId());
    if(attached==null) {
            attached = new AnyEntity();
            em.persist(attached);
    }
    BeanUtils.copyProperties(attached, entityToSave);

    return attached;
}

Chociaż połączenie z MySQL Merge () może być tak samo wydajne jak persist () przy użyciu wywołania INSERT z opcją ON DUPLICATE KEY UPDATE, JPA to programowanie na bardzo wysokim poziomie i nie można zakładać, że tak będzie wszędzie.

Josep Panadero
źródło
Można wymienić przypadek, gdy nie jest to ważne, aby wymienić em.persist(x)z x = em.merge(x)?
Aaron Digulla,
20
persist () może zgłosić wyjątek EntityExistsException. Jeśli chcesz mieć pewność, że Twój kod wykonuje wstawianie, a nie aktualizację danych, musisz używać.
Josep Panadero,
1
merge()może również rzucićEntityExistsException
Sean
1
@Brak Może, ponieważ jest RuntimeException, ale nie jest wspomniany w Javadoc.
Martin
154

Jeśli używasz przypisanego generatora, użycie funkcji scalania zamiast opcji utrwalania może spowodować nadmiarową instrukcję SQL , a tym samym wpłynąć na wydajność.

Ponadto, nazywając seryjnej dla podmiotów zarządzanych jest również błędem, ponieważ podmioty zarządzane są automatycznie zarządzane przez Hibernate i ich stan jest zsynchronizowany z rekordu bazy danych przez mechanizm kontroli brudny po spłukiwania Context Persistence .

Aby zrozumieć, jak to wszystko działa, najpierw powinieneś wiedzieć, że Hibernacja zmienia sposób myślenia programisty z instrukcji SQL na przejścia stanów encji .

Gdy jednostka jest aktywnie zarządzana przez Hibernację, wszystkie zmiany będą automatycznie propagowane do bazy danych.

Hibernacja monitoruje aktualnie podłączone podmioty. Ale aby jednostka mogła być zarządzana, musi być we właściwym stanie encji.

Aby lepiej zrozumieć przejścia stanu JPA, możesz zwizualizować następujący diagram:

Przejścia stanu encji JPA

Lub jeśli używasz interfejsu API Hibernacji:

Hibernacja przejść stanu encji

Jak ilustrują powyższe diagramy, jednostka może znajdować się w jednym z czterech następujących stanów:

  • Nowy (przejściowy)

    Nowo utworzony obiekt, który nigdy nie był powiązany z Hibernacją Session(alias Persistence Context) i nie jest odwzorowany na żaden wiersz tabeli bazy danych, jest uważany za będący w stanie Nowy (przejściowy).

    Aby się utrwalić, musimy albo jawnie wywołać tę EntityManager#persistmetodę, albo skorzystać z mechanizmu trwałości przechodniego.

  • Trwały (zarządzany)

    Trwała jednostka została powiązana z wierszem tabeli bazy danych i jest zarządzana przez aktualnie działający kontekst Persistence. Wszelkie zmiany dokonane w takiej jednostce zostaną wykryte i propagowane do bazy danych (podczas czasu opróżniania sesji). Dzięki Hibernacji nie musimy już wykonywać instrukcji INSERT / UPDATE / DELETE. Hibernacja stosuje transakcyjny styl pracy z zapisem, a zmiany są synchronizowane w ostatniej odpowiedzialnej chwili, podczas bieżącego Sessionczasu spłukiwania.

  • Oderwany

    Po zamknięciu aktualnie działającego kontekstu trwałości wszystkie wcześniej zarządzane jednostki zostają odłączone. Kolejne zmiany nie będą już śledzone i nie nastąpi automatyczna synchronizacja bazy danych.

    Aby powiązać odłączony byt z aktywną sesją hibernacji, możesz wybrać jedną z następujących opcji:

    • Ponowne podłączenie

      Hibernacja (ale nie JPA 2.1) obsługuje ponowne podłączanie za pomocą metody aktualizacji Session #. Sesja hibernacji może skojarzyć tylko jeden obiekt encji dla danego wiersza bazy danych. Wynika to z faktu, że kontekst trwałości działa jako pamięć podręczna w pamięci (pamięć podręczna pierwszego poziomu) i tylko jedna wartość (jednostka) jest powiązana z danym kluczem (typ jednostki i identyfikator bazy danych). Podmiot może zostać ponownie przyłączony tylko wtedy, gdy żaden inny obiekt JVM (pasujący do tego samego wiersza bazy danych) nie jest już powiązany z bieżącą sesją hibernacji.

    • Scalanie

    Scalenie spowoduje skopiowanie stanu odłączonej jednostki (źródła) do instancji jednostki zarządzanej (miejsca docelowego). Jeśli łącząca się jednostka nie ma odpowiednika w bieżącej sesji, zostanie ona pobrana z bazy danych. Instancja odłączonego obiektu pozostanie odłączona nawet po operacji scalania.

  • Oddalony

    Chociaż JPA wymaga, aby można było usuwać tylko zarządzane jednostki, Hibernacja może również usuwać odłączone jednostki (ale tylko poprzez wywołanie metody Session # delete). Usunięty obiekt jest zaplanowany tylko do usunięcia, a rzeczywista instrukcja DELETE bazy danych zostanie wykonana podczas czasu opróżniania sesji.

Vlad Mihalcea
źródło
możesz spojrzeć stackoverflow.com/questions/46214322/... ?
gstackoverflow
@gstackoverflow Odpowiedź jest prawidłowa. Więcej informacji można znaleźć w tym artykule lub w mojej książce „ Wysoka wydajność Java Trwałość” .
Vlad Mihalcea,
Zatem nie ma możliwości zmiany kolejności operacji dla osierocenia = prawda?
gstackoverflow
Twój artykuł o kolejności operacji w zwykłym przypadku. Moje pytanie specyficzne dla sierotyRemoval
gstackoverflow
1
Faktem jest, że nie można wyjaśnić hibernacji za pomocą takiego schematu. Dlaczego nie możesz opróżnić sesji po odłączeniu? Co się stanie, gdy spróbujesz zapisać już utrwalony byt? Dlaczego to zachowanie koloru jest inne, jeśli chodzi o zapisywanie i utrzymywanie? Istnieje 1000 takich pytań, na które nikt nie ma jasnej logiki.
GingerBeer
37

Zauważyłem, że kiedy korzystałem em.merge, otrzymałem SELECTinstrukcję dla każdego INSERT, nawet gdy nie było dla mnie pola generowanego przez JPA - pole klucza podstawowego to UUID, który sam ustawiłem. Przełączyłem się wtedy em.persist(myEntityObject)i dostałem tylko INSERToświadczenia.

Sarah Vessels
źródło
3
Ma to sens, ponieważ przypisujesz identyfikatory, a kontener JPA nie ma pojęcia, skąd je masz. Istnieje (mała) szansa, że ​​obiekt już istnieje w bazie danych, na przykład w scenariuszu, w którym kilka aplikacji pisze do tej samej bazy danych.
Aaron Digulla
Mam podobny problem z merge(). Miałem bazę danych PostgreSQL ze skomplikowanym widokiem : widok agregował dane z kilku tabel (tabele miały identyczną strukturę, ale różne nazwy). Więc JPA próbowało to zrobić merge(), ale tak naprawdę najpierw JPA SELECT(baza danych z powodu ustawień widoku mogła zwrócić kilka rekordów z tym samym kluczem podstawowym z różnych tabel!), A następnie JPA (Hibernacja była implementacją) nie powiodło się: istnieje kilka rekordów z tym samym kluczem ( org.hibernate.HibernateException: More than one row with the given identifier was found). W moim przypadku persist()pomógł mi.
flaz14
29

Specyfikacja JPA mówi o tym, co następuje persist().

Jeśli X jest obiektem odłączonym, EntityExistsExceptionmoże zostać wyrzucony, gdy wywoływana jest operacja utrwalania, EntityExistsExceptionlub inny PersistenceExceptionmoże zostać wyrzucony w czasie opróżniania lub zatwierdzania.

Dlatego użycie persist()byłoby odpowiednie, gdy obiekt nie powinien być obiektem odłączonym. Być może wolisz, aby kod wyrzucił, PersistenceExceptionwięc szybko się nie powiedzie.

Chociaż specyfikacja jest niejasna , persist()może ustawić @GeneratedValue @Iddla obiektu. merge()musi jednak mieć obiekt z @Idjuż wygenerowanym.

Raedwald
źródło
5
+1 dla „ merge()jednak musi mieć obiekt z @Id już wygenerowanym . ”. Ilekroć EntityManager nie znajdzie wartości dla pola identyfikatora obiektu, jest on utrwalany (wstawiany) do bazy danych.
Omar
Nie zrozumiałem tego po raz pierwszy, ponieważ nie miałem jasności co do stanów. Mam nadzieję, że to komuś pomaga, tak jak dla mnie. docs.jboss.org/hibernate/core/3.6/reference/en-US/html/…
RBz
1
@GeneratedValue nie ma różnych implikacji dla merge () i persist ()
SandeepGodara
17

Trochę więcej szczegółów na temat scalania, które pomogą ci użyć scalania ponad:

Zwracanie zarządzanej instancji innej niż pierwotna jednostka jest krytyczną częścią procesu scalania. Jeśli instancja encji o tym samym identyfikatorze już istnieje w kontekście trwałości, dostawca zastąpi swój stan stanem encji, która jest scalana, ale istniejąca już wersja zarządzana musi zostać zwrócona klientowi, aby można ją było używany. Jeśli dostawca nie zaktualizował instancji pracownika w kontekście trwałości, wszelkie odwołania do tej instancji staną się niespójne w przypadku połączenia nowego stanu.

Gdy merge () jest wywoływana na nowej encji, zachowuje się podobnie jak operacja persist (). Dodaje jednostkę do kontekstu trwałości, ale zamiast dodawać oryginalną instancję encji, tworzy nową kopię i zamiast tego zarządza tą instancją. Kopia utworzona przez operację merge () jest utrwalana tak, jakby wywołano na niej metodę persist ().

W przypadku relacji operacja scalania () spróbuje zaktualizować zarządzaną jednostkę, aby wskazywała na zarządzane wersje jednostek, do których odwołuje się odłączona jednostka. Jeśli jednostka ma związek z obiektem, który nie ma trwałej tożsamości, wynik operacji scalania jest niezdefiniowany. Niektórzy dostawcy mogą zezwolić, aby kopia zarządzana wskazywała na nietrwały obiekt, podczas gdy inni mogą natychmiast zgłosić wyjątek. W takich przypadkach operacja scalania () może być opcjonalnie kaskadowana, aby zapobiec wystąpieniu wyjątku. W dalszej części tej sekcji omówimy kaskadowanie operacji scalania (). Jeśli scalany obiekt wskazuje na usunięty obiekt, zostanie zgłoszony wyjątek IllegalArgumentException.

Relacje leniwe ładowanie są szczególnym przypadkiem w operacji scalania. Jeśli relacja leniwego ładowania nie została uruchomiona na encji przed jej odłączeniem, relacja ta zostanie zignorowana po scaleniu encji. Jeśli relacja została uruchomiona podczas zarządzania, a następnie ustawiona na null, gdy jednostka została odłączona, zarządzana wersja jednostki również będzie wyczyścić relację podczas scalania. ”

Wszystkie powyższe informacje pochodzą z „Pro JPA 2 Mastering the Java ™ Persistence API” autorstwa Mike'a Keitha i Merricka Schnicariola. Rozdział 6. Odłączanie i łączenie sekcji. Ta książka jest właściwie drugą książką poświęconą WZP autorom. Ta nowa książka zawiera wiele nowych informacji niż poprzednia. Naprawdę poleciłem przeczytać tę książkę dla tych, którzy będą poważnie zaangażowani w JPA. Przykro mi, że anonimowo opublikowałem pierwszą odpowiedź.

Khurshed Salimov
źródło
17

Jest jeszcze kilka różnic między mergei persist(wymienię ponownie te już tutaj opublikowane):

D1 mergenie sprawia, że ​​przekazywana jednostka jest zarządzana, ale zwraca inną zarządzaną instancję. persistz drugiej strony sprawi, że przekazywany podmiot będzie zarządzany:

//MERGE: passedEntity remains unmanaged, but newEntity will be managed
Entity newEntity = em.merge(passedEntity);

//PERSIST: passedEntity will be managed after this
em.persist(passedEntity);

D2 Jeśli usuniesz encję, a następnie zdecydujesz się zachować encję z powrotem, możesz to zrobić tylko za pomocą persist (), ponieważ mergewyrzuci an IllegalArgumentException.

D3 Jeśli zdecydujesz się zająć ręcznie swoimi identyfikatorami (np. Za pomocą identyfikatorów UUID), wówczas merge operacja wywoła kolejne SELECTzapytania w celu wyszukania istniejących encji o tym identyfikatorze, chociaż persistmoże nie potrzebować tych zapytań.

D4 Są przypadki, gdy po prostu nie ufasz kodowi, który wywołuje Twój kod, i aby upewnić się, że żadne dane nie są aktualizowane, ale raczej wstawione, musisz użyć persist.

Andrei I
źródło
8

Otrzymywałem wyjątki leniwe ładowanie mojej encji, ponieważ próbowałem uzyskać dostęp do leniwie ładowanej kolekcji, która była w sesji.

To, co bym zrobił, to oddzielne żądanie, pobierz encję z sesji, a następnie spróbuj uzyskać dostęp do kolekcji na mojej stronie jsp, która była problematyczna.

Aby temu zaradzić, zaktualizowałem ten sam byt w moim kontrolerze i przekazałem go do mojego pliku jsp, chociaż wyobrażam sobie, że kiedy ponownie zapisałem w sesji, będzie on również dostępny SessionScopei nie wrzuci LazyLoadingException, modyfikacja przykładu 2:

Dla mnie zadziałało:

// scenario 2 MY WAY
// tran starts
e = new MyEntity();
e = em.merge(e); // re-assign to the same entity "e"

//access e from jsp and it will work dandy!!
logixplayer
źródło
7

Znalazłem to wyjaśnienie z oświecenia dokumentów Hibernacja, ponieważ zawierają one przypadek użycia:

Użycie i semantyka funkcji merge () wydaje się być myląca dla nowych użytkowników. Po pierwsze, dopóki nie próbujesz użyć stanu obiektu załadowanego w jednym menedżerze encji w innym nowym menedżerze encji, nie powinieneś w ogóle używać merge () . Niektóre całe aplikacje nigdy nie użyją tej metody.

Zwykle funkcja merge () jest używana w następującym scenariuszu:

  • Aplikacja ładuje obiekt do pierwszego menedżera encji
  • obiekt jest przekazywany do warstwy prezentacji
  • wprowadzono pewne modyfikacje do obiektu
  • obiekt jest przekazywany z powrotem do warstwy logiki biznesowej
  • aplikacja utrzymuje te modyfikacje, wywołując funkcję merge () w menedżerze drugiej jednostki

Oto dokładna semantyka scalania ():

  • jeśli istnieje instancja zarządzana z tym samym identyfikatorem aktualnie powiązanym z kontekstem trwałości, skopiuj stan danego obiektu do instancji zarządzanej
  • jeśli nie ma obecnie instancji zarządzanej powiązanej z kontekstem trwałości, spróbuj załadować ją z bazy danych lub utwórz nową instancję zarządzaną
  • instancja zarządzana jest zwracana
  • dana instancja nie zostaje skojarzona z kontekstem trwałości, pozostaje odłączona i zwykle jest odrzucana

Od: http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/objectstate.html

Ray Hulha
źródło
6

Przeglądając odpowiedzi brakuje pewnych szczegółów dotyczących `Kaskady 'i generowania identyfikatorów. Zobacz pytanie

Warto również wspomnieć, że możesz mieć osobne Cascadeadnotacje do łączenia i utrwalania: Cascade.MERGEi Cascade.PERSISTktóre będą traktowane zgodnie z zastosowaną metodą.

Specyfikacja jest twoim przyjacielem;)

Ioannis Deligiannis
źródło
6

JPA jest bez wątpienia doskonałym uproszczeniem w dziedzinie aplikacji korporacyjnych zbudowanych na platformie Java. Jako programista, który musiał poradzić sobie z zawiłościami starej fasoli encji w J2EE, postrzegam włączenie JPA do specyfikacji Java EE jako duży krok naprzód. Jednak zagłębiając się w szczegóły JPA, znajduję rzeczy, które nie są takie łatwe. W tym artykule zajmuję się porównaniem metod łączenia i utrwalania EntityManager, których zachodzenie na siebie może powodować zamieszanie nie tylko dla początkujących. Ponadto proponuję uogólnienie, w którym obie metody są traktowane jako szczególne przypadki bardziej ogólnej metody.

Utrzymujące się podmioty

W przeciwieństwie do metody scalania metoda persist jest dość prosta i intuicyjna. Najczęstszy scenariusz użycia metody persist można podsumować w następujący sposób:

„Nowo utworzona instancja klasy encji jest przekazywana do metody persist. Po powrocie tej metody encja jest zarządzana i planowana do wstawienia do bazy danych. Może się to zdarzyć w momencie lub przed zatwierdzeniem transakcji lub po wywołaniu metody flush. Jeśli jednostka odwołuje się do innej jednostki poprzez relację oznaczoną strategią kaskadową PERSIST, procedura ta ma również zastosowanie do niej. ”

wprowadź opis zdjęcia tutaj

Specyfikacja jest bardziej szczegółowa, jednak ich zapamiętanie nie jest kluczowe, ponieważ dotyczą one mniej lub bardziej egzotycznych sytuacji.

Scalanie podmiotów

W porównaniu do trwałego opis zachowania scalania nie jest tak prosty. Nie ma głównego scenariusza, jak ma to miejsce w przypadku persist, a programista musi zapamiętać wszystkie scenariusze, aby napisać poprawny kod. Wydaje mi się, że projektanci WZP chcieli mieć metodę, której podstawową troską byłaby obsługa odłączonych encji (w przeciwieństwie do metody persist, która dotyczy przede wszystkim nowo tworzonych encji). Głównym zadaniem metody scalania jest przeniesienie stanu z niezarządzana jednostka (przekazana jako argument) do swojego zarządzanego odpowiednika w kontekście trwałości. To zadanie dzieli się jednak na kilka scenariuszy, które pogarszają zrozumiałość zachowania całej metody.

Zamiast powtarzania akapitów ze specyfikacji JPA przygotowałem schemat blokowy, który schematycznie przedstawia zachowanie metody scalania:

wprowadź opis zdjęcia tutaj

Kiedy więc powinienem użyć opcji keep, a kiedy scalenia?

trwać

  • Chcesz, aby metoda zawsze tworzyła nowy byt i nigdy go nie aktualizowała. W przeciwnym razie metoda zgłasza wyjątek w wyniku naruszenia unikatowości klucza podstawowego.
  • Procesy wsadowe, obsługa jednostek w sposób stanowy (patrz Wzorzec bramy).
  • Optymalizacja wydajności

łączyć

  • Chcesz, aby metoda wstawiała lub aktualizowała jednostkę w bazie danych.
  • Chcesz obsługiwać jednostki w sposób bezstanowy (obiekty przesyłania danych w usługach)
  • Chcesz wstawić nowy byt, który może mieć odniesienie do innego bytu, który może, ale może jeszcze nie zostać utworzony (relacja musi być oznaczona POŁĄCZ). Na przykład wstawienie nowego zdjęcia z odniesieniem do nowego lub istniejącego albumu.
Amit gudżarathi
źródło
Jaka jest różnica między zarządzaniem E a komputerem PC zawiera zarządzaną wersję E?
GingerBeer
5

Scenariusz X:

Table: Spitter (One), Table: Spittles (Many) (Spittles jest właścicielem relacji z FK: spitter_id)

Ten scenariusz skutkuje zapisaniem: Spittera i obu Spittlesów, jakby były własnością Same Spittera.

        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.addSpittle(spittle3); // <--persist     
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!

Scenariusz Y:

To uratuje Spittera, uratuje 2 Spittles Ale nie będą odnosić się do tego samego Spittera!

        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.save(spittle3); // <--merge!!       
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!
George Papatheodorou
źródło
1
Spitter to obiekt zaczerpnięty z książki „Spring in Action” trzeciej edycji Graiga Wallsa. Spitters to osoby, które coś mówią, a ich Spittle jest tym, co faktycznie mówią. Więc Spitter ma wiele spittles, co oznacza, że ​​ma listę Strun.
George Papatheodorou,
1
Mógłbyś skorzystać z przykładu, który jest nieco bardziej czytelny bez czytania Wiosny w akcji ...
wonderb0lt
1
W rzeczywistości nie musisz wiedzieć, co to jest ślina lub spitter, ponieważ na górze jest napisane, że Spitter to stół, spitter to kolejny stół, który jest właścicielem ... tego i tamtego ...
George Papatheodorou
3

Kolejna obserwacja:

merge()będzie dbał o automatycznie wygenerowany identyfikator (testowany na IDENTITYi SEQUENCE), gdy rekord o takim identyfikatorze już istnieje w twojej tabeli. W takim przypadku merge()spróbuje zaktualizować rekord. Jeśli jednak identyfikator jest nieobecny lub nie pasuje do żadnych istniejących rekordów, merge()całkowicie go zignoruje i poprosi db o przydzielenie nowego. Czasami jest to źródłem wielu błędów. Nie używaj merge()do wymuszania identyfikatora dla nowego rekordu.

persist()z drugiej strony nigdy nie pozwoli ci nawet przekazać mu identyfikatora. Natychmiast zawiedzie. W moim przypadku jest to:

Przyczyna: org.hibernate.PersistentObjectException: odłączony byt przekazany do trwałego działania

hibernate-jpa javadoc ma podpowiedź:

Zgłasza : javax.persistence.EntityExistsException - jeśli jednostka już istnieje. (Jeśli jednostka już istnieje, może zostać wygenerowany wyjątek EntityExistsException podczas wywoływania operacji trwałej lub wyjątek EntityExistsException lub inny wyjątek PersistenceException może zostać zgłoszony w momencie opróżnienia lub zatwierdzenia.)

yuranos
źródło
2
Jeśli nie używasz automatycznie generowanych identyfikatorów, musisz ręcznie nadać nowemu podmiotowi identyfikator. persist()nie narzeka, że ​​ma identyfikator, narzeka tylko wtedy, gdy coś o tym samym identyfikatorze jest już w bazie danych.
godz.
1

Być może przyszedłeś tutaj po porady, kiedy używać trwałości, a kiedy używać scalania . Myślę, że to zależy od sytuacji: jak prawdopodobne jest, że musisz utworzyć nowy rekord i jak trudno jest odzyskać utrwalone dane.

Załóżmy, że możesz użyć naturalnego klucza / identyfikatora.

  • Dane muszą zostać utrwalone, ale raz na jakiś czas istnieje rekord i wymagana jest aktualizacja. W takim przypadku możesz spróbować utrwalić, a jeśli zgłosi wyjątek EntityExistsException, poszukaj go i połącz dane:

    spróbuj {entityManager.persist (byt)}

    catch (wyjątek EntityExistsException) {/ * pobierz i scal * /}

  • Utrwalone dane muszą zostać zaktualizowane, ale od czasu do czasu nie ma jeszcze danych. W takim przypadku możesz to sprawdzić i wykonać utrwalenie, jeśli brakuje elementu:

    encja = entityManager.find (klucz);

    if (encja == null) {entityManager.persist (encja); }

    else {/ * merge * /}

Jeśli nie masz naturalnego klucza / identyfikatora, trudniej będzie ci ustalić, czy istota istnieje, czy nie, i jak to sprawdzić.

Połączenia można również wykonać na dwa sposoby:

  1. Jeśli zmiany są zwykle niewielkie, zastosuj je do zarządzanego obiektu.
  2. Jeśli zmiany są częste, skopiuj identyfikator z utrwalonej jednostki, a także niezmienione dane. Następnie wywołaj EntityManager :: merge (), aby zastąpić starą treść.
Peter Willems
źródło
0

persist (encja) powinna być używana z całkowicie nowymi encjami, aby dodać je do DB (jeśli encja już istnieje w DB, będzie rzut EntityExistsException).

należy użyć scalania (bytu), aby przywrócić byt do kontekstu trwałości, jeśli byt został odłączony i został zmieniony.

Prawdopodobnie trwa to generowanie instrukcji SQL INSERT i scalanie instrukcji SQL UPDATE (ale nie jestem pewien).

Krystian
źródło
To jest niepoprawne. Jeśli wywołasz scalanie (e) na nowym e, należy go utrwalić.
Pedro Lamarão,
Ze specyfikacji JPA wersja 2.1, sekcja 3.2.7.1, drugi punkt: „Jeśli X jest nową instancją jednostki, tworzona jest nowa instancja jednostki zarządzanej X ', a stan X jest kopiowany do nowej instancji jednostki zarządzanej X”. ”
Pedro Lamarão,