2013-04-26 1 views
52

ich einige Objekte in dieser Konfiguration im Wesentlichen habe (das reale Datenmodell ist etwas komplexer):Hibernate Fehler: ein anderes Objekt mit dem gleichen Kennungswert wurde bereits mit der Sitzung verknüpft

  • A ein vieles hat -zu-viele-Beziehung mit B. (B hat inverse="true")
  • B hat eine viele-zu-eins-Beziehung mit C (I haben cascade Satz "save-update")
  • C ist eine Art von Typ/Kategorietabelle.

Auch sollte ich wohl erwähnen, dass die Primärschlüssel von der Datenbank generiert werden, auf speichern.

Mit meinen Daten habe ich manchmal Probleme, wo A eine Menge verschiedener B-Objekte hat und diese B-Objekte sich auf dasselbe C-Objekt beziehen.

Wenn ich session.saveOrUpdate(myAObject) anrufen, bekomme ich einen Ruhezustand Fehler sagen: "a different object with the same identifier value was already associated with the session: C". Ich weiß, dass Hibernate das gleiche Objekt nicht zweimal in derselben Sitzung einfügen/aktualisieren/löschen kann, aber gibt es einen Weg um dies zu umgehen? Das scheint nicht so ungewöhnlich für eine Situation zu sein.

Während meiner Forschung zu diesem Problem habe ich gesehen, Leute empfehlen die Verwendung von session.merge(), aber wenn ich das tun, werden alle "widersprüchlichen" Objekte in die Datenbank als leere Objekte mit allen Werten auf Null gesetzt. Das wollen wir natürlich nicht.

[Bearbeiten] Eine andere Sache, die ich vergessen zu erwähnen ist, dass (aus architektonischen Gründen außerhalb meiner Kontrolle), jedes Lesen oder Schreiben in einer separaten Sitzung erfolgen muss.

+0

Sehen Sie, wenn dies zu ändern [ ** antwort **] (http://stackoverflow.com/questions/1074081/hibernate-error-org-hibernate-nonuniqueobjectexception-a-differen-object-with) hilft dir .. – joaonlima

Antwort

60

Wahrscheinlich weil die B-Objekte nicht auf dieselbe Java C-Objektinstanz verweisen. Sie beziehen sich auf dieselbe Zeile in der Datenbank (d. H. Denselben Primärschlüssel), aber sie sind verschiedene Kopien davon.

Was passiert ist, dass die Hibernate-Sitzung, die die Entitäten verwaltet, verfolgen würde, welches Java-Objekt der Zeile mit dem gleichen Primärschlüssel entspricht.

Eine Option wäre, sicherzustellen, dass die Entitäten von Objekten B, die sich auf dieselbe Zeile beziehen, sich tatsächlich auf die gleiche Objektinstanz von C beziehen. Alternativ können Sie die Kaskadierung für diese Elementvariable deaktivieren. Auf diese Weise bleibt B nicht bestehen. Sie müssen C manuell jedoch separat speichern. Wenn C eine Typ/Kategorie-Tabelle ist, dann ist es wahrscheinlich sinnvoll, so zu sein.

+2

Danke jbx. Wie Sie gesagt haben, beziehen sich die B-Objekte auf mehrere C-Instanzen im Speicher. Im Wesentlichen passiert, dass ein Teil meines Programms in C liest und es an B bindet. Ein anderer Teil lädt ein anderes B mit demselben C aus der Datenbank. Beide werden an A angehängt, was den Fehler beim Speichern auslöst. Ich habe die

cascade
für die B-> C-Beziehung auf "
none
" gesetzt, aber ich bekomme immer noch den gleichen Fehler. Gibt es in einer Eins-zu-eins-Beziehung eine Möglichkeit, Hibernate zu sagen, nur den Fremdschlüssel zu ändern und sich nicht um den Rest zu kümmern? – John

+1

Hat der Primärschlüssel von C eine ID-Generierungsstrategie? Wie ein Sequenzgenerator oder ähnliches? – jbx

+0

Ja, jeder hat seine eigene Sequenz in der Datenbank. Wie Sie bereits erwähnt haben, erwies sich die Kaskadierung als das Problem. Wir haben die Kaskadierung für die Typtabellen abgeschaltet, und für die anderen haben wir die "Zusammenführungs" -Kaskade verwendet, die es uns ermöglicht hat, merge() aufzurufen, ohne all diese Nullzeilen zu erzeugen. Ich habe deine Antwort entsprechend markiert, danke! – John

0

Möglicherweise setzen Sie den Bezeichner des Objekts nicht vor dem Aufruf der Aktualisierungsabfrage.

+1

Wenn er nicht wäre, hätte er dieses Problem nicht. Das Problem ist, dass er zwei Objekte mit der gleichen Kennung hat. – aalku

2

Gerade kam diese Nachricht aber in C# -Code. Nicht sicher, ob es relevant ist (genau die gleiche Fehlermeldung).

Ich debugging den Code mit Haltepunkten und erweitert einige Sammlungen durch private Mitglieder, während Debugger an einem Haltepunkt war. Wenn Sie den Code erneut ausführen, ohne Strukturen zu durchsuchen, wird die Fehlermeldung nicht mehr angezeigt. Es sieht so aus, als ob der Blick in private Lazy-Loaded-Sammlungen dazu geführt hat, dass NHibernate Dinge geladen hat, die zu dieser Zeit nicht geladen werden sollten (weil sie sich in privaten Mitgliedern befanden).

Der Code selbst ist in eine ziemlich komplizierte Transaktion verpackt, die eine große Anzahl von Datensätzen und viele Abhängigkeiten als Teil dieser Transaktion (Importprozess) aktualisieren kann.

Hoffentlich ein Hinweis für alle anderen, die auf das Problem stoßen.

5

Übertragung der Aufgabe, die Objekt-ID aus dem Ruhezustand in der Datenbank zuweisen, indem Sie:

<generator class="native"/> 

Dies löste das Problem für mich.

5

Sie müssen nur eine Sache machen. Führen Sie session_object.clear() aus und speichern Sie das neue Objekt. Dadurch wird die Sitzung gelöscht (wie zutreffend benannt) und das problematische doppelte Objekt aus Ihrer Sitzung entfernt.

3

Eine Möglichkeit, das obige Problem zu lösen, besteht darin, die hashcode() zu überschreiben.
Spülen Sie auch die Ruhezustand-Sitzung vor und nach dem Speichern.

getHibernateTemplate().flush(); 

Explizit Einstellung der freistehende Objekt null hilft auch.

1

Fügen Sie die Anmerkung @GeneratedValue der einzufügenden Bean hinzu.

1

Ich hatte diesen Fehler einige Tage und ich habe zu viel Zeit damit verbracht, diesen Fehler zu beheben.

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; 
    } 
} 

