首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >行被另一个事务更新或删除(或未保存的值映射不正确)

行被另一个事务更新或删除(或未保存的值映射不正确)
EN

Stack Overflow用户
提问于 2011-12-27 14:25:15
回答 23查看 307.3K关注 0票数 92

我有一个运行在that服务器上的java项目。我总是碰到这个例外。

我阅读了一些文档,发现悲观锁定(或者乐观,但我看到悲观更好)是防止这种异常的最佳方法。

但我找不到任何明确的例子来解释如何使用它。

我的方法是:

代码语言:javascript
运行
复制
    @Transactional
    public void test(Email email, String subject) {
        getEmailById(String id);
        email.setSubject(subject);
        updateEmail(email);
    }

同时:

  • Email是一个Hibernate类(它将是数据库中的一个表)
  • getEmailById(String id)是一个返回email的函数(此方法不带@Transactional注释)
  • updateEmail(email):是一种更新电子邮件的方法。

注意:我使用Hibernate保存、更新等等(例如:session.getcurrentSession.save(email))

例外情况:

代码语言:javascript
运行
复制
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 
EN

回答 23

Stack Overflow用户

回答已采纳

发布于 2011-12-27 14:58:53

悲观锁定通常不被推荐,而且它在数据库端的性能方面非常昂贵。您提到的问题(代码部分)有几点不清楚,例如:

  • 如果您的代码同时被多个线程访问。
  • 如何创建session对象(不确定是否使用Spring)?

Hibernate 会话对象不是线程安全的。.因此,如果有多个线程访问同一个会话并试图更新同一个数据库实体,那么您的代码可能会出现这样的错误情况。

因此,这里发生的情况是,多个线程尝试更新同一个实体,一个线程成功,当下一个线程提交数据时,它会看到它已经被修改,并最终抛出StaleObjectStateException

编辑

在Hibernate中有一种使用悲观锁定的方法。看看此链接。但这一机制似乎存在一些问题。然而,我偶然发现hibernate (HHH-5275)中有一个bug。bug中提到的场景如下:

两个线程正在读取相同的数据库记录;其中一个线程应该使用悲观锁定,从而阻塞另一个线程。但是两个线程都可以读取数据库记录,导致测试失败。

这与你所面对的非常接近。如果这不起作用,请尝试一下,我唯一能想到的方法是使用本机SQL查询,在这里您可以使用SELECT FOR UPDATE查询来实现postgres中的悲观锁定数据库。

票数 69
EN

Stack Overflow用户

发布于 2013-09-27 01:41:22

我们有一个队列管理器,它轮询数据并将其交给处理程序处理。为了避免再次捕获相同的事件,队列管理器以LOCKED状态锁定数据库中的记录。

代码语言:javascript
运行
复制
    void poll() {
        record = dao.getLockedEntity();
        queue(record);
    }

这个方法不是事务性的,但是dao.getLockedEntity()REQUIRED是事务性的。

一切顺利,在路上,经过几个月的生产,它以乐观的锁例外失败了。

经过大量的调试和详细检查之后,我们可以发现有些人已经更改了代码如下:

代码语言:javascript
运行
复制
    @Transactional(propagation=Propagation.REQUIRED, readOnly=false)
    void poll() {
        record = dao.getLockedEntity();
        queue(record);              
    }

因此,甚至在dao.getLockedEntity()中的事务被提交(它使用相同的轮询方法事务)之前,记录就排队了,并且在提交poll()方法事务时,处理程序(不同的线程)改变了对象下面的对象。

我们解决了这个问题,现在看来不错。我想分享它,因为乐观的锁异常可能会令人困惑,而且很难调试。

票数 20
EN

Stack Overflow用户

发布于 2011-12-27 14:29:17

看起来你并没有实际使用从数据库中检索到的电子邮件,而是将一个旧的副本作为参数使用。行上用于版本控制的内容在检索上一个版本时和执行更新时之间发生了更改。

您可能希望您的代码看起来更像:

代码语言:javascript
运行
复制
    @Transactional
    public void test(String id, String subject) {
       Email email = getEmailById(id);
       email.setSubject(subject);
       updateEmail(email);
    }
票数 16
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8645694

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档