2010-07-01 6 views
13

Ich bekomme diese Ausnahme in einem Controller einer Webanwendung basierend auf Feder-Framework mit Hibernate. Ich habe viele Wege versucht, dem entgegenzuwirken, konnte es aber nicht lösen.StaleObjectStateException Zeile wurde aktualisiert oder gelöscht von

In der Controller-Methode, handleRequestInternal, gibt es Aufrufe an die Datenbank hauptsächlich für 'lesen', es sei denn, es ist eine Übermittlungsaktion. Ich habe Spring Session verwendet, aber umgezogen zu getHibernateTemplate() und das Problem bleibt immer noch.

im Grunde, löst dieser zweite Aufruf der Datenbank diese Ausnahme aus. Das heißt:

1) getEquipmentsByNumber(number) {Zuerst wird ein Gerät aus der Datenbank abgerufen, basierend auf der 'Nummer', die eine Liste von Eigenschaften enthält und jede Eigenschaft eine Werteliste enthält. I Schleife durch diese Werte (primitive Objekte Strings) auf Variablen lesen in)

2) getMaterialById(id) {holt Materialien auf Basis von id}

verstehe ich, dass der zweite Anruf, höchstwahrscheinlich, macht die Sitzung "flush", aber ich lese nur Objekte, und warum löst dann der zweite Aufruf die veraltete Objektstatusausnahme in der Equipment-Eigenschaft aus, wenn nichts geändert wird?

Ich kann den Cache nach dem Aufruf nicht löschen, da er LazyExceptions für Objekte verursacht, die ich an die Ansicht übergebe.

Ich habe gelesen: https://forums.hibernate.org/viewtopic.php?f=1&t=996355&start=0 aber konnte das Problem auf der Grundlage der vorgeschlagenen Vorschläge nicht lösen.

Wie kann ich dieses Problem lösen? Irgendwelche Ideen und Gedanken werden geschätzt.

UPDATE: Was ich nur getestet, dass in der Funktion getEquipmentsByNumber() nach den Variablen aus der Liste der Eigenschaften zu lesen, ich dies tun: getHibernateTemplate().flush(); und jetzt die Ausnahme ist auf dieser Linie eher dann der Anruf Material zu holen (das ist getMaterialById(id)).

UPDATE: Vor dem expliziten Aufruf von Flush entferne ich das Objekt aus dem Sitzungscache, sodass kein veraltetes Objekt im Cache verbleibt.

getHibernateTemplate().evict(equipment); 
getHibernateTemplate().flush(); 

OK, so jetzt ist das Problem zum nächsten Abruf von DB nach diesem Schritt verschoben. Ich nehme an, ich muss die Methoden als synchronisiert markieren und die Objekte entfernen, sobald ich ihre Inhalte gelesen habe! es klingt nicht sehr gut.

UPDATE: Die handleRequestInternal Methode "synchronisiert". Der Fehler ist verschwunden. Natürlich nicht die beste Lösung, aber was zu tun ist! Versucht in handleRequestInternal, um die aktuelle Sitzung zu schließen und eine neue zu öffnen. Es würde jedoch dazu führen, dass andere Teile der App nicht ordnungsgemäß funktionieren. Versucht, ThreadLocal zu verwenden, das auch nicht funktionierte.

+1

Wenn Sie den Code für die Methode posten könnten, die die Ausnahme wirft, werde ich es weiter betrachten. Klingt irgendwie fischig – walnutmon

Antwort

0

Dieses Problem war etwas, das ich erlebt hatte und ziemlich frustrierend war, obwohl es etwas seltsames in Ihren DAO/Hibernate-Anrufen geben muss, denn wenn Sie nach ID suchen, gibt es keinen Grund dazu einen veralteten Zustand erhalten, da dies nur eine einfache Suche nach einem Objekt ist.

Erstens, stellen Sie sicher, dass alle Methoden kommentierten mit @Transaction(required=true) // you'll have to look up the exact syntax

jedoch diese Ausnahme in der Regel wird ausgelöst, wenn Sie versuchen, auf ein Objekt Änderungen, die von der Sitzung gelöst wurde es aus abgerufen wurde. Die Lösung dafür ist oft nicht einfach und würde mehr Code erfordern, damit wir genau sehen können, was vor sich geht. Mein allgemeiner Vorschlag wäre, eine @Service zu erstellen, die diese Art von Dingen innerhalb einer einzigen Transaktion ausführt.

+0