Bevor ich diesen Fehler erhalte, habe ich ID-Generierungstyp auf dem OrderDetil-Objekt nicht erwähnt. Wenn die ID der Auftragsdetails nicht generiert wird, wird die ID für jede OrderDetail-Objekte als 0 beibehalten. das was #jbx erklärt. Ja, es ist die beste Antwort. Dieses ein Beispiel, wie es passiert.

12

Setzen Sie einfach Kaskade zu MERGE, das sollte den Trick machen.

2

Ich stimme @Hemant Kumar zu, danke viel variieren. Nach seiner Lösung habe ich mein Problem gelöst.

Zum Beispiel:

@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; 
    } 

} 

Dieser Code immer Fehler in meiner Anwendung machen: A different object with the same identifier value was already associated with the session , später fand ich heraus, dass ich frogot zu autoincrease mein Primärschlüssel!

meine Lösung ist es, diesen Code auf Ihrem Primärschlüssel hinzuzufügen:

@GeneratedValue(strategy = GenerationType.AUTO) 
1

versuchen, bevor Sie den Code Ihrer Abfrage zu platzieren. Das behebt mein Problem. z.B. dies ändern:

query1 
query2 - get the error 
update 

dazu:

query2 
query1 
update 
1

Finden Sie die "Cascade" atribute in Hibernate und löschen. Wenn Sie "Cascade" zur Verfügung stellen, ruft es andere Operationen (Speichern, Aktualisieren und Löschen) für andere Entitäten auf, die eine Beziehung mit verwandten Klassen haben. Es wird also der gleiche Identitätswert passieren. Es hat mit mir funktioniert.

0

ich wegen der Primärschlüssel Generation das Problem erfüllt ist falsch, wenn ich eine Zeile wie folgt einfügen:

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()); 
     } 
} 

ich die ID-Generator-Klasse Identität

<id name="id" type="int"> 
    <column name="id" /> 
    <generator class="identity" /> 
</id> 
Verwandte Themen