Ruhezustand: Der Sitzung wurde bereits ein anderes Objekt mit demselben Bezeichnerwert zugeordnet

90

Ich habe im Wesentlichen einige Objekte in dieser Konfiguration (das reale Datenmodell ist etwas komplexer):

  • A hat eine Viele-zu-Viele-Beziehung zu B. (B hat inverse="true")
  • B hat eine Eins-zu-Eins-Beziehung zu C. (Ich habe cascadeeingestellt auf "save-update")
  • C ist eine Art Typ- / Kategorietabelle.

Außerdem sollte ich wahrscheinlich erwähnen, dass die Primärschlüssel beim Speichern von der Datenbank generiert werden.

Bei meinen Daten treten manchmal Probleme auf, bei denen A eine Reihe verschiedener B-Objekte hat und diese B-Objekte auf dasselbe C-Objekt verweisen.

Wenn ich anrufe session.saveOrUpdate(myAObject), wird eine Fehlermeldung im Ruhezustand angezeigt, die besagt : "a different object with the same identifier value was already associated with the session: C". Ich weiß, dass der Ruhezustand nicht dasselbe Objekt zweimal in derselben Sitzung einfügen / aktualisieren / löschen kann, aber gibt es einen Weg, dies zu umgehen? Dies scheint nicht so ungewöhnlich zu sein.

Während meiner Untersuchung dieses Problems habe ich Leute gesehen, die die Verwendung von vorgeschlagen haben session.merge(), aber wenn ich das tue, werden alle "widersprüchlichen" Objekte als leere Objekte in die Datenbank eingefügt, wobei alle Werte auf null gesetzt sind. Das wollen wir natürlich nicht.

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

John
quelle
Sehen Sie, ob diese Antwort Ihnen hilft ..
Joaonlima

Antworten:

96

Höchstwahrscheinlich, weil sich die B-Objekte nicht auf dieselbe Java C-Objektinstanz beziehen. Sie beziehen sich auf dieselbe Zeile in der Datenbank (dh denselben Primärschlüssel), sind jedoch unterschiedliche Kopien davon.

Was also passiert, ist, dass die Hibernate-Sitzung, die die Entitäten verwaltet, verfolgt, welches Java-Objekt der Zeile mit demselben Primärschlüssel entspricht.

Eine Möglichkeit wäre, sicherzustellen, dass die Entitäten von Objekten B, die auf dieselbe Zeile verweisen, tatsächlich auf dieselbe Objektinstanz von C verweisen. Alternativ können Sie die Kaskadierung für diese Mitgliedsvariable deaktivieren. Auf diese Weise ist C nicht erhalten, wenn B bestehen bleibt. Sie müssen C jedoch manuell separat speichern. Wenn C eine Typ- / Kategorietabelle ist, ist es wahrscheinlich sinnvoll, dies zu tun.

jbx
quelle
3
Danke jbx. Wie Sie sagten, stellt sich heraus, dass sich die B-Objekte auf mehrere C-Instanzen im Speicher beziehen. Im Wesentlichen liest ein Teil meines Programms C ein und hängt es an B. 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 <pre> -Kaskade </ pre> für die B-> C-Beziehung auf "<pre> none </ pre>" gesetzt, erhalte aber immer noch den gleichen Fehler. Gibt es in vielen-zu-eins- oder eins-zu-vielen-Beziehungen eine Möglichkeit, Hibernate anzuweisen, 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
Ja, jeder hat seine eigene Sequenz in der Datenbank. Wie Sie bereits erwähnt haben, stellte sich die Kaskadierung als Problem heraus. Wir haben die Kaskadierung für die Typentabellen deaktiviert und für die anderen die "Merge" -Kaskade verwendet, mit der wir merge () aufrufen konnten, ohne alle diese Nullzeilen zu erstellen. Ich habe Ihre Antwort entsprechend markiert, danke!
John
14
Ich habe merge () anstelle von saveOrUpdate () und BOOM! es funktioniert :)
Lahiru Ruhunage
27

Stellen Sie einfach die Kaskade auf MERGE, das sollte den Trick machen.

andy.codes
quelle
13

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

dsk
quelle
9

Ich stimme @Hemant Kumar zu, vielen Dank. Nach seiner Lösung habe ich mein Problem gelöst.

Beispielsweise:

@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 macht in meiner Anwendung immer einen Fehler: A different object with the same identifier value was already associated with the sessionSpäter stellte ich fest, dass ich vergessen hatte, meinen Primärschlüssel automatisch zu erhöhen!

Meine Lösung besteht darin, diesen Code Ihrem Primärschlüssel hinzuzufügen:

@GeneratedValue(strategy = GenerationType.AUTO)
Eisblau
quelle
5

Übertragen Sie die Aufgabe zum Zuweisen der Objekt-ID aus dem Ruhezustand zur Datenbank mithilfe von:

<generator class="native"/>

Dies löste das Problem für mich.

user2845946
quelle
5

Dies bedeutet, dass Sie versuchen, mehrere Zeilen in Ihrer Tabelle mit dem Verweis auf dasselbe Objekt zu speichern.

Überprüfen Sie die ID-Eigenschaft Ihrer Entitätsklasse.

@Id
private Integer id;

zu

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(unique = true, nullable = false)
private Integer id;
rex roy
quelle
1
nicht der Fall mit Frage gemäß Beschreibung gegeben
Sudip Bhandari
3

Fügen Sie der Bean, die Sie einfügen, die Anmerkung @GeneratedValue hinzu.