Danke für den Vorschlag. Was ich gerade getestet habe ist, dass in der Funktion getEquipmentsByNumber nach dem Lesen der Variablen aus der Liste der Eigenschaften, mache ich dies: getHibernateTemplate(). Flush(); und jetzt ist die Ausnahme in dieser Zeile eher als der Aufruf, Material abzurufen (das ist getMaterialById (id)) Ich verwende keine Anmerkungen, da ich mich nicht sehr gut damit auskenne. – Saky

2

Hier sind 3 Möglichkeiten (wie ich nicht genau weiß, welche Art von Ruhezustand Sitzung Sie verwenden). Fügen Sie einen nach dem anderen hinzu und testen Sie:

Verwenden Sie das bidirektionale Mapping mit inverse=true zwischen dem übergeordneten Objekt und dem untergeordneten Objekt, damit die Änderung im übergeordneten oder untergeordneten Element ordnungsgemäß zum anderen Ende der Beziehung weitergeleitet wird.

hinzufügen Unterstützung für Optimistisch Sperren TimeStamp oder Version Spalte

Verwenden Join-Abfrage das ganze Objekt Diagramm zu holen [Eltern + Kinder] zusammen insgesamt den zweiten Anruf zu vermeiden.

Schließlich, wenn und nur wenn nichts funktioniert: laden die Eltern wieder durch Id (Sie haben das schon) und geänderten Daten bevölkern dann aktualisieren.

Das Leben wird gut sein! :)

+0

Ich denke, der Vorschlag der Verwendung von inverse ist wahrscheinlich im Kontext der Weitergabe von Änderungen von Eltern zu Kind verwirrend. Dazu ist das zu konfigurierende Verhalten "Kaskade". Invers als Konzept ist orthogonal zur Kaskadierung – Shailendra

3

Ich habe auch mit dieser Ausnahme gekämpft, aber als es immer wieder auftrat, auch wenn ich eine Sperre auf das Objekt setzte (und in einer Testumgebung, wo ich wusste, dass ich der einzige Prozess war, der das Objekt berührte), Ich habe beschlossen, die Klammer in der Stapelrückverfolgung gebührend zu berücksichtigen.

org.hibernate.StaleObjectStateException: Reihe wurde durch eine andere Transaktion (oder nicht gespeicherten Wertzuordnung nicht korrekt war) aktualisiert oder gelöscht: [com.rc.model.mexp.MerchantAccount # 59132]

In unserem Fall stellte sich heraus, dass die Zuordnung falsch war; Wir hatten type="text" in der Zuordnung für ein Feld, das ein Mediumtext-Typ in der Datenbank war, und es scheint, dass Hibernate das wirklich hasst, zumindest unter bestimmten Umständen. Wir haben die Typspezifikation vollständig aus dem Mapping für dieses Feld entfernt und das Problem wurde behoben.

Jetzt ist das Seltsame, dass wir in unserer Produktionsumgebung, mit der vermeintlich problematischen Zuordnung, diese Ausnahme NICHT bekommen. Hat jemand eine Idee, warum das so sein könnte? Wir verwenden die gleiche Version von MySQL - "5.0.22-log" (ich weiß nicht, was "-log" bedeutet) - in dev und production envs.

5

Sie sind falsch mit Hibernate in irgendeiner Weise, die es Ihnen oder Objekte aus der Datenbank zu löschen Aktualisierung sind zu denken, verursacht.

Deshalb ruft Aufruf flush() eine Ausnahme auf.

Eine Möglichkeit: Sie "teilen" Sitzung oder Entitäten inkorrekt über Mitgliedsfeld (e) Ihres Servlets oder Controllers. Dies ist der Hauptgrund, dass 'synchronisierte' Ihre Fehlersymptome ändern würde. Kurze Lösung: Tun Sie das niemals. Sitzungen und Entitäten sollten nicht & funktionieren nicht auf diese Weise - jeder Request sollte unabhängig verarbeitet werden.

Eine andere Möglichkeit: unsaved-value ist standardmäßig 0 für "int" PK Felder. Sie können diese möglicherweise als "Ganzzahl" eingeben, wenn Sie wirklich 0 als gültigen PK-Wert verwenden möchten.

Dritter Vorschlag: Verwendung Hibernate Session explizit, lernen einfache richtigen Code zu schreiben, die, dann laden Sie die Java-Quelle für Hibernate/Frühjahr Bibliotheken arbeitet, so dass Sie & verstehen, was diese Bibliotheken lesen können, sind tatsächlich für Sie tun.

Verwandte Themen