it-swarm.com.ru

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

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

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

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

Мой метод похож на:

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

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

  • Email - это спящий класс (это будет таблица в базе данных)
  • 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$$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
Georgian Citizen

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

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

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

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

ПРАВКА:

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

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

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

44
Santosh

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

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

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

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

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

   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 () была зафиксирована (она использует ту же транзакцию метода poll), а объект был изменен обработчиками (различными потоками) к моменту получения транзакции метода poll () comitted.

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

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

С уважением Lyju

7
Lyju I Edwinson

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

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

Не нужно называть обновление. Hibernate автоматически сбросит изменения перед совершением транзакции.

4
JB Nizet

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

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

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

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

3
MF Wolf

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

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

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

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

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

 - 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()
1
Suganthan Madhavan Pillai

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

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

После изменения ключа на LocalDateTime ошибка исчезла. Кроме того, начало работать также обновление отдельных строк - раньше не было возможности найти строку для обновления, и тестирование этой отдельной проблемы фактически привело меня к моим выводам относительно сопоставления первичного ключа.

1
Josh Manderson

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

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

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

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

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

1
deadManN

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

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

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

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

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

1
user2639828

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

1
Roger

Я также получил такое исключение, но проблема была в моем идентификаторе сущности. Я использую UUID и есть некоторые проблемы в том, как Spring работает с ними. Поэтому я просто добавил эту строку в свой идентификатор объекта, и он начал работать:

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

Здесь вы можете найти немного больше информации.

0
Kamo Spertsian

У меня была такая же проблема в моем проекте Grails. Ошибка была в том, что я переписал метод получения поля коллекции. При этом всегда возвращалась новая версия коллекции в другом потоке.

class Entity {
    List collection

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

Решением было переименовать метод получения:

class Entity {
    List collection

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