Mam sytuację, w której muszę ponownie dołączyć odłączone obiekty do sesji hibernacji, chociaż obiekt o tej samej tożsamości MOŻE już istnieć w sesji, co spowoduje błędy.
W tej chwili mogę zrobić jedną z dwóch rzeczy.
getHibernateTemplate().update( obj )
Działa to wtedy i tylko wtedy, gdy obiekt nie istnieje jeszcze w sesji hibernacji. Zgłaszane są wyjątki stwierdzające, że obiekt o podanym identyfikatorze już istnieje w sesji, gdy będę go później potrzebować.getHibernateTemplate().merge( obj )
Działa to wtedy i tylko wtedy, gdy obiekt istnieje w sesji hibernacji. Zgłaszane są wyjątki, gdy potrzebuję, aby obiekt był w sesji później, jeśli go użyję.
Biorąc pod uwagę te dwa scenariusze, jak mogę ogólnie dołączyć sesje do obiektów? Nie chcę używać wyjątków do kontrolowania przepływu rozwiązania tego problemu, ponieważ musi być bardziej eleganckie rozwiązanie ...
refresh()
na odłączone podmioty? Przeglądając specyfikację 2.0, nie widzę żadnego uzasadnienia; tylko to nie jest dozwolone.*Reattaching a modified detached instance* A detached instance may be reattached to a new Session (and managed by this new persistence context) by calling update() on the detached object. In our experience, it may be easier for you to understand the following code if you rename the update() method in your mind to reattach()—however, there is a good reason it’s called updating.
Więcej można znaleźć w sekcji 9.3.2lock(LockMode.NONE)
w rzeczywistości można go wywołać na obiekcie przejściowym i ponownie podłącza byt do sesji. Zobacz stackoverflow.com/a/3683370/14379Wszystkie te odpowiedzi pomijają ważne rozróżnienie. update () służy do (ponownego) dołączenia wykresu obiektowego do sesji. Przekazywane obiekty to te, które są zarządzane.
merge () w rzeczywistości nie jest interfejsem API (re) załącznika. Zauważ, że merge () ma wartość zwracaną? To dlatego, że zwraca ci zarządzany wykres, który może nie być wykresem, który przeszedłeś. merge () jest interfejsem API JPA, a jego zachowanie jest regulowane przez specyfikację JPA. Jeśli obiekt przekazywany do scalenia () jest już zarządzany (już powiązany z sesją), to jest to wykres, z którym Hibernacja działa; przekazany obiekt to ten sam obiekt zwrócony przez merge (). Jeśli jednak obiekt, który przekazujesz do merge (), zostanie odłączony, Hibernacja utworzy nowy zarządzany wykres obiektów i skopiuje stan z odłączonego wykresu na nowy zarządzany wykres. Ponownie, wszystko to jest podyktowane i regulowane przez specyfikację JPA.
Jeśli chodzi o ogólną strategię „upewnij się, że ten podmiot jest zarządzany lub spraw, aby był zarządzany”, to w pewnym stopniu zależy od tego, czy chcesz uwzględnić również dane jeszcze nie wstawione. Zakładając, że tak, użyj czegoś takiego
Zauważ, że użyłem saveOrUpdate () zamiast update (). Jeśli nie chcesz obsługiwać danych jeszcze nie wstawionych, użyj zamiast tego aktualizacji () ...
źródło
Session.contains(Object)
sprawdza przez odniesienie. Jeśli istnieje już inny podmiot reprezentujący ten sam wiersz w sesji i zdasz odłączoną instancję, otrzymasz wyjątek.Session.contains(Object)
kontroli przez odniesienie, jeśli w sesji jest inny podmiot reprezentujący ten sam wiersz, zwróci wartość false i zaktualizuje go.Odpowiedź niedyplomatyczna: Prawdopodobnie szukasz rozszerzonego kontekstu trwałości. Jest to jeden z głównych powodów, dla których Seam Framework ... Jeśli masz trudności z użyciem Hibernacji w szczególności wiosną, zapoznaj się z tym dokumentem Seama.
Odpowiedź dyplomatyczna: Opisano to w dokumentach Hibernacji . Jeśli potrzebujesz więcej wyjaśnień, zapoznaj się z sekcją 9.3.2 Trwałość Java z Hibernacją, zatytułowaną „Praca z odłączonymi obiektami”. Chciałbym zdecydowanie polecam Ci tę książkę, jeśli robisz coś więcej niż CRUD z Hibernate.
źródło
Jeśli masz pewność, że Twój byt nie został zmodyfikowany (lub jeśli zgadzasz się, że jakiekolwiek modyfikacje zostaną utracone), możesz ponownie dołączyć go do sesji z blokadą.
Nic nie zablokuje, ale pobierze byt z pamięci podręcznej sesji lub (jeśli go nie znajdzie) odczytać z bazy danych.
Bardzo przydatne jest zapobieganie LazyInitException podczas nawigacji w relacjach ze „starych” (na przykład z HttpSession) jednostek. Najpierw „ponownie dołączasz” byt.
Korzystanie z get może również działać, z wyjątkiem sytuacji, w której mapowane jest dziedziczenie (które już wyrzuci wyjątek na getId ()).
źródło
Session.lock(entity, LockMode.NONE)
z wyjątkiem mówi: nie można ponownie powiązać niezainicjowanej kolekcji transjentów. Jak temu zaradzić?Session.find()
metody API. Być może masz na myśliSession.load(Object object, Serializable id)
.Stany podmiotu
WZP definiuje następujące stany encji:
Nowy (przejściowy)
Nowo utworzony obiekt, który nigdy nie był powiązany z Hibernacją
Session
(aliasPersistence 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#persist
metodę, 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
Session
czasu 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.
Przejścia stanu encji
Możesz zmienić stan encji przy użyciu różnych metod zdefiniowanych przez
EntityManager
interfejs.Aby lepiej zrozumieć przejścia stanu encji JPA, rozważ następujący diagram:
Korzystając z JPA, aby ponownie powiązać odłączony obiekt z aktywnym
EntityManager
, możesz użyć operacji scalania .Korzystając z natywnego interfejsu API Hibernacji, oprócz tego
merge
można ponownie podłączyć odłączony byt do aktywnej sesji hibernacji przy użyciu metod aktualizacji, jak pokazano na poniższym diagramie:Scalanie odłączonego bytu
Scalenie spowoduje skopiowanie stanu odłączonej jednostki (źródła) do instancji jednostki zarządzanej (miejsca docelowego).
Rozważmy, że utrwaliliśmy następującą
Book
jednostkę, a teraz jednostka jest odłączona, ponieważ ta,EntityManager
która była używana do utrwalenia, została zamknięta:Gdy jednostka jest w stanie odłączonym, modyfikujemy ją w następujący sposób:
Teraz chcemy propagować zmiany w bazie danych, abyśmy mogli wywołać
merge
metodę:Hibernacja wykona następujące instrukcje SQL:
Jeśli łącząca się jednostka nie ma odpowiednika w bieżącym
EntityManager
, migawka nowej jednostki zostanie pobrana z bazy danych.Gdy istnieje zmieniony obiekt, JPA kopiuje stan odłączonego obiektu na ten, który jest obecnie zarządzany, a podczas kontekstu
flush
trwałości zostanie wygenerowana AKTUALIZACJA, jeśli mechanizm brudnego sprawdzania wykryje, że zarządzany obiekt się zmienił.Ponowne podłączanie odłączonego bytu
Hibernacja, ale nie JPA obsługuje ponowne podłączanie za pomocą tej
update
metody.Hibernacja
Session
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żącym Hibernacją
Session
.Biorąc pod uwagę, że utrwaliliśmy
Book
encję i zmodyfikowaliśmy ją, gdyBook
encja była w stanie odłączonym:Możemy ponownie podłączyć odłączony byt w następujący sposób:
Hibernacja wykona następującą instrukcję SQL:
W odróżnieniu od tego
merge
, podana odłączona jednostka zostanie ponownie powiązana z bieżącym kontekstem trwałości, a podczas aktualizacji zaplanowano aktualizację, niezależnie od tego, czy jednostka zmodyfikowała się, czy nie.Aby temu zapobiec, możesz użyć
@SelectBeforeUpdate
adnotacji Hibernacja, która wyzwoli instrukcję SELECT, która pobrała stan załadowania, który jest następnie używany przez mechanizm sprawdzania nieczytelności.Uwaga na wyjątek NonUniqueObjectException
Jednym z problemów, które mogą wystąpić,
update
jest to, czy kontekst utrwalania zawiera już odwołanie do jednostki o tym samym identyfikatorze i tego samego typu, co w poniższym przykładzie:Teraz, wykonując powyższy przypadek testowy, Hibernacja rzuci a,
NonUniqueObjectException
ponieważ drugiEntityManager
zawiera jużBook
encję o tym samym identyfikatorze, co ten, który przekazujemyupdate
, a kontekst trwałości nie może pomieścić dwóch reprezentacji tej samej encji.Wniosek
Ta
merge
metoda jest preferowana, jeśli korzystasz z blokowania optymistycznego, ponieważ pozwala ona zapobiec utracie aktualizacji. Więcej informacji na ten temat można znaleźć w tym artykule .Jest
update
to przydatne w przypadku aktualizacji wsadowych, ponieważ może zapobiec dodatkowej instrukcji SELECT generowanej przezmerge
operację, a tym samym skrócić czas wykonywania aktualizacji wsadowej.źródło
@SelectBeforeUpdate
jednak nad adnotacją. Kiedy uruchamiany jest wybór? Podczas dzwonieniaupdate
, tuż przed opróżnieniem, czy to naprawdę nie ma znaczenia (może mieć znaczenie, jeśli hibernacja pobrała wszystkie adnotacje w jednym połączeniu przed opróżnieniem)?@SelectBeforeUpdate
Wyzwala SELECT podczas Context Trwałośćflush
operacji. Sprawdź tengetDatabaseSnapshot
sposób wDefaultFlushEntityEventListener
więcej szczegółów.Wróciłem do JavaDoc
org.hibernate.Session
i znalazłem:Tak więc
update()
,saveOrUpdate()
,lock()
,replicate()
imerge()
są opcje kandydujących.update()
: Zgłasza wyjątek, jeśli istnieje trwała instancja o tym samym identyfikatorze.saveOrUpdate()
: Zapisz lub zaktualizujlock()
: Przestarzałereplicate()
: Utrzymuje stan danej odłączonej instancji, ponownie wykorzystując bieżącą wartość identyfikatora.merge()
: Zwraca trwały obiekt o tym samym identyfikatorze. Dana instancja nie zostaje skojarzona z sesją.Dlatego
lock()
nie powinien być stosowany od razu i na podstawie wymagań funkcjonalnych można wybrać jeden lub więcej z nich.źródło
Zrobiłem to w C # z NHibernate, ale w Javie powinno działać tak samo:
Pierwsza blokada została wywołana na każdym obiekcie, ponieważ zawartość zawsze była fałszywa. Problem polega na tym, że NHibernate porównuje obiekty według identyfikatora i typu bazy danych. Zawiera używa
equals
metody, która porównuje przez odniesienie, jeśli nie jest nadpisana. Dzięki tejequals
metodzie działa bez wyjątków:źródło
Session.contains(Object obj)
sprawdza referencję i nie wykryje innej instancji, która reprezentuje ten sam wiersz i jest już do niego dołączona.Oto moje ogólne rozwiązanie dla encji z właściwością identyfikatora.
Jest to jeden z niewielu aspektów .Net EntityFramework podoba mi się, różne opcje dołączania dotyczące zmienionych encji i ich właściwości.
źródło
Wymyśliłem rozwiązanie „odświeżania” obiektu ze sklepu trwałości, który będzie uwzględniał inne obiekty, które mogą być już dołączone do sesji:
źródło
Niestety, nie możesz dodać komentarzy (jeszcze?).
Korzystanie z Hibernacji 3.5.0-Final
Natomiast
Session#lock
metoda ta nieaktualnych, Javadoc nie sugerować użyciuSession#buildLockRequest(LockOptions)#lock(entity)
i jeśli upewnij się, że związki mającascade=lock
, leniwe ładowanie nie jest problem albo.Więc moja metoda dołączania wygląda trochę jak
Wstępne testy sugerują, że to działa.
źródło
Być może zachowuje się nieco inaczej w Eclipselink. Aby ponownie dołączyć odłączone obiekty bez uzyskiwania nieaktualnych danych, zwykle wykonuję:
i opcjonalnie drugi krok (aby unieważnić pamięci podręczne):
źródło
spróbuj getHibernateTemplate (). replicate (encja, ReplicationMode.LATEST_VERSION)
źródło
W oryginalnym poście, istnieją dwie metody,
update(obj)
imerge(obj)
które są wymienione do pracy, ale w przeciwnych okolicznościach. Jeśli to prawda, to dlaczego nie przetestować, aby najpierw sprawdzić, czy obiekt jest już w sesji, a następnie wywołać,update(obj)
jeśli tak, w przeciwnym razie wywołaćmerge(obj)
.Testem na obecność w sesji jest
session.contains(obj)
. Dlatego wydaje mi się, że działałby następujący pseudo-kod:źródło
aby ponownie podłączyć ten obiekt, musisz użyć merge ();
ta metoda przyjmuje parametr odłączony przez jednostkę i zwraca jednostkę, która zostanie dołączona i ponownie załadowana z bazy danych.
źródło
wywołanie najpierw merge () (w celu aktualizacji trwałej instancji), a następnie zablokowanie (LockMode.NONE) (w celu dołączenia bieżącej instancji, a nie tej zwróconej przez merge ()) wydaje się działać w niektórych przypadkach użycia.
źródło
Właściwość
hibernate.allow_refresh_detached_entity
załatwiła sprawę dla mnie. Ale jest to ogólna zasada, więc nie jest bardzo odpowiednie, jeśli chcesz to zrobić tylko w niektórych przypadkach. Mam nadzieję, że to pomoże.Testowane na Hibernacji 5.4.9
SessionFactoryOptionsBuilder
źródło
Obsługa hibernacji ponownie podłącz odłączony byt na serwalski sposób, patrz instrukcja użytkownika Hibernacja .
źródło
źródło