我开发了REST Web服务,它能够接受一些数据,从数据库加载用户,稍微更改一下并将其保存回数据库。
以下是REST Web服务的内容:
class RespondentController {
static allowedMethods = [save: "GET"]
synchronized def save = {
Campaign.withTransaction { status ->
User user = User.get(params['userId'])
// do some changes in users...
user.save()
}
}
}
问题是当同时有更多请求时。有没有办法改变Web服务,以便它可以同时接受更多的请求(可能是一些配置)?
2011-12-18 10:28:57,808 [http-8080-5] ERROR errors.GrailsExceptionResolver - Exception occurred when processing request: [GET] /my-rest-ws/respondent
Stacktrace follows:
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [my.app.User#1]
at my.app.UserController$_closure1.doCall(RespondentController.groovy:19)
at my.app.UserController$_closure1.doCall(RespondentController.groovy)
at java.lang.Thread.run(Thread.java:636)
2011-12-18 10:29:02,625 [http-8080-5] 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): [my.app.User#1]
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.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.support.TransactionTemplate.execute(TransactionTemplate.java:147)
at sun.reflect.GeneratedMethodAccessor773.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:124)
at org.codehaus.groovy.grails.plugins.orm.hibernate.HibernatePluginSupport$_addTransactionalMethods_closure22.doCall(HibernatePluginSupport.groovy:502)
at sun.reflect.GeneratedMethodAccessor666.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.metaclass.ClosureStaticMetaMethod.invoke(ClosureStaticMetaMethod.java:59)
at org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite$StaticMetaMethodSiteNoUnwrapNoCoerce.invoke(StaticMetaMethodSite.java:148)
at org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite.call(StaticMetaMethodSite.java:88)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:124)
at my.app.UserController$_closure1.doCall(UserController.groovy:19)
答案 0 :(得分:3)
为每个请求创建一个新的控制器,因此同步该操作将无济于事,因为它不会被并发用户共享。另外,它是一个Closure(2.0之前的版本),并且同步Closure没有任何意义。
如果要阻止对象的并发编辑,请将其锁定在事务中。创建服务
class UserService {
void updateUser(long userId, ...) {
User user = User.lock(userId)
// do some changes in users...
user.save()
}
}
并将其注入您的控制器:
class RespondentController {
def userService
def save = {
long userId = params['userId'] as Long
userService.updateUser(userId, ...)
}
}
答案 1 :(得分:0)
最终解决方案就是这个。它确实有效,所以我能够处理数百个并发访问我的其他Web服务:
class RespondentController {
static scope = "singleton"
static allowedMethods = [save: "GET"]
def save() {
long userId = params['userId'] as Long
User.lock(userId)
User user = User.get(userId)
// do changes in domain classes...
// and some stuff which should be done (e.g. send an email...)
user.save(flush:true)
}
}
答案 2 :(得分:0)
“Synchronized
”没有意义,因为Grails
每次调用controller
使用:
User user = User.lock(userId)
而不是
User.lock(userId)
User user = User.get(userId)