2013-04-26 11 views
52

że zasadniczo mają kilka przedmiotów w tej konfiguracji (model danych rzeczywistych jest nieco bardziej skomplikowana)hibernacji błędu: inny obiekt o tej samej wartości identyfikatora już związane z sesją

  • A ma wiele -to-wielu B. (B posiada inverse="true")
  • B ma związek wiele-do-jednego z C (mam cascade zestaw do "save-update")
  • C jest rodzajem typ/kategorii tabeli.

Ponadto, powinienem wspomnieć, że klucze podstawowe są generowane przez bazę danych przy zapisie.

Z moimi danymi, czasami napotykam na problemy, w których A ma zestaw różnych obiektów B, a te obiekty B odnoszą się do tego samego obiektu C.

Po wywołaniu session.saveOrUpdate(myAObject) pojawia się błąd hibernacji z informacją: "a different object with the same identifier value was already associated with the session: C". Wiem, że hibernacja nie może wstawić/zaktualizować/usunąć tego samego obiektu dwa razy w tej samej sesji, ale czy jest jakiś sposób obejścia tego? Nie wydaje się, żeby to było niezbyt częste.

Podczas moich badań tego problemu widziałem, jak ludzie sugerują użycie session.merge(), ale kiedy to zrobię, wszelkie "sprzeczne" obiekty zostaną wstawione do bazy danych jako puste obiekty z wszystkimi wartościami ustawionymi na wartość null. Oczywiście, tego nie chcemy.

[Edytuj] Kolejną rzeczą, o której zapomniałem wspomnieć, jest to, że (z przyczyn architektonicznych pozostających poza moją kontrolą) każdy odczyt lub zapis należy wykonać w oddzielnej sesji.

+0

