it-swarm.com.de

Zeile wurde von einer anderen Transaktion aktualisiert oder gelöscht (oder die Zuordnung nicht gespeicherter Werte war falsch)

Ich habe ein Java-Projekt, das auf einem Webserver läuft. Ich habe immer diese Ausnahme getroffen.

Ich habe einige Dokumentationen gelesen und festgestellt, dass pessimistisches Sperren (oder optimistisch, aber ich habe gelesen, dass pessimistisch besser ist) der beste Weg ist, diese Ausnahme zu verhindern.

Ich konnte jedoch kein klares Beispiel finden, das erklärt, wie man es benutzt.

Meine Methode ist wie:

@Transactional
Public void test(Email email, String Subject){
   getEmailById(String id);
   email.setSubject(Subject);
   updateEmail(email);
}

während:

  • Email ist eine Hibernate-Klasse (es wird eine Tabelle in der Datenbank sein)
  • getEmailById(String id) ist eine Funktion, die eine email zurückgibt (diese Methode wird nicht mit @Transctional kommentiert)
  • updateEmail(email): ist eine Methode, mit der die E-Mail aktualisiert wird.

Hinweis: Ich verwende den Ruhezustand zum Speichern, Aktualisieren usw. (Beispiel: session.getcurrentSession.save(email))

Die Ausnahme:

ERROR 2011-12-21 15:29:24,910 Could not synchronize database state with session [myScheduler-1]
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [email#21]
    at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.Java:1792)
    at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.Java:2435)
    at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.Java:2335)
    at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.Java:2635)
    at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.Java:115)
    at org.hibernate.engine.ActionQueue.execute(ActionQueue.Java:279)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.Java:263)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.Java:168)
    at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.Java:321)
    at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.Java:50)
    at org.hibernate.impl.SessionImpl.flush(SessionImpl.Java:1027)
    at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.Java:365)
    at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.Java:137)
    at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.Java:656)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.Java:754)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.Java:723)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.Java:393)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.Java:120)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.Java:202)
    at $Proxy130.generateEmail(Unknown Source)
    at com.admtel.appserver.tasks.EmailSender.run(EmailNotificationSender.Java:33)
    at com.admtel.appserver.tasks.EmailSender$$FastClassByCGLIB$$ea0d4fc2.invoke(<generated>)
    at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.Java:149)
    at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.Java:688)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:150)
    at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.Java:55)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:161)
    at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.Java:50)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:161)
    at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.Java:50)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:161)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.Java:89)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:172)
    at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.Java:621)
    at com.admtel.appserver.tasks.EmailNotificationSender$$EnhancerByCGLIB$$33eb7303.run(<generated>)
    at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:39)
    at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:25)
    at Java.lang.reflect.Method.invoke(Method.Java:597)
    at org.springframework.util.MethodInvoker.invoke(MethodInvoker.Java:273)
    at org.springframework.scheduling.support.MethodInvokingRunnable.run(MethodInvokingRunnable.Java:65)
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.Java:51)
    at Java.util.concurrent.Executors$RunnableAdapter.call(Executors.Java:441)
    at Java.util.concurrent.FutureTask$Sync.innerRunAndReset(FutureTask.Java:317)
    at Java.util.concurrent.FutureTask.runAndReset(FutureTask.Java:150)
    at Java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$101(ScheduledThreadPoolExecutor.Java:98)
    at Java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.runPeriodic(ScheduledThreadPoolExecutor.Java:180)
    at Java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.Java:204)
    at Java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.Java:886)
    at Java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.Java:908)
    at Java.lang.Thread.run(Thread.Java:680)
