Błąd hibernacji: org.hibernate.NonUniqueObjectException: inny obiekt o tej samej wartości identyfikatora był już powiązany z sesją

114

Mam dwa obiekty użytkownika i podczas próby zapisania obiektu za pomocą

session.save(userObj);

Otrzymuję następujący błąd:

Caused by: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session:
[com.pojo.rtrequests.User#com.pojo.rtrequests.User@d079b40b]

Sesję tworzę za pomocą

BaseHibernateDAO dao = new BaseHibernateDAO();          

rtsession = dao.getSession(userData.getRegion(),
                           BaseHibernateDAO.RTREQUESTS_DATABASE_NAME);

rttrans = rtsession.beginTransaction();
rttrans.begin();

rtsession.save(userObj1);
rtsession.save(userObj2);

rtsession.flush();
rttrans.commit();

rtsession.close(); // in finally block

Próbowałem też zrobić to session.clear()przed zapisaniem, nadal bez powodzenia.

Po raz pierwszy otrzymuję obiekt sesji, gdy przychodzi żądanie użytkownika, więc otrzymuję dlaczego mówi, że obiekt jest obecny w sesji.

Jakieś sugestie?

ostry
źródło
Oto kolejny wspaniały wątek, który pomógł rozwiązać mój problem. Getj2ee.over-blog.com/…
Reddymails

Odpowiedzi:

173

Miałem ten błąd wiele razy i może być dość trudno wytropić ...

Zasadniczo hibernacja mówi, że masz dwa obiekty, które mają ten sam identyfikator (ten sam klucz podstawowy), ale nie są tym samym obiektem.

Proponuję rozbić kod, tj. Zakomentować bity, aż błąd zniknie, a następnie wstawić kod z powrotem, aż wróci i powinieneś znaleźć błąd.

Najczęściej dzieje się to poprzez składowanie kaskadowe, gdzie między obiektami A i B jest zapis kaskadowy, ale obiekt B został już powiązany z sesją, ale nie znajduje się w tej samej instancji B, co obiekt w A.

Jakiego generatora kluczy podstawowych używasz?

Pytam o to, że ten błąd jest związany ze sposobem, w jaki mówisz do hibernacji, aby ustalić trwały stan obiektu (tj. Czy obiekt jest trwały, czy nie). Błąd może występować, ponieważ hibernacja próbuje utrwalić obiekt, który jest już trwały. W rzeczywistości, jeśli użyjesz zapisania, hibernacja spróbuje zachować ten obiekt, a być może istnieje już obiekt z tym samym kluczem podstawowym skojarzonym z sesją.

Przykład

Zakładając, że masz obiekt klasy hibernacji dla tabeli z 10 wierszami na podstawie kombinacji klawiszy podstawowych (kolumna 1 i kolumna 2). Teraz w pewnym momencie usunąłeś 5 wierszy z tabeli. Teraz, jeśli spróbujesz ponownie dodać te same 10 wierszy, podczas gdy hibernacja próbuje zachować obiekty w bazie danych, 5 wierszy, które zostały już usunięte, zostanie dodanych bez błędów. Teraz pozostałe 5 wierszy, które już istnieją, zgłosi ten wyjątek.

Więc prostym podejściem byłoby sprawdzenie, czy zaktualizowałeś / usunąłeś jakąkolwiek wartość w tabeli, która jest częścią czegoś, a później czy próbujesz ponownie wstawić te same obiekty

Michael Wiles
źródło
4
Niezła odpowiedź². Kluczem podstawowym był mój problem, rozwiązany przez ustawienie GeneratedValue sekwencji dla postgresql.
Rodrigo Ferrari
2
Miałem ten sam problem. W moim przypadku przeszukałem obiekt w kodzie i próbowałem zbudować nowy obiekt o tym samym identyfikatorze w innym fragmencie kodu, podczas gdy pierwszy obiekt był jeszcze w sesji hibernacji.
dellasavia
18

To tylko jeden punkt, w którym hibernacja stwarza więcej problemów niż rozwiązuje. W moim przypadku jest wiele obiektów o tym samym identyfikatorze 0, ponieważ są nowe i go nie mają. Baza danych je generuje. Gdzieś przeczytałem, że 0 sygnałów nie było ustawione. Intuicyjnym sposobem ich utrwalenia jest iterowanie po nich i mówienie o hibernacji, aby zapisać obiekty. Ale nie możesz tego zrobić - „Oczywiście powinieneś wiedzieć, że hibernacja działa w ten i ten sposób, dlatego musisz…” Więc teraz mogę spróbować zmienić ID na długie zamiast na długie i sprawdzić, czy to działa. Ostatecznie łatwiej jest to zrobić za pomocą prostego mapera, ponieważ hibernacja jest tylko dodatkowym nieprzejrzystym obciążeniem. Inny przykład: próba odczytania parametrów z jednej bazy danych i utrwalenia ich w innej wymusza wykonanie prawie całej pracy ręcznie.

Hein
źródło
13
Ja też nienawidzę hibernacji ... i baz danych ... Po wszystkich problemach, które mi spowodowały, myślę, że łatwiej jest użyć pliku tekstowego (żartuję, ale nadal ...).
Igor Popov
W moim przypadku jest wiele obiektów o tym samym identyfikatorze 0, ponieważ są nowe i go nie mają. Baza danych je generuje. Gdzieś przeczytałem, że 0 sygnałów nie było ustawione. Intuicyjnym sposobem ich utrwalenia jest iterowanie po nich i mówienie o hibernacji, aby zapisać obiekty . Muszę dokładnie to zrobić. Czy możesz mi powiedzieć, jaki jest „sposób hibernacji”, aby to zrobić?
Ramzes
W moim przypadku musiałem użyć session.merge (myobject), ponieważ miałem dwa wystąpienia tego obiektu, zwykle dzieje się tak, gdy jednostka jest utrwalana i odłączana od sesji. Kolejne wystąpienie tej jednostki jest żądane do hibernacji. Ta druga instancja pozostała połączona z sesją. Pierwsza instancja jest modyfikowana. Więcej na getj2ee.over-blog.com/…
Reddymails
to nie hibernacja powoduje problemy, ale brak zrozumienia, jak to działa
ACV
17

USe session.evict(object);Funkcja evict()metody służy do usuwania instancji z pamięci podręcznej sesji. Tak więc przy pierwszym zapisaniu obiektu zapisz obiekt, wywołując session.save(object)metodę przed wykluczeniem obiektu z pamięci podręcznej. W ten sam sposób aktualizuj obiekt przez wywołanie session.saveOrUpdate(object)lub session.update(object)przed wywołaniem evict ().

Rahul N
źródło
11

Może się to zdarzyć, gdy użyłeś tego samego obiektu sesji do odczytu i zapisu. W jaki sposób? Powiedzmy, że utworzyłeś jedną sesję. Odczytujesz rekord z tabeli pracowników z kluczem podstawowym Emp_id = 101 Teraz zmodyfikowałeś rekord w Javie. Zamierzasz zapisać rekord pracownika w bazie danych. nigdzie nie zamknęliśmy sesji. Ponieważ obiekt, który został odczytany, również pozostaje w sesji. Koliduje z przedmiotem, który chcemy napisać. Stąd ten błąd.

Prashant Bari
źródło
9

Jak ktoś już wskazał powyżej, napotkałem ten problem, gdy miałem cascade=allna obu końcach one-to-manyrelacji, więc załóżmy, że A -> B (jeden do wielu z A i wiele do jednego z B) i aktualizowałem instancję B w A, a następnie wywołanie saveOrUpdate (A), spowodowało to cykliczne żądanie składowania, tj. Zapis A wyzwala zapis B, który wyzwala zapis A ... iw trzecim przypadku, gdy jednostka (A) była próbowana być dodany do sessionPersistenceContext, zgłoszony został wyjątek duplicateObject.
  Mogłem to rozwiązać, usuwając kaskadę z jednego końca.

redzedi
źródło
Rozwiązałem mój problem razem ze stackoverflow.com/questions/4334970/ ...
Magno C
5

Możesz użyć session.merge(obj), jeśli robisz składowanie z różnymi sesjami z tym samym trwałym obiektem identyfikatora.
Udało się, wcześniej miałem ten sam problem.

Pavan Kumar
źródło
4

Natknąłem się również na ten problem i ciężko mi było znaleźć błąd.

Problem, który miałem, był następujący:

Obiekt został odczytany przez Dao podczas innej sesji hibernacji.

Aby uniknąć tego wyjątku, po prostu ponownie przeczytaj obiekt za pomocą dao, który później zapisze / zaktualizuje ten obiekt.

więc:

class A{      

 readFoo(){
       someDaoA.read(myBadAssObject); //Different Session than in class B
    }

}

class B{



 saveFoo(){
       someDaoB.read(myBadAssObjectAgain); //Different Session than in class A
       [...]
       myBadAssObjectAgain.fooValue = 'bar';
       persist();
    }

}

Mam nadzieję, że zaoszczędzi to niektórym ludziom dużo czasu!

Frithjof
źródło
4

Natknąłem się na ten problem przez:

  1. Usuwanie obiektu (przy użyciu HQL)
  2. Natychmiastowe przechowywanie nowego obiektu z tym samym identyfikatorem

Rozwiązałem to, opróżniając wyniki po usunięciu i czyszcząc pamięć podręczną przed zapisaniem nowego obiektu

String delQuery = "DELETE FROM OasisNode";
session.createQuery( delQuery ).executeUpdate();
session.flush();
session.clear();
wbdarby
źródło
4

Ten problem pojawia się, gdy aktualizujemy ten sam obiekt sesji, którego użyliśmy do pobrania obiektu z bazy danych.

Możesz użyć metody scalania hibernacji zamiast metody aktualizacji.

np. Najpierw użyj session.get (), a następnie możesz użyć session.merge (obiekt). Ta metoda nie spowoduje żadnego problemu. Możemy również użyć metody merge () do aktualizacji obiektu w bazie danych.

Jayendra Birtharia
źródło
3

Pobierz obiekt w ramach sesji, tutaj przykład:

MyObject ob = null;
ob = (MyObject) session.get(MyObject.class, id);
sock_osg
źródło
3
Niezła próba, ale obiekt „ob” jest zwracany bez zaktualizowanych danych (biorąc pod uwagę, że został zaktualizowany przez aplikację w czasie wykonywania, między pobraniem obiektów z bazy danych a jej zapisaniem).
Alex
2

Czy twoje mapowania identyfikatorów są prawidłowe? Jeśli baza danych jest odpowiedzialna za tworzenie Id za pomocą identyfikatora, musisz zmapować swój obiekt użytkownika do tego.

cwap
źródło
2

Napotkałem ten problem przy usuwaniu obiektu, ani eksmisja, ani czyszczenie nie pomogły.

/**
 * Deletes the given entity, even if hibernate has an old reference to it.
 * If the entity has already disappeared due to a db cascade then noop.
 */
public void delete(final Object entity) {
  Object merged = null;
  try {
    merged = getSession().merge(entity);
  }
  catch (ObjectNotFoundException e) {
    // disappeared already due to cascade
    return;
  }
  getSession().delete(merged);
}
TimP
źródło
2

przed pozycją, w której zaczynają się powtarzające się obiekty, należy zamknąć sesję, a następnie rozpocząć nową sesję

session.close();      
session = HibernateUtil.getSessionFactory().openSession();

w ten sposób w jednej sesji nie ma więcej niż jednej jednostki, która ma ten sam identyfikator.

engtuncay
źródło
2

Spóźniony na imprezę, ale może pomóc przyszłym użytkownikom -

Mam ten problem, gdy wybieram rekord za pomocą getsession() i ponownie aktualizuję inny rekord z tym samym identyfikatorem przy użyciu tej samej sesji powoduje problem. Dodano kod poniżej.

Customer existingCustomer=getSession().get(Customer.class,1);
Customer customerFromUi;// This customer details comiong from UI with identifer 1

getSession().update(customerFromUi);// Here the issue comes

Nigdy nie powinno się tego robić. Rozwiązaniem jest wykluczenie sesji przed aktualizacją lub zmiana logiki biznesowej.

vipin cp
źródło
1

Sprawdź, czy nie zapomniałeś wstawić @GenerateValue dla kolumny @Id. Miałem ten sam problem z wieloma do wielu związkami między filmem a gatunkiem. Program wyrzucił błąd Hibernate: org.hibernate.NonUniqueObjectException: inny obiekt o tej samej wartości identyfikatora był już powiązany z błędem sesji. Później dowiedziałem się, że muszę się tylko upewnić, że w metodzie GenreId get masz @GenerateValue.

Thupten
źródło
jak mogę zastosować twoje rozwiązanie do mojego pytania? utworzyć kolumnę id w klasie Task? stackoverflow.com/questions/55732955/ ...
sbattou
1

po prostu sprawdź identyfikator, czy przyjmuje wartość null lub 0

if(offersubformtwo.getId()!=null && offersubformtwo.getId()!=0)

w dodawaniu lub aktualizowaniu, gdzie zawartość jest ustawiana z formularza na Pojo

Ayan Sarkar
źródło
1

Jestem nowy w NHibernate i mój problem polegał na tym, że użyłem innej sesji do odpytania o mój obiekt niż do zapisania go. Więc sesja zapisu nie wiedziała o obiekcie.

Wydaje się to oczywiste, ale czytając poprzednie odpowiedzi szukałem wszędzie 2 obiektów, a nie 2 sesji.

DustinA
źródło
1

@GeneratedValue (strategy = GenerationType.IDENTITY), dodanie tej adnotacji do właściwości klucza podstawowego w komponencie bean encji powinno rozwiązać ten problem.

Sójka
źródło
1

Rozwiązałem ten problem.
Właściwie to się dzieje, ponieważ zapomnieliśmy o implementacji właściwości PK typu generatora w klasie bean. Więc zrób to dowolny typ, taki jak

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;

kiedy utrwalamy obiekty z fasoli, każdy obiekt uzyskał ten sam identyfikator, więc pierwszy obiekt jest zapisywany, gdy inny obiekt ma być utrwalony, to HIB FW przez ten typ Exception: org.hibernate.NonUniqueObjectException:innego obiektu o tej samej wartości identyfikatora był już powiązany z sesją.

Ankit Sharma
źródło
1

Problem występuje, ponieważ w tej samej sesji hibernacji próbujesz zapisać dwa obiekty o tym samym identyfikatorze.Istnieją dwa rozwiązania: -

  1. Dzieje się tak, ponieważ nie skonfigurowałeś poprawnie pliku mapping.xml dla pól id, jak poniżej: -

    <id name="id">
      <column name="id" sql-type="bigint" not-null="true"/>
      <generator class="hibernateGeneratorClass"</generator>
    </id>
  2. Przeciąż metodę getsession, aby zaakceptować parametr, taki jak isSessionClear, i wyczyść sesję przed zwróceniem bieżącej sesji, jak poniżej

    public static Session getSession(boolean isSessionClear) {
        if (session.isOpen() && isSessionClear) {
            session.clear();
            return session;
        } else if (session.isOpen()) {
            return session;
        } else {
            return sessionFactory.openSession();
        }
    }

Spowoduje to wyczyszczenie istniejących obiektów sesji, a nawet jeśli hibernacja nie wygeneruje unikalnego identyfikatora, zakładając, że poprawnie skonfigurowałeś bazę danych dla klucza podstawowego przy użyciu czegoś takiego jak Auto_Increment, powinno to działać dla Ciebie.

Rahul Kumar
źródło
1

Miałem podobny problem. W moim przypadku zapomniałem ustawić increment_bywartość w bazie danych na taką samą, jak ta używana przez cache_sizei allocationSize. (Strzałki wskazują wymienione atrybuty)

SQL:

CREATED         26.07.16
LAST_DDL_TIME   26.07.16
SEQUENCE_OWNER  MY
SEQUENCE_NAME   MY_ID_SEQ
MIN_VALUE       1
MAX_VALUE       9999999999999999999999999999
INCREMENT_BY    20 <-
CYCLE_FLAG      N
ORDER_FLAG      N
CACHE_SIZE      20 <-
LAST_NUMBER     180

Jawa:

@SequenceGenerator(name = "mySG", schema = "my", 
sequenceName = "my_id_seq", allocationSize = 20 <-)
kaba713
źródło
1

W przeciwieństwie do tego, co powiedział wbdarby , może się to zdarzyć nawet, gdy obiekt jest pobierany przez przekazanie identyfikatora obiektu do HQL. W przypadku próby zmodyfikowania pól obiektu i zapisania go z powrotem do DB (modyfikacja może być wstawieniem, usunięciem lub aktualizacją) w tej samej sesji , pojawi się ten błąd. Spróbuj wyczyścić sesję hibernacji przed zapisaniem zmodyfikowanego obiektu lub utwórz zupełnie nową sesję.

Mam nadzieję, że pomogłem ;-)