Zobacz, czy to [ ** Odpowiedź **] (http://stackoverflow.com/questions/1074081/hibernate-error-org-hibernate-nonuniqueobjectexception-a-different-object-with) pomaga .. – joaonlima

Odpowiedz

60

Najprawdopodobniej dlatego, że obiekty B nie odwołują się do tej samej instancji obiektu Java C. Odnoszą się do tego samego wiersza w bazie danych (to jest tego samego klucza głównego), ale są to różne kopie tego zbioru.

Tak więc dzieje się tak, że sesja hibernacji, która zarządza obiektami, będzie śledzić, który obiekt Java odpowiada wierszowi z tym samym kluczem podstawowym.

Jedną z opcji jest upewnienie się, że Encje obiektów B, które odnoszą się do tego samego wiersza, faktycznie odnoszą się do tej samej instancji obiektu C. Alternatywnie wyłącz kaskadowanie dla tej zmiennej składowej. W ten sposób, gdy B jest utrwalone, C nie jest. Będziesz musiał jednak zapisać C ręcznie oddzielnie. Jeśli C jest tabelą typu/kategorii, to prawdopodobnie ma to sens.

+2

Dzięki jbx. Jak już powiedziałeś, okazuje się, że obiekty B odnoszą się do wielu instancji C w pamięci. Zasadniczo, co się dzieje, jest to, że jedna część mojego programu czyta w C i dołącza ją do B. Inną częścią jest ładowanie innego B z tym samym C z bazy danych. Oba są dołączane do A, co powoduje błąd przy zapisywaniu. Ustawiłem

cascade
dla relacji B-> C na "
none
", ale nadal otrzymuję ten sam błąd. W relacjach wiele do jednego lub jeden do wielu, czy istnieje sposób, aby powiedzieć Hibernacja tylko, aby zmienić klucz obcy i nie martwić się o resztę? – John

+1

Czy klucz podstawowy C ma strategię generowania identyfikatorów? Jak generator sekwencji czy coś podobnego? – jbx

+0

Tak, każdy ma własną sekwencję w bazie danych. Jak wspomniałeś, kaskadowanie okazało się problemem. Wyłączyliśmy kaskadowanie dla tabel typów, a dla innych użyliśmy kaskady "scalania", która pozwoliła nam wywołać funkcję merge() bez tworzenia wszystkich tych pustych wierszy. Odpowiednio zaznaczyłem twoją odpowiedź, dzięki! – John

0

Użytkownik może nie ustawić identyfikatora obiektu przed wywołaniem zapytania o aktualizację.

+1

Gdyby tak nie było, nie miałby tego problemu. Problem polega na tym, że ma dwa obiekty o tym samym identyfikatorze. – aalku

2

Natknąłem się na tę wiadomość, ale w kodzie C#. Nie wiem, czy jest to istotne (dokładnie ten sam komunikat o błędzie).

Byłem debugowania kodu z punktami przerwania i rozszerzyłem niektóre kolekcje za pośrednictwem prywatnych członków, podczas gdy debugger był w punkcie przerwania. Po ponownym uruchomieniu kodu bez przekopywania struktur, komunikat o błędzie zniknął. Wygląda na to, że akt oglądania prywatnych, leniwych kolekcji sprawił, że NHibernate ładuje rzeczy, które nie powinny być załadowane w tym czasie (ponieważ były w prywatnych członkach).

Sam kod jest zawijany w dość skomplikowaną transakcję, która może aktualizować dużą liczbę rekordów i wiele zależności w ramach tej transakcji (proces importowania).

Mam nadzieję, że jest to wskazówka dla każdego, kto napotka na problem.

5

transferu zadanie przypisanie identyfikatora obiektu z Hibernate do bazy danych za pomocą:

<generator class="native"/> 

To rozwiązało problem dla mnie.

5

Trzeba tylko zrobić jedną rzecz. Uruchom session_object.clear(), a następnie zapisz nowy obiekt. Spowoduje to wyczyszczenie sesji (jako trafnie nazwanej) i usunięcie obraźliwego duplikatu obiektu z sesji.

3

Jednym ze sposobów rozwiązania powyższego problemu będzie zastąpienie hashcode().
Należy również opróżnić sesję hibernacji przed i po zapisaniu.

getHibernateTemplate().flush(); 

Wyraźnie ustawienie odłączonej przedmiotu do null pomaga.

1

Dodaj adnotację @GeneratedValue do wstawianego komponentu.

1

Miałem ten błąd kilka dni przed i miałem zbyt wiele czasu na naprawienie tego błędu.

public boolean save(OrderHeader header) { 
    Session session = sessionFactory.openSession(); 


    Transaction transaction = session.beginTransaction(); 

    try { 
     session.save(header); 

     for (OrderDetail detail : header.getDetails()) { 
      session.save(detail); 
     } 

     transaction.commit(); 
     session.close(); 

     return true; 
    } catch (HibernateException exception) { 

     exception.printStackTrace(); 
     transaction.rollback(); 
     return false; 
    } 
} 

Zanim otrzymam ten błąd, nie wspomniałem o typie generowania identyfikatora w obiekcie OrderDetil. gdy bez generowania identyfikatora Orderdetails zachowuje Id jako 0 dla wszystkich obiektów OrderDetail. to co wyjaśniono #jbx. Tak, to najlepsza odpowiedź. ten jeden przykład jak to się dzieje.

12

Po prostu ustaw kaskadę na MERGE, która powinna wystarczyć.

2

Zgadzam się z @Hemant kumar, dziękuję bardzo się różnią. zgodnie z jego rozwiązaniem rozwiązałem mój problem.

Na przykład:

@Test 
public void testSavePerson() { 
    try (Session session = sessionFactory.openSession()) { 
     Transaction tx = session.beginTransaction(); 
     Person person1 = new Person(); 
     Person person2 = new Person(); 
     person1.setName("222"); 
     person2.setName("111"); 
     session.save(person1); 
     session.save(person2); 
     tx.commit(); 
    } 
} 

Person.java

public class Person { 
    private int id; 
    private String name; 

    @Id 
    @Column(name = "id") 
    public int getId() { 
     return id; 
    } 

    public void setId(int id) { 
     this.id = id; 
    } 

    @Basic 
    @Column(name = "name") 
    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

} 

Ten kod zawsze błąd w mojej aplikacji: A different object with the same identifier value was already associated with the session , później okazało się, że frogot do autoincrease mój główny klucz!

moje rozwiązanie jest dodanie ten kod na swojej podstawowej klucz:

@GeneratedValue(strategy = GenerationType.AUTO) 
1

Postaraj się przed wprowadzeniem kodu zapytania. To naprawić mój problem. np. to zmienić:

query1 
query2 - get the error 
update 

do tego:

query2 
query1 
update 
1

Znajdź "Cascade" atribute w Hibernate i usuń go. Po ustawieniu opcji "Kaskada" będzie wywoływać inne operacje (zapisywanie, aktualizowanie i usuwanie) na innych obiektach, które mają relacje z powiązanymi klasami. Tak więc nastąpi ta sama wartość tożsamości. To działało ze mną.

0

Poznałem problem z powodu generowania klucza podstawowego jest źle, kiedy wstawić wiersz tak:

public void addTerminal(String typeOfDevice,Map<Byte,Integer> map) { 
     // TODO Auto-generated method stub 
     try { 
      Set<Byte> keySet = map.keySet(); 
      for (Byte byte1 : keySet) { 
       Device device=new Device(); 
       device.setNumDevice(DeviceCount.map.get(byte1)); 
       device.setTimestamp(System.currentTimeMillis()); 
       device.setTypeDevice(byte1); 
       this.getHibernateTemplate().save(device); 
      } 
      System.out.println("hah"); 
     }catch (Exception e) { 
      // TODO: handle exception 
      logger.warn("wrong"); 
      logger.warn(e.getStackTrace()+e.getMessage()); 
     } 
} 

zmienić klasę generatora id tożsamości

<id name="id" type="int"> 
    <column name="id" /> 
    <generator class="identity" /> 
</id> 
Powiązane problemy