Строка была обновлена или удалена другой транзакцией (или неверное сопоставление несохраненных значений)

у меня есть проект java, который работает на веб-сервере. Я всегда попадаю в это исключение.

Я прочитал некоторые документы и обнаружил, что пессимистическая блокировка (или оптимистическая, но я читал, что пессимистическая лучше) - лучший способ предотвратить это исключение.

но я не мог найти четкого примера, который объясняет, как его использовать.

мой метод такой:

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

в то время как:

  • Email является классом hibernate (это будет таблица в базе данных)
  • getEmailById(String id) - это функция, которая возвращает email (этот метод не аннотируется @Transctional)
  • updateEmail(email): это метод, который обновляет электронную почту.

Примечание: я использую hibernate для сохранения, обновления и так далее (пример:session.getcurrentSession.save(email))

исключение:

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$eb7303.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.access1(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 

13 ответов


пессимистическая блокировка обычно не рекомендуется, и это очень дорого с точки зрения производительности на стороне базы данных. Проблема, которую вы упомянули (часть кода), несколько вещей не ясны, такие как:

  • если доступ к вашему коду осуществляется несколькими потоками одновременно.
  • как вы создаете session объект (не уверен, что вы используете Spring)?

спящий режим Session объекты не являются потокобезопасными. Так что если есть если несколько потоков обращаются к одному сеансу и пытаются обновить один и тот же объект базы данных, ваш код потенциально может оказаться в такой ситуации ошибки.

Итак, что происходит здесь, так это то, что более одного потока пытается обновить один и тот же объект, один поток преуспевает, и когда следующий поток идет для фиксации данных, он видит, что его уже модифицировали и заканчивают бросать StaleObjectStateException.

редактировать:

есть способ использовать пессимистический Блокировка в спящем режиме. Проверьте этой ссылке. Но, похоже, с этим механизмом есть некоторые проблемы. Я наткнулся на сообщение об ошибке в спящем режиме (HHH-5275),. Сценарий, упомянутый в ошибке, выглядит следующим образом:

два потока читают одну и ту же запись базы данных; один из этих потоков следует использовать пессимистическую блокировку, тем самым блокируя другой поток. Но оба потока могут читать запись базы данных, вызывающую тест неудача.

Это очень близко к тому, с чем вы столкнулись. Пожалуйста, попробуйте это, если это не работает, единственный способ, который я могу придумать, это использовать собственные SQL-запросы где вы можете достичь пессимистическая блокировка в postgres


не похоже, что вы на самом деле используете электронное письмо, которое вы получаете из базы данных, но более старую копию, которую вы получаете в качестве параметра. Все, что используется для управления версиями в строке, изменилось между моментом получения предыдущей версии и моментом обновления.

вы, вероятно, хотите, чтобы ваш код, чтобы выглядеть так:

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

Я знаю, что это старый вопрос, но некоторые из нас все еще ударяют его и смотрят на небо, блуждающее как. Вот один вид вопроса, с которым я столкнулся,

у нас есть администратор очередей, который опрашивает данные и дает обработчикам для обработки. Чтобы избежать повторения одних и тех же событий, служба очереди блокирует запись в базе данных в заблокированном состоянии.

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

этот метод не был транзакционным, но dao.getLockedEntity() был транзакционным с 'REQUIRED'.

все хорошо и на дорога, после нескольких месяцев в производстве, провалилась с оптимистичным исключением блокировки,

после множества отладки и проверки деталей мы могли бы узнать, что кто-то изменил код, как это,

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

таким образом, запись была поставлена в очередь еще до транзакции в dao.getLockedEntity () get committed (он использует ту же транзакцию метода poll), и объект был изменен обработчиками (разными потоками) к моменту транзакции метода poll () вам поручено.

мы исправили проблему, и теперь она выглядит хорошо.

Я думал поделиться им, потому что оптимистическое исключение блокировки может быть запутанным и трудно отлаживать. Кто-то может извлечь пользу из моего опыта.

С уважением Lyju


это исключение, вероятно, вызвано оптимистической блокировкой (или ошибкой в вашем коде). Вы, вероятно, используете его, не зная. И ваш псевдо-код (который должен быть заменен реальным кодом, чтобы иметь возможность диагностировать проблему) ошибочен. Hibernate автоматически сохраняет все изменения, внесенные в прикрепленные объекты. Вы никогда не должны вызывать update, merge или saveOrUpdate для вложенного объекта. Просто сделай!--2-->

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

нет необходимости вызывать update. Hibernate очистит изменения автоматически перед совершением транзакции.


У меня была эта проблема в моем проекте.

после того, как я реализовал оптимистическую блокировку, я получил то же исключение. Моя ошибка заключалась в том, что я не удалил сеттера поля, который стал @Version. Поскольку сеттер вызывался в пространстве java, значение поля больше не соответствовало значению, генерируемому БД. Таким образом, в основном поля версии больше не совпадали. В этот момент любое изменение сущности привело к:

org.зимовать.StaleObjectStateException: строка была обновлена или удалена другая транзакция (или неверное сопоставление несохраненных значений)

Я использую H2 в памяти DB и Hibernate.


проверьте, существует ли объект или нет в БД, если он существует, получите объект и обновите его:

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

если она не выше, если состоянии... найдите объект с Id в БД, выполните операцию, которая вам нужна,в этом случае точно отразятся изменения.

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

у меня была такая же проблема в другом контексте моего проекта, и есть разные сценарии, такие как

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

В первом случае

когда я выпускаю сервер cal, перед сохранением этого объекта их один вызов из js и пытается сохранить и другое место, я получил, как, JS вызов идет два, три раза (я вещь, которая вызывает привязку вещь вызывает проблему)

Я решена путем

e.preventDefault()

второй случай,

object.lock()

у меня была та же проблема, и в моем случае проблема отсутствовала и/или неправильная реализация equals на некоторых типах полей в объекте сущности. Во время фиксации Hibernate проверяет все объекты, загруженные в сеанс, чтобы проверить, являются ли они грязными. Если какой - либо из объектов загрязнен, hibernate пытается сохранить их-независимо от того, что фактический объект, который запрашивается операция сохранения, не связан с другими объектами.

грязь сущности выполняется путем сравнения каждое свойство данного объекта (с их методами equals) или UserType.equals Если свойство имеет связанный org.Hibernate.UserType.

еще одна вещь, которая меня удивила, была в моей транзакции (используя аннотацию Spring @Transactional), Я имел дело с одной сущностью. Hibernate жаловался на какой-то случайный объект, который не связан с этим объектом, который сохраняется. Я понял, что есть внешняя транзакция, которую мы создаем на уровне контроллера REST, поэтому объем сеанса слишком велик и, следовательно, все объекты, когда-либо загруженные в рамках обработки запросов, проверяются на грязность.

надеюсь, что это поможет кому-то, когда-нибудь.

Спасибо Тряпья


эта ошибка произошла для меня, когда я пытался обновить одну и ту же строку из 2 разных сеансов. Я обновил поле в одном браузере, в то время как второй был открыт и уже сохранил исходный объект в своем сеансе. Когда я попытался обновить этот второй" устаревший " сеанс, я получаю устаревшую ошибку объекта. Чтобы исправить это, я переназначаю свой объект для обновления из базы данных, прежде чем я установлю значение для обновления, а затем сохраню его как обычно.


на всякий случай кто-то проверил эту тему и имел ту же проблему, что и моя...

строка была обновлена или удалена другой транзакцией (или неверное отображение несохраненного значения)

Я использую NHibernate, я получаю ту же ошибку при создании объекта...

Я передавал ключ вручную, а также указал генератор GUID в сопоставлении, поэтому hibernate генерирует ту же самую точную ошибку для меня, поэтому однажды я удалил GUID и покинул поле пусто, все прошло отлично.

этот ответ может не помочь вам, но поможет кому-то вроде меня, кто просто ваш поток из-за той же ошибки


у меня была такая же проблема в моем проекте grails. Ошибка заключалась в том, что я перезаписываю метод getter поля коллекции. Это всегда возвращало новую версию коллекции в другом потоке.

class Entity {
    List collection

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

решением было переименовать метод getter:

class Entity {
    List collection

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

для того, чтобы предотвратить StaleObjectStateException в своем hbm файл напишите ниже код:

<timestamp name="lstUpdTstamp" column="LST_UPD_TSTAMP" source="db"/>

У меня была эта проблема в одном из моих приложений, теперь я знаю, что это старый поток, но вот мое решение; я понял, посмотрев на данные внутри отладчика, что JVM фактически не загрузил его должным образом, когда Hibernate пытался обновить базу данных (что на самом деле делается в другом потоке), поэтому я добавил ключевое слово "volatile" в каждое поле сущностей. У него есть некоторые проблемы с производительностью, чтобы сделать это, но вместо того, чтобы бросать тяжелые предметы...