Arash moradabadi
źródło
1

Mam ten sam błąd co przy wymianie mojego zestawu na nowy otrzymany od Jacksona.

Aby rozwiązać ten problem, zachowuję istniejący zestaw, usuwam ze starego zestawu nieznany element do nowej listy za pomocą retainAll. Następnie dodaję nowe z addAll.

    this.oldSet.retainAll(newSet);
    this.oldSet.addAll(newSet);

Nie ma potrzeby posiadania sesji i manipulowania nią.

Ôrel
źródło
1

Spróbuj tego. Poniższy działał dla mnie!

W hbm.xmlpliku

  1. Musimy ustawić dynamic-updateatrybut tagu klasy na true:

    <class dynamic-update="true">
  2. Ustaw atrybut class znacznika generatora w unikalnej kolumnie na identity:

    <generator class="identity">

Uwaga: ustaw unikatową kolumnę identityzamiast assigned.

RaGa__M
źródło
0

Inną rzeczą, która zadziałała, było ustawienie zmiennej instancji Long zamiast long


Miałem długi id zmiennej klucza głównego; zmieniając go na Long id; pracował

Wszystkiego najlepszego


źródło
0

Zawsze możesz wykonać rzut sesyjny. Flush zsynchronizuje stan wszystkich twoich obiektów w sesji (proszę, ktoś mnie poprawi, jeśli się mylę) i być może w niektórych przypadkach rozwiąże twój problem.

