【问题标题】:Optimistic Locking failure error in Grails GORMGrails GORM中的乐观锁定失败错误
【发布时间】:2011-05-21 06:25:09
【问题描述】:

我有控制器操作,我在其中更新域对象中的一个简单值:

user = User.get(session.user.id) 
user.credits +=20 

只要我不使用 ajax,一切都可以正常工作。但在我的应用程序中,我必须使用 ajax 提前获取内容并存储在客户端的 javascript 变量中。此 ajax 在客户端启动,无需用户交互即可提前获取内容。此 ajax 请求调用完全相同的操作和修改 user.credits 的控制器。在此操作中,不会对用户进行其他更新。仅读取此域对象上的其他数据。 ajax 请求是异步的,因此当对同一操作发出请求时,同一客户端很快就会对同一操作发出另一个请求。这会导致 Grails 中的乐观锁定失败。

我尝试了所有的组合 用户锁() user.save(flush: true)

在更新之前、期间和/或之后,但没有任何区别。

如果我不使用 ajax 发起的请求,那么 grails 内置的自动保存域对象(如果在控制器操作退出时脏了)效果很好,我什至不必使用 user.save 或 user.lock()。

这是一个 grails 错误还是遗漏了什么?有人经历过吗?

这是堆栈跟踪:

ERROR events.PatchedDefaultFlushEventListener  - Could not synchronize database state with session 
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [zen37.User#8]
        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.codehaus.groovy.grails.orm.hibernate.events.PatchedDefaultFlushEventListener.performExecutions(PatchedDefaultFlushEventListener.java:46) 
        at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50) 
        at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027) 
        at org.springframework.orm.hibernate3.HibernateAccessor.flushIfNecessary(HibernateAccessor.java:390) 
        at org.codehaus.groovy.grails.orm.hibernate.support.GrailsOpenSessionInViewInterceptor.flushIfNecessary(GrailsOpenSessionInViewInterceptor.java:120) 
        at org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor.postHandle(OpenSessionInViewInterceptor.java:181) 
        at org.codehaus.groovy.grails.orm.hibernate.support.GrailsOpenSessionInViewInterceptor.postHandle(GrailsOpenSessionInViewInterceptor.java:70)
        at org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter.postHandle(WebRequestHandlerInterceptorAdapter.java:61) 
        at org.codehaus.groovy.grails.web.servlet.GrailsDispatcherServlet.doDispatch(GrailsDispatcherServlet.java:303) 
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719) 
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644) 
        at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:560) 
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:727) 
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:820) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:70) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:70) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:70) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:70) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
        at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:646) 
        at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:436) 
        at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:374) 
        at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:302) 
        at org.codehaus.groovy.grails.web.util.WebUtils.forwardRequestForUrlMappingInfo(WebUtils.java:293) 
        at org.codehaus.groovy.grails.web.util.WebUtils.forwardRequestForUrlMappingInfo(WebUtils.java:260) 
        at org.codehaus.groovy.grails.web.util.WebUtils.forwardRequestForUrlMappingInfo(WebUtils.java:251) 
        at org.codehaus.groovy.grails.web.mapping.filter.UrlMappingsFilter.doFilterInternal(UrlMappingsFilter.java:183) 
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
        at org.codehaus.groovy.grails.web.sitemesh.GrailsPageFilter.obtainContent(GrailsPageFilter.java:246) 
        at org.codehaus.groovy.grails.web.sitemesh.GrailsPageFilter.doFilter(GrailsPageFilter.java:135) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
        at org.codehaus.groovy.grails.web.servlet.filter.GrailsReloadServletFilter.doFilterInternal(GrailsReloadServletFilter.java:104) 
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
        at org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:69) 
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
        at org.codehaus.groovy.grails.web.filters.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:65) 
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88) 
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76) 
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237) 
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233) 
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191) 
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128) 
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) 
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) 
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293) 
        at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849) 
        at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583) 
        at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:454) 
        at java.lang.Thread.run(Thread.java:619) 
2010-12-05 13:40:52,848 [http-8181-5] ERROR errors.GrailsExceptionResolver  - Object of class [zen37.User] with identifier [8]: optimistic locking failed; nested exception is org.hibernate.StaleObject 
StateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [zen37.User#8] 
org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException: Object of class [zen37.User] with identifier [8]: optimistic locking failed; nested exception is org.hibernate.StaleObjec 
tStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [zen37.User#8] 
        at java.lang.Thread.run(Thread.java:619) 
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [zen37.User#8] 
        ... 1 more

【问题讨论】:

    标签: grails


    【解决方案1】:

    如果您在会话中存储用户域对象的实例,则需要使用合并而不是保存。看 http://www.grails.org/doc/latest/ref/Domain%20Classes/merge.html

    【讨论】:

    • 记住我是给你的人> 10k ;)
    • 感谢您的建议。我在操作开始时尝试了 user = user.merge() 或 user = user.merge(flush: true),就在更新 user.credits 之前,更新 user.credits 等之后。我确实大量使用了 http 会话。 user 在我的 GSP 中,我也做了 session.user = user.merge() 但无论我做什么,都会抛出完全相同的异常。这花费了我很多时间。
    【解决方案2】:

    尝试使用这样的东西:

    if(!user.isAttached()){
        user.attach()
    }
    

    当对象在当前会话关闭后(例如在请求结束后)丢失与当前会话的附件时,这很有用。

    【讨论】:

      【解决方案3】:

      尝试读取与获取

      即:

      User.read(session.user.id)
      

      这仍然允许您通过刷新保存对用户进行更改,并避免并发锁定问题

      【讨论】:

        【解决方案4】:

        我在尝试保存用户发回的一系列 GUI 状态 cookie 时遇到了同样的问题(因此当他们从不同的浏览器登录时我可以重新发送它们)。我尝试了很多方法——包括直接对数据库使用 SQL(没有休眠)。我得到了不同的错误,但它们都是同一个问题的表现。确实有一个并发更新。

        诀窍是为每次状态更改创建一个新实体。这样它就不会过时。

        在您的情况下,我认为您需要做的是在附加到用户的“信用”表中插入一些内容。不要使用 user.addToCredits().save() 这样做,因为这会修改用户,使用 new Credit(user:user).save() 因为这不会修改用户 - 不要问我为什么 ;-) 然后为了获得用户的信用,您可以使用类似的东西来计算用户信用的内容一个 user.credits.size()

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-11-17
          • 1970-01-01
          • 1970-01-01
          • 2015-03-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多