Grails GORM中的乐观锁定失败错误

时间:2010-12-06 22:19:09

标签: grails

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

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

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

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

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

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

这是一个grails bug还是其他东西被遗漏了?有没有人经历过?

这是堆栈跟踪:

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

4 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分:1)

尝试使用以下内容:

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

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

答案 2 :(得分:0)

尝试阅读与获取

即:

User.read(session.user.id)

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

答案 3 :(得分:0)

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

诀窍是为每个状态变化创建一个新实体。这样就不会陈旧。

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