Jaki jest właściwy sposób ponownego dołączania odłączonych obiektów w Hibernacji?

186

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.

  1. 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ć.

  2. 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 ...

Stefan Kendall
źródło

Odpowiedzi:

181

Wygląda więc na to, że nie ma sposobu, aby ponownie podłączyć przestarzały odłączony byt w JPA.

merge() wypchnie przestarzały stan do bazy danych i zastąpi wszelkie interweniujące aktualizacje.

refresh() nie można wywołać oderwanego elementu.

lock() nie może zostać wywołany w odłączonym obiekcie, a nawet gdyby mógł, i ponownie go załączył, nazywając „lock” argumentem „LockMode.NONE” sugerującym, że blokujesz, ale nie blokujesz, jest najbardziej sprzecznym z intuicją elementem interfejsu API Jakie kiedykolwiek widziałem.

Więc utknąłeś. Jest detach()metoda, ale nie attach()lub reattach(). Oczywisty krok w cyklu życia obiektu nie jest dostępny.

Sądząc po liczbie podobnych pytań na temat JPA, wydaje się, że nawet jeśli JPA twierdzi, że ma spójny model, z pewnością nie pasuje do modelu mentalnego większości programistów, którzy zostali przeklęci przez wiele godzin próbując zrozumieć, jak zdobyć WZP wykonuje najprostsze rzeczy i kończy na kodzie zarządzania pamięcią podręczną we wszystkich aplikacjach.

Wydaje się, że jedynym sposobem na to jest odrzucenie starej, odłączonej jednostki i wykonanie zapytania znajdującego się z tym samym identyfikatorem, który uderzy w L2 lub DB.

Mik

mikhailfranco
źródło
1
Zastanawiam się, czy istnieje powód, dla którego specyfikacja JPA nie zezwala refresh()na odłączone podmioty? Przeglądając specyfikację 2.0, nie widzę żadnego uzasadnienia; tylko to nie jest dozwolone.
FGreg
11
To zdecydowanie NIE jest dokładne. Z JPwH: *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.2
cwash
Obiekty trwałe działają świetnie, flaga „brudna” jest ustawiana na podstawie delty między początkowym ładowaniem a wartością (wartościami) w czasie opróżniania (). Odłączone obiekty potrzebują i nie mają obecnie tej funkcji. Hibernacja polega na dodaniu dodatkowego skrótu / identyfikatora dla odłączonych obiektów. I zachowaj migawkę ostatniego dostępnego stanu odłączonego obiektu, podobnie jak w przypadku obiektów trwałych. Dzięki temu mogą wykorzystać cały istniejący kod i sprawić, że będzie działał dla odłączonych obiektów. W ten sposób, jak zauważył @mikhailfranco, nie będziemy „wypychać przestarzałego stanu do bazy danych i zastępować wszelkie interweniujące aktualizacje”
tom
2
Zgodnie z Hibernacją javadoc (ale nie JPA), lock(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/14379
seanf
blokada nie działała dla mnie: java.lang.IllegalArgumentException: jednostka nie w kontekście trwałości w org.hibernate.internal.SessionImpl.lock (SessionImpl.java:3491) w org.hibernate.internal.SessionImpl.lock (SessionImpl. java: 3482) w com.github.vok.framework.DisableTransactionControlEMDelegate.lock (DB.kt)
Martin Vysny
32

Wszystkie 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

if ( session.contains( myEntity ) ) {
    // nothing to do... myEntity is already associated with the session
}
else {
    session.saveOrUpdate( myEntity );
}

Zauważ, że użyłem saveOrUpdate () zamiast update (). Jeśli nie chcesz obsługiwać danych jeszcze nie wstawionych, użyj zamiast tego aktualizacji () ...

Steve Ebersole
źródło
3
To jest prawidłowa odpowiedź na to pytanie - sprawa zamknięta!
cwash
2
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.
djmj
W ramach 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.
AxelWass
19

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.

cwash
źródło
5
From seamframework.org : „Aktywny rozwój Seam 3 został zatrzymany przez Red Hata.” Link „ten fragment dokumentacji Seama” również nie żyje.
badbishop
14

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ą.

session.lock(entity, LockMode.NONE);

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 ()).