Hemant Kumar
quelle
Vielen Dank! Dies war genau mein Problem
DriLLFreAK100
3

Eine Möglichkeit, das oben genannte Problem zu lösen, besteht darin, das zu überschreiben hashcode().
Leeren Sie auch den Ruhezustand vor und nach dem Speichern.

getHibernateTemplate().flush();

Das explizite Festlegen des getrennten Objekts auf nullhilft ebenfalls.

Waqar
quelle
2

Ich bin gerade auf diese Nachricht gestoßen, aber in C # -Code. Ich bin mir nicht sicher, ob es relevant ist (genau die gleiche Fehlermeldung).

Ich habe den Code mit Haltepunkten debuggt und einige Sammlungen durch private Mitglieder erweitert, während sich der Debugger an einem Haltepunkt befand. Nachdem der Code erneut ausgeführt wurde, ohne die Strukturen zu durchsuchen, verschwand die Fehlermeldung. Es scheint, als hätte NHibernate durch das Durchsuchen privater, faul geladener Sammlungen Dinge geladen, die zu diesem Zeitpunkt nicht geladen werden sollten (weil sie sich in privaten Mitgliedern befanden).

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

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

Ales Potocnik Hahonina
quelle
2

Suchen Sie das Attribut "Cascade" im Ruhezustand und löschen Sie es. Wenn Sie "Cascade" verfügbar machen, werden andere Vorgänge (Speichern, Aktualisieren und Löschen) für andere Entitäten aufgerufen, die eine Beziehung zu verwandten Klassen haben. Es wird also der gleiche Identitätswert auftreten. Es hat bei mir funktioniert.

Nguyen Vu Quang
quelle
1

Ich hatte diesen Fehler einige Tage später und 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 den ID-Generierungstyp im OrderDetil-Objekt nicht erwähnt. Ohne die ID von Orderdetails zu generieren, wird die ID für jedes OrderDetail-Objekt als 0 beibehalten. das erklärte #jbx. Ja, das ist die beste Antwort. Dieses eine Beispiel, wie es passiert.

Buddhi
quelle
1

Versuchen Sie, den Code Ihrer Abfrage vorher zu platzieren. Das behebt mein Problem. zB ändern Sie dies:

query1 
query2 - get the error 
update

dazu:

query2
query1
update
juldeh
quelle
0

Möglicherweise legen Sie die Kennung des Objekts nicht fest, bevor Sie die Aktualisierungsabfrage aufrufen.

Fawad Khaliq
quelle
3
Wenn er nicht wäre, hätte er dieses Problem nicht. Das Problem ist, dass er zwei Objekte mit derselben Kennung hat.
Aalku
0

Ich bin auf das Problem gestoßen, weil die Generierung des Primärschlüssels falsch ist, wenn ich eine Zeile wie diese einfüge:

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 ändere die ID-Generator-Klasse in Identität

<id name="id" type="int">
    <column name="id" />
    <generator class="identity"  />
 </id>
张云 风
quelle
0

In meinem Fall hat nur flush () nicht funktioniert. Ich musste nach flush () ein clear () verwenden.

public Object merge(final Object detachedInstance)
    {
        this.getHibernateTemplate().flush();
        this.getHibernateTemplate().clear();
        try
        {
            this.getHibernateTemplate().evict(detachedInstance);
        }
}
Shubhranshu
quelle
0

Wenn Sie EntityRepository verwenden, verwenden Sie saveAndFlush anstelle von save

Jayen Chondigara
quelle
0

Wenn in meiner IDE eine Registerkarte für Ausdrücke geöffnet bleibt, die einen Ruhezustand auslöst, wird das Objekt aufgerufen, das diese Ausnahme verursacht. Ich habe versucht, dasselbe Objekt zu löschen. Außerdem hatte ich einen Haltepunkt beim Löschaufruf, der notwendig zu sein scheint, damit dieser Fehler auftritt. Durch einfaches Erstellen einer anderen Registerkarte für Ausdrücke als vordere Registerkarte oder Ändern der Einstellung, damit die Idee nicht an Haltepunkten anhält, wurde dieses Problem behoben.

Aaron Byrnes
quelle
0

Stellen Sie sicher, dass Ihre Entität mit allen zugeordneten Entitäten denselben Generierungstyp hat

Beispiel: UserRole

public class UserRole extends AbstractDomain {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private String longName;

private String shortName;

@Enumerated(EnumType.STRING)
private CommonStatus status;

private String roleCode;

private Long level;

@Column(columnDefinition = "integer default 0")
private Integer subRoleCount;

private String modification;

@ManyToOne(fetch = FetchType.LAZY)
private TypeOfUsers licenseType;

}}

Modul:

public class Modules implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private String longName;

private String shortName;

}}

Hauptentität mit Mapping

public class RoleModules implements Serializable{

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
private UserRole role;

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
private Modules modules;

@Type(type = "yes_no")
private boolean isPrimaryModule;

public boolean getIsPrimaryModule() {
    return isPrimaryModule;
}

}}

Sandeep Patel
quelle
0

Zusätzlich zu allen vorherigen Antworten eine mögliche Lösung für dieses Problem in einem Großprojekt, wenn Sie ein Wertobjekt für Ihre Klassen verwenden und das ID-Attribut nicht in der VO Transformer-Klasse festlegen.

George Michael
quelle
0

Übernehmen Sie einfach die aktuelle Transaktion.

currentSession.getTransaction().commit();

Jetzt können Sie eine weitere Transaktion starten und alles für die Entität tun

Mahdi
quelle