ERROR 2011-12-21 15:29:24,915 [ exception thrown < EmailNotificationSender.run() > exception message Object of class [Email] with identifier [211]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [Email#21] with params ] [myScheduler-1]
org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException: Object of class [Email] with identifier [21]: optimistic locking failed; nested exception is 
49

Pessimistisches Sperren wird im Allgemeinen nicht empfohlen und ist hinsichtlich der Leistung auf Datenbankseite sehr kostspielig. Das Problem, das Sie erwähnt haben (der Codeteil), einige Dinge sind nicht klar, wie zum Beispiel:

  • Wenn auf den Code von mehreren Threads gleichzeitig zugegriffen wird.
  • Wie erstellen Sie ein session-Objekt (nicht sicher, ob Sie Spring verwenden)?

Ruhezustand Sitzungsobjekte sind NICHT threadsicher . Wenn also mehrere Threads auf dieselbe Sitzung zugreifen und versuchen, dieselbe Datenbankentität zu aktualisieren, kann Ihr Code möglicherweise zu einer Fehlersituation wie dieser führen. 

Hier passiert also, dass mehr als ein Thread versucht, dieselbe Entität zu aktualisieren. Ein Thread ist erfolgreich. Wenn der nächste Thread die Daten festschreibt, sieht er, dass seine Daten bereits geändert wurden und StaleObjectStateException wirft.

EDIT:

Es gibt eine Möglichkeit, die pessimistische Sperre im Ruhezustand zu verwenden. Check out diesen Link . Es scheint jedoch ein Problem mit diesem Mechanismus zu geben. Ich stieß jedoch auf einen Fehler im Hibernate ( HHH-5275 ). Das in dem Fehler erwähnte Szenario sieht wie folgt aus:

Zwei Threads lesen denselben Datenbanksatz. einer dieser Threads sollte pessimistische Sperren verwenden, um den anderen Thread zu blockieren. Aber Beide Threads können den Datenbankdatensatz lesen, wodurch der Test fehlschlägt.

Dies ist sehr nahe an dem, was Sie sehen. Bitte versuchen Sie dies, wenn dies nicht funktioniert. Die einzige Möglichkeit, die ich mir vorstellen kann, ist die Verwendung von Native SQL-Abfragen , bei denen Sie pessimistisches Sperren in postgres database mit SELECT FOR UPDATE-Abfrage erreichen können.

44
Santosh

Es scheint nicht, dass Sie tatsächlich die E-Mail verwenden, die Sie aus der Datenbank abrufen, sondern eine ältere Kopie, die Sie als Parameter erhalten. Was für die Versionskontrolle in der Zeile verwendet wird, hat sich zwischen dem Abrufen der vorherigen Version und dem Update geändert.

Sie möchten wahrscheinlich, dass Ihr Code mehr aussieht:

@Transactional
Public void test(String id, String subject){
   Email email = getEmailById(id);
   email.setSubject(subject);
   updateEmail(email);
}
13
tvanfosson

Ich weiß, dass dies eine alte Frage ist, aber einige von uns treffen immer noch darauf und schauen, wie der Himmel wandert. Hier ist eine Art Problem, mit dem ich konfrontiert war,

Wir haben einen Warteschlangenmanager, der Daten abfragt und Handler zur Verarbeitung übergibt. Um zu vermeiden, dass dieselben Ereignisse erneut abgerufen werden, sperrt der Warteschlangenmanager den Datensatz in der Datenbank mit dem Status LOCKED.

   void poll() {
        record = dao.getLockedEntity();
        queue(record);
     }

diese Methode war nicht transaktional, aber dao.getLockedEntity() war transaktional mit 'REQUIRED'.

Alles gut und unterwegs, nach ein paar Monaten in der Produktion, scheiterte es mit optimistischen Sperren, 

Nach vielem Debuggen und Einchecken von Details konnten wir feststellen, dass jemand den Code so geändert hat.

@Transactional(propagation=Propagation.REQUIRED, readOnly=false)
void poll() {
        record = dao.getLockedEntity();
        queue(record);              
     }

Der Datensatz wurde also bereits in die Warteschlange gestellt, bevor die Transaktion in dao.getLockedEntity () festgeschrieben wurde (er verwendet dieselbe Transaktionsmethode), und das Objekt wurde von den Handlern (verschiedenen Threads) darunter geändert, wenn die Transaktion der poll () -Methode abgerufen wird begangen.

Wir haben das Problem behoben und es sieht jetzt gut aus.

Ich dachte daran, es weiterzugeben, da die Ausnahme für eine optimistische Sperre verwirrend und schwer zu debuggen ist. Jemand könnte von meiner Erfahrung profitieren.

Grüße Lyju

7
Lyju I Edwinson

Diese Ausnahme wird wahrscheinlich durch ein optimistisches Sperren (oder durch einen Fehler in Ihrem Code) verursacht. Sie benutzen es wahrscheinlich ohne es zu wissen. Ihr Pseudo-Code (der durch echten Code ersetzt werden sollte, um das Problem diagnostizieren zu können) ist falsch. Im Ruhezustand werden alle an verbundenen Objekten vorgenommenen Änderungen automatisch gespeichert. Sie sollten niemals Update, Merge oder SaveOrUpdate für eine verbundene Entität aufrufen. Mach einfach

Email email = session.get(emailId);
email.setSubject(subject);

Update muss nicht aufgerufen werden. Im Ruhezustand werden die Änderungen automatisch gelöscht, bevor die Transaktion abgeschlossen wird.

4
JB Nizet

Ich hatte dieses Problem bei meinem Projekt. 

Nachdem ich das optimistische Sperren implementiert hatte, bekam ich die gleiche Ausnahme. Mein Fehler war, dass ich den Setter des Feldes, das zum @Version wurde, nicht entfernt hat. Da der Setter im Java-Bereich aufgerufen wurde, stimmte der Wert des Feldes nicht mehr mit dem von der DB generierten überein. Im Grunde stimmten die Versionsfelder nicht mehr überein. Zu diesem Zeitpunkt führten Änderungen an der Entität zu folgendem Ergebnis:

org.hibernate.StaleObjectStateException: Zeile wurde aktualisiert oder gelöscht von eine andere Transaktion (oder die Zuordnung nicht gespeicherter Werte war falsch)

Ich verwende H2 in der Speicher-DB und im Ruhezustand.

3
MF Wolf

prüfen Sie, ob das Objekt in der DB vorhanden ist oder nicht. Wenn es existiert, holen Sie das Objekt ab und aktualisieren Sie es:

if (getEntityManager().contains(instance)) {
    getEntityManager().refresh(instance);
    return instance;
}

wenn die obige if-Bedingung nicht erfüllt wird ... das Objekt mit der ID in der DB finden, müssen Sie die Operation ausführen, die Sie benötigen. In diesem Fall werden genau die Änderungen berücksichtigt.

if (....) {
    } else if (null != identity) {
        E dbInstance = (E) getEntityManager().find(instance.getClass(), identity);
        return dbInstance;
    }
2
pavan

Ich hatte das gleiche Problem in einem anderen Kontext meines Projekts erlebt und es gibt verschiedene Szenarien wie

 - object is accessed from various source like (server side and client)
 - without any interval accessing the same object from a different place

Im ersten Fall

Wenn ich einen Server cal ausstelle, bevor ich das Objekt speichere, rufe ich einen Aufruf von js ab und versuche zu speichern und an einem anderen Ort, ich habe den Eindruck, js call geht zwei, dreimal (ich glaube, dass das Call-Binding-Problem das Problem verursacht).

Ich löste mich von 

e.preventDefault()

Der zweite Fall 

object.lock()

Ich bin auch auf diesen Fehler gestoßen, als ich versucht habe, eine vorhandene Zeile zu aktualisieren, nachdem ich eine neue erstellt hatte, und habe mir über lange Zeit den Kopf zerkratzt, die Transaktions- und Versionslogik durchgearbeitet, bis mir klar wurde, dass ich habe den falschen Typ für einen meiner Zeilen verwendet) Primärschlüsselspalten.

Ich habe LocalDate verwendet, wenn ich LocalDateTime hätte verwenden sollen. Ich glaube, dies hat dazu geführt, dass der Ruhezustand keine Entitäten unterscheiden kann, was zu diesem Fehler geführt hat.

Nachdem der Schlüssel in eine LocalDateTime geändert wurde, wurde der Fehler behoben. Auch das Aktualisieren einzelner Zeilen begann zu funktionieren - zuvor konnte keine Zeile zum Aktualisieren gefunden werden, und das Testen dieses separaten Problems führte mich zu meinen Schlussfolgerungen hinsichtlich der Primärschlüsselzuordnung.

1
Josh Manderson

Nur für den Fall, dass jemand diesen Thread überprüft hat und das gleiche Problem hatte wie mein ...

Zeile wurde von einer anderen Transaktion aktualisiert oder gelöscht (oder die Zuordnung nicht gespeicherter Werte war falsch)

Ich verwende NHibernate. Ich erhalte dieselbe Fehlermeldung, während ich ein Objekt erstellt ...

Ich gab den Schlüssel manuell weiter und gab auch einen GUID - Generator in der Zuordnung an....................... Wenn ich die GUID entfernt und das Feld leer gelassen habe, ist alles gelaufen Alles gut.

diese Antwort kann dir nicht helfen, aber sie hilft jemandem wie mir, der nur aufgrund des gleichen Fehlers in deinem Thread ist

1
deadManN

Ich hatte das gleiche Problem, und in meinem Fall fehlte das Problem und/oder eine falsche Implementierung bei einigen Arten von Feldern im Entitätsobjekt. Zur Übergabe prüft der Ruhezustand ALLE in der Sitzung geladenen Entitäten, um zu prüfen, ob sie fehlerhaft sind. Wenn eine der Entitäten fehlerhaft ist, versucht der Ruhezustand, sie beizubehalten - unabhängig davon, dass das eigentliche Objekt, für das ein Sicherungsvorgang angefordert wurde, nicht mit den anderen Entitäten zusammenhängt. 

Die Unreinheit der Entität wird durch den Vergleich jeder Eigenschaft eines gegebenen Objekts (mit ihren equals-Methoden) oder UserType.equals ausgeführt, wenn der Eigenschaft org.Hibernate.UserType eine Eigenschaft zugeordnet ist.

Eine andere Sache, die mich überraschte, war, dass ich mich in meiner Transaktion (mit Spring-Annotation @Transactional) mit einer einzigen Entität befasste. Hibernate beklagte sich über eine zufällige Entität, die nicht mit der geretteten Entität zusammenhängt. Mir wurde klar, dass es eine äußerste Transaktion gibt, die wir auf REST - Controller-Ebene erstellen. Daher ist der Umfang der Sitzung zu groß und daher werden alle Objekte, die jemals im Rahmen der Anforderungsverarbeitung geladen wurden, auf Verschmutzung geprüft.

Hoffe, das hilft jemandem eines Tages.

Danke Lumpen

1
user2639828

Dieser Fehler ist bei mir aufgetreten, als ich versuchte, dieselbe Zeile aus zwei verschiedenen Sitzungen zu aktualisieren. Ich habe ein Feld in einem Browser aktualisiert, während ein zweiter geöffnet war und das ursprüngliche Objekt bereits in seiner Sitzung gespeichert hatte. Wenn ich versuchte, von dieser zweiten "veralteten" Sitzung zu aktualisieren, erhalte ich den veralteten Objektfehler. Um dies zu korrigieren, rufe ich mein zu aktualisierendes Objekt erneut aus der Datenbank ab, bevor ich den zu aktualisierenden Wert einstelle, und speichere es als normal.

1
Roger

Ich habe auch eine solche Ausnahme erhalten, aber das Problem lag in meiner Objekt-ID. Ich benutze UUID und es gibt einige Probleme in der Art, wie Spring mit ihnen arbeitet. Also habe ich diese Zeile zu meiner Entitätskennung hinzugefügt und sie hat funktioniert:

@Column(columnDefinition = "BINARY(16)")

hier Hier finden Sie ein bisschen mehr Informationen.

0
Kamo Spertsian

Ich hatte das gleiche Problem in meinem Grails-Projekt. Der Fehler war, dass ich die Getter-Methode eines Collection-Feldes überschreibe. Dadurch wurde immer eine neue Version der Sammlung in einem anderen Thread zurückgegeben.

class Entity {
    List collection

    List getCollection() {
        return collection.unique()
    }
}

Die Lösung bestand darin, die Getter-Methode umzubenennen:

class Entity {
    List collection

    List getUniqueCollection() {
        return collection.unique()
    }
}
0
moskauerst

Setzen Sie kein Id auf das Objekt, das Sie speichern, da das Id automatisch generiert wird

0
w0ns88