Wdrożenie własnych równych sobie i hashcode również może Ci pomóc.

caarlos0
źródło
0

Możesz sprawdzić ustawienia kaskady. Przyczyną mogą być ustawienia kaskady w Twoich modelach. Usunąłem Ustawienia kaskadowe (zasadniczo nie zezwalam na wstawianie / aktualizacje kaskadowe) i to rozwiązało mój problem

user1609848
źródło
0

Znalazłem również ten błąd. Udało mi się upewnić, że klucz podstawowy (który jest generowany automatycznie) nie jest PDT (tj. Long, int, ect.), Ale obiektem (tj. Long, Integer itp.)

Kiedy tworzysz obiekt, aby go zapisać, upewnij się, że podajesz wartość null, a nie 0.

Jaco Van Niekerk
źródło
0

czy to pomaga?

User userObj1 = new User();
User userObj2 = userObj1;
.
.
.
rtsession.save(userObj1);
rtsession.save(userObj2); 
George Papatheodorou
źródło
0

Rozwiązałem podobny problem, taki jak ten:

plan = (FcsRequestPlan) session.load(plan.getClass(), plan.getUUID());
while (plan instanceof HibernateProxy)
    plan = (FcsRequestPlan) ((HibernateProxy) plan).getHibernateLazyInitializer().getImplementation();
user3132194
źródło