开发者

Optimistic Locking failure error in Grails GORM

开发者 https://www.devze.com 2023-01-28 23:22 出处:网络
I have controller action in which I update a simple value in domain object: user = User.get(session.user.id)

I have controller action in which I update a simple value in domain object:

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

All works fine as long as I do not use ajax. But in my application I have to use ajax to fetch content in advance and store in javascript variable in the client. This ajax is initiated in the client without user interaction to fetch content in advance. This ajax request calls exactly same action and same controller in which user.credits is modified. In this action no other updates are made to user. Other data on this domain object is only read. The ajax request is asynchronous so while a request is made to same action, very quickly another request is made to same action by the same client. This cause optimistic locking failure showstopper in Grails.

I tried all combinations of user.lock() user.save(flush: true)

before, during and/or after the update but it does not make any difference.

If I do not use ajax initiated requests then the grails built-in automatic save domain objects if dirty upon exit of controller action works very well and I do not even have to use user.save or user.lock().

Is this a grails bug or something else is being missed? Has any one experienced?

This is the stack trace:

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.开发者_Go百科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


If you are storing an instance of a User domain object in your session you need to use merge instead of save. See http://www.grails.org/doc/latest/ref/Domain%20Classes/merge.html


Try using something like this:

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

It's useful when the object looses the attachment to the current session after this one is closed (for example after a request ends).


Try read vs get

ie:

User.read(session.user.id)

This will still allow you to make a change to the user with a flushed save and will avoid concurrent locking issues


I had the same problem trying to save a sequence of GUI state cookies sent back by a user (so I could re-send them when they logged in from a different browser). I tried lots of ways of doing this - including using SQL directly against the database (no hibernate). I got different errors, but they are all manifestations of the same problem. There genuinely is a concurrent update going on.

The trick is to create a new entity for each change in state. That way it can't go stale.

In your case, what I think you need to do is to insert something into a "credits" table attached to the user. Don't do this with a user.addToCredits().save() because that will modify the user, do it with a new Credit(user:user).save() because that doesn't modify the user - don't ask me why ;-) Then in order to get the user's credit, you can count the contents of the user's credits with something like a user.credits.size()

0

精彩评论

暂无评论...
验证码 换一张
取 消