Строка была обновлена или удалена другой транзакцией (или неверное сопоставление несохраненных значений)
у меня есть проект 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" в каждое поле сущностей. У него есть некоторые проблемы с производительностью, чтобы сделать это, но вместо того, чтобы бросать тяжелые предметы...