Подтвердить что ты не робот

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

У меня есть проект 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): это метод, который обновляет электронную почту.

Примечание. Я использую спящий режим для сохранения, обновления и т.д. (пример: 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 
4b9b3361

Ответ 1

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

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

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

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

ИЗМЕНИТЬ

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

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

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

Ответ 2

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

Вероятно, вам понравится ваш код:

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

Ответ 3

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

У нас есть менеджер очередей, который опрашивает данные и передает их обработчикам для обработки. Чтобы избежать повторного получения тех же событий, менеджер очередей блокирует запись в базе данных с состоянием 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.

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

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

С уважением Лю

Ответ 4

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

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

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

Ответ 5

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

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

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

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

Ответ 6

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

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

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

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

Ответ 7

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

 - 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 call идет два, три раза (я вещь, которая вызывает привязку, вызывает проблему )

Я решил

e.preventDefault()

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

object.lock()

Ответ 8

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

Ответ 9

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

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

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

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

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

Ответ 10

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

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

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

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

Спасибо Rags

Ответ 11

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

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

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

Ответ 12

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

class Entity {
    List collection

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

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

class Entity {
    List collection

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

Ответ 13

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

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

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

Ответ 14

Не устанавливать Id на объект вы сохраняете как Id будет генерироваться автоматически

Ответ 15

Чтобы предотвратить StaleObjectStateException, в файле hbm напишите ниже код:

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

Ответ 16

Я тоже сталкивался с этой проблемой. Сначала проверьте импорт, когда вы используете сеанс, транзакция должна быть org.hibernate и удалите аннотацию @Transactinal. и что наиболее важно в классе Entity, если вы использовали @GeneratedValue (стратегии = GenerationType.AUTO) или что-либо другое, тогда во время создания объекта модели/создания объекта сущности не следует создавать идентификатор. окончательный вывод - если вы хотите передать идентификатор, то есть PK, затем удалите @GeneratedValue из класса сущностей.

Ответ 17

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