2012-11-06 48 views
1

我遇到了一個線程併發訪問的方法問題。場景是線程A遍歷一個列表,創建並保存一個域對象。線程B在線程A仍在迭代時調用相同的方法,嘗試訪問線程A創建的域對象,並引發StaleStateException。防止Grails中的陳舊狀態錯誤

舉例說明:

def populateStudents(listOfStudents) { 

    listOfStudents.each { Student student -> 
     def schoolName = student.schoolName 
     def school = School.findByName(schoolName) ?: new School(name: schoolName).save(flush:true) 

     student.addToSchools(school) 

     // So Thread A creates a new School domain object and continues to iterate. 
     // Thread B comes along and finds the domain created by Thread A. 
     // However, it appears that the StaleStateException is thrown when Thread B tries to addToSchools 

    } 

} 

這裏是堆棧:

org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.app.School#60] 
    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.HibernateTemplate$28.doInHibernate(HibernateTemplate.java:883) 
    at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:406) 
    at org.springframework.orm.hibernate3.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:374) 
    at org.springframework.orm.hibernate3.HibernateTemplate.flush(HibernateTemplate.java:881) 
    at org.codehaus.groovy.grails.orm.hibernate.metaclass.MergePersistentMethod$1.doInHibernate(MergePersistentMethod.java:60) 
    at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:406) 
    at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:339) 
    at org.codehaus.groovy.grails.orm.hibernate.metaclass.MergePersistentMethod.performSave(MergePersistentMethod.java:54) 
    at org.codehaus.groovy.grails.orm.hibernate.metaclass.AbstractSavePersistentMethod.doInvokeInternal(AbstractSavePersistentMethod.java:179) 
    at org.codehaus.groovy.grails.orm.hibernate.metaclass.AbstractDynamicPersistentMethod.invoke(AbstractDynamicPersistentMethod.java:59) 
    at sun.reflect.GeneratedMethodAccessor23965.invoke(Unknown Source) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:616) 
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoCachedMethodSite.invoke(PojoMetaMethodSite.java:188) 
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:52) 
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:132) 
    at org.codehaus.groovy.grails.plugins.orm.hibernate.HibernatePluginSupport$_addBasicPersistenceMethods_closure73.doCall(HibernatePluginSupport.groovy:814) 
    at sun.reflect.GeneratedMethodAccessor845.invoke(Unknown Source) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:616) 
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90) 
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaMethod.invoke(ClosureMetaMethod.java:80) 
    at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoMetaMethodSiteNoUnwrapNoCoerce.invoke(PogoMetaMethodSite.java:307) 
    at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.call(PogoMetaMethodSite.java:63) 
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:124) 
    at com.app.TransactionService$_populateStudents_closure3.doCall(TransactionService.groovy:96) 
    at sun.reflect.GeneratedMethodAccessor24059.invoke(Unknown Source) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:616) 
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90) 
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233) 
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1058) 
    at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:1070) 
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:886) 
    at groovy.lang.Closure.call(Closure.java:282) 
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.eachWithIndex(DefaultGroovyMethods.java:1213) 
    at org.codehaus.groovy.runtime.dgm$150.invoke(Unknown Source) 
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:270) 
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:52) 
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:124) 
    at com.app.TransactionService.populateStudents(TransactionService.groovy:49) 
    at com.app.TransactionService$$FastClassByCGLIB$$1b289e99.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.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
    at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:621) 
    at com.app.TransactionService$$EnhancerByCGLIB$$8986440c.populateStudents(<generated>) 


INFO UpdateController - Object of class [com.app.School] with identifier [60]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.app.School#60] 

任何想法

+0

請你附上相關部分堆棧跟蹤? –

+0

@AndreSteingress當然 - 附 – RyanLynch

回答

4

您可以通過使用pessimistic locking解決這個問題:

def school = School.findByName(schoolName, [lock: true]) ?: new School(name: schoolName).save(flush:true) 
+0

酷,我會試試這個。我是否需要關閉學校的版本專欄? – RyanLynch

+0

不,只要事務沒有被提交,'lock:true'應該鎖定相關的數據庫行。 –

+0

工作,謝謝 – RyanLynch