entity = session.get(entity.getClass(), entity.getId());
John Rizzo
źródło
2
Chciałbym ponownie powiązać jednostkę z sesją. Niestety, Session.lock(entity, LockMode.NONE)z wyjątkiem mówi: nie można ponownie powiązać niezainicjowanej kolekcji transjentów. Jak temu zaradzić?
dma_k
1
W rzeczywistości nie miałem całkowitej racji. Użycie lock () powoduje ponowne podłączenie twojego bytu, ale nie innych powiązanych z nim bytów. Więc jeśli wykonasz entity.getOtherEntity (). GetYetAnotherEntity (), możesz mieć wyjątek LazyInit. Jedynym sposobem na pokonanie tego jest użycie find. entity = em.find (entity.getClass (), entity.getId ();
John Rizzo
Nie ma Session.find()metody API. Być może masz na myśli Session.load(Object object, Serializable id).
dma_k
11

Ponieważ jest to bardzo częste pytanie, napisałem ten artykuł , na którym opiera się ta odpowiedź.

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(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.

Przejścia stanu encji

Możesz zmienić stan encji przy użyciu różnych metod zdefiniowanych przez EntityManagerinterfejs.

Aby lepiej zrozumieć przejścia stanu encji JPA, rozważ następujący diagram:

Przejścia stanu encji JPA

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 mergemożna ponownie podłączyć odłączony byt do aktywnej sesji hibernacji przy użyciu metod aktualizacji, jak pokazano na poniższym diagramie:

Hibernacja przejść stanu encji

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ą Bookjednostkę, a teraz jednostka jest odłączona, ponieważ ta, EntityManagerktóra była używana do utrwalenia, została zamknięta:

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    return book;
});

Gdy jednostka jest w stanie odłączonym, modyfikujemy ją w następujący sposób:

_book.setTitle(
    "High-Performance Java Persistence, 2nd edition"
);

Teraz chcemy propagować zmiany w bazie danych, abyśmy mogli wywołać mergemetodę:

doInJPA(entityManager -> {
    Book book = entityManager.merge(_book);

    LOGGER.info("Merging the Book entity");

    assertFalse(book == _book);
});

Hibernacja wykona następujące instrukcje SQL:

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

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 kontekstuflush trwałości zostanie wygenerowana AKTUALIZACJA, jeśli mechanizm brudnego sprawdzania wykryje, że zarządzany obiekt się zmienił.

Tak więc podczas używania mergeinstancja odłączonego obiektu pozostanie odłączona nawet po operacji scalania.

Ponowne podłączanie odłączonego bytu

Hibernacja, ale nie JPA obsługuje ponowne podłączanie za pomocą tej updatemetody.

Hibernacja Sessionmoż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 Bookencję i zmodyfikowaliśmy ją, gdy Bookencja była w stanie odłączonym:

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    return book;
});

_book.setTitle(
    "High-Performance Java Persistence, 2nd edition"
);

Możemy ponownie podłączyć odłączony byt w następujący sposób:

doInJPA(entityManager -> {
    Session session = entityManager.unwrap(Session.class);

    session.update(_book);

    LOGGER.info("Updating the Book entity");
});

Hibernacja wykona następującą instrukcję SQL:

-- Updating the Book entity

UPDATE
    book
SET
    author = 'Vlad Mihalcea',
    isbn = '978-9730228236',
    title = 'High-Performance Java Persistence, 2nd edition'
WHERE
    id = 1

Ta updatemetoda wymaga przejścia do unwrapstanu EntityManagerhibernacji Session.

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ć @SelectBeforeUpdateadnotacji 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.

@Entity(name = "Book")
@Table(name = "book")
@SelectBeforeUpdate
public class Book {

    //Code omitted for brevity
}

Uwaga na wyjątek NonUniqueObjectException

Jednym z problemów, które mogą wystąpić, updatejest to, czy kontekst utrwalania zawiera już odwołanie do jednostki o tym samym identyfikatorze i tego samego typu, co 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, NonUniqueObjectExceptionponieważ drugi EntityManagerzawiera już Bookencję o tym samym identyfikatorze, co ten, który przekazujemy update, a kontekst trwałości nie może pomieścić 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)

Wniosek

Ta mergemetoda 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 updateto przydatne w przypadku aktualizacji wsadowych, ponieważ może zapobiec dodatkowej instrukcji SELECT generowanej przez mergeoperację, a tym samym skrócić czas wykonywania aktualizacji wsadowej.

Vlad Mihalcea
źródło
Niezła odpowiedź. Zastanawiałem się @SelectBeforeUpdatejednak nad adnotacją. Kiedy uruchamiany jest wybór? Podczas dzwonienia update, 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)?
Andronicus
@SelectBeforeUpdateWyzwala SELECT podczas Context Trwałość flushoperacji. Sprawdź ten getDatabaseSnapshotsposób wDefaultFlushEntityEventListener więcej szczegółów.
Vlad Mihalcea
10

Wróciłem do JavaDoc org.hibernate.Sessioni znalazłem:

Przemijające przypadki mogą być trwałe przez powołanie save(), persist()albo saveOrUpdate(). Trwałe instancje mogą stać się przejściowe przez wywołanie delete(). Każde wystąpienie zwrócone przez metodę get()lub load()jest trwałe. Wolnostojące przypadki mogą być trwałe przez wywołanie update(), saveOrUpdate(), lock()lub replicate(). Stan instancji przejściowej lub odłączonej można również ustawić jako trwałą nową instancję przez wywołanie merge().

Tak więc update(), saveOrUpdate(), lock(), replicate()i merge()są opcje kandydujących.

update(): Zgłasza wyjątek, jeśli istnieje trwała instancja o tym samym identyfikatorze.

saveOrUpdate(): Zapisz lub zaktualizuj

lock(): Przestarzałe

replicate(): 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.

Amitabha Roy
źródło
7

Zrobiłem to w C # z NHibernate, ale w Javie powinno działać tak samo:

public virtual void Attach()
{
    if (!HibernateSessionManager.Instance.GetSession().Contains(this))
    {
        ISession session = HibernateSessionManager.Instance.GetSession();
        using (ITransaction t = session.BeginTransaction())
        {
            session.Lock(this, NHibernate.LockMode.None);
            t.Commit();
        }
    }
}

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 equalsmetody, która porównuje przez odniesienie, jeśli nie jest nadpisana. Dzięki tej equalsmetodzie działa bez wyjątków:

public override bool Equals(object obj)
{
    if (this == obj) { 
        return true;
    } 
    if (GetType() != obj.GetType()) {
        return false;
    }
    if (Id != ((BaseObject)obj).Id)
    {
        return false;
    }
    return true;
}
Verena Haunschmid
źródło
4

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.

public static void update(final Session session, final Object entity)
{
    // if the given instance is in session, nothing to do
    if (session.contains(entity))
        return;

    // check if there is already a different attached instance representing the same row
    final ClassMetadata classMetadata = session.getSessionFactory().getClassMetadata(entity.getClass());
    final Serializable identifier = classMetadata.getIdentifier(entity, (SessionImplementor) session);

    final Object sessionEntity = session.load(entity.getClass(), identifier);
    // override changes, last call to update wins
    if (sessionEntity != null)
        session.evict(sessionEntity);
    session.update(entity);
}

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.

djmj
źródło
3

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:

public void refreshDetached(T entity, Long id)
{
    // Check for any OTHER instances already attached to the session since
    // refresh will not work if there are any.
    T attached = (T) session.load(getPersistentClass(), id);
    if (attached != entity)
    {
        session.evict(attached);
        session.lock(entity, LockMode.NONE);
    }
    session.refresh(entity);
}
WhoopP
źródło
2

Niestety, nie możesz dodać komentarzy (jeszcze?).

Korzystanie z Hibernacji 3.5.0-Final

Natomiast Session#lockmetoda ta nieaktualnych, Javadoc nie sugerować użyciu Session#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

MyEntity attach(MyEntity entity) {
    if(getSession().contains(entity)) return entity;
    getSession().buildLockRequest(LockOptions.NONE).lock(entity);
    return entity;

Wstępne testy sugerują, że to działa.

Gwaptiva
źródło
2

Być może zachowuje się nieco inaczej w Eclipselink. Aby ponownie dołączyć odłączone obiekty bez uzyskiwania nieaktualnych danych, zwykle wykonuję:

Object obj = em.find(obj.getClass(), id);

i opcjonalnie drugi krok (aby unieważnić pamięci podręczne):

em.refresh(obj)
Hartmut P.
źródło
1

spróbuj getHibernateTemplate (). replicate (encja, ReplicationMode.LATEST_VERSION)

Pavitar Singh
źródło
1

W oryginalnym poście, istnieją dwie metody, update(obj)i merge(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:

if (session.contains(obj))
{
    session.update(obj);
}
else 
{
    session.merge(obj);
}
John DeRegnaucourt
źródło
2
zawiera () sprawdza porównuje przez odniesienie, ale funkcje hibernacji działają według identyfikatora bazy danych. session.merge nigdy nie będzie wywoływany w twoim kodzie.
Verena Haunschmid
1

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.

Example :
    Lot objAttach = em.merge(oldObjDetached);
    objAttach.setEtat(...);
    em.persist(objAttach);
Ryuku
źródło
0

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.

ser
źródło
0

Właściwość hibernate.allow_refresh_detached_entityzał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

Radeck
źródło
-6
try getHibernateTemplate().saveOrUpdate()
Ben Hammond
źródło