我的问题与我已经得到答案的问题略有不同:link
我有以下域类:
@ToString(includeNames = true, includeFields = true, excludes = 'dateCreated,lastUpdated,metaClass')
@EqualsAndHashCode
class Users {
Integer userId
Integer roleId
String username
String email
static constraints = {
roleId blank:false, nullable:false
username blank:false, nullable:false
email blank:true, nullable:true
}
static mapping = {
table 'users'
version false
id name:'userId', column:'user_id', sqlType:'int'
roleId name:'roleId', column:'role_id', sqlType:'int'
}
}
MySql数据库表: 列:
user_id int(11) PK
role_id int(11)
username varchar(32)
email varchar(255)
修改后的默认生成的控制器来执行JSON CRUD:
@Transactional
class UsersController extends RestfulController {
static responseFormats = ['json', 'xml']
GET工作正常,但是当我发布POST时,我收到错误:
curl -i -X POST -H "Content-Type: application/json" -d "{userId:2, roleId:1, username:'testId1', email:'test.id@test.com'}" http://test.xxx.com:8090/test/Users
错误:
2014-06-16 15:46:49,458 [http-bio-8090-exec-5] ERROR errors.GrailsExceptionResolver - StaleObjectStateException occurred when processing request: [POST] /test/Users
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.xxx.Users#2]. Stacktrace follows:
Message: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.xxx.Users#2]
Line | Method
->> 44 | $tt__save in com.xxx.UsersController$$EOhNaqPu
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 198 | doFilter in grails.plugin.cache.web.filter.PageFragmentCachingFilter
| 63 | doFilter in grails.plugin.cache.web.filter.AbstractFilter
| 1145 | runWorker in java.util.concurrent.ThreadPoolExecutor
| 615 | run . . . in java.util.concurrent.ThreadPoolExecutor$Worker
^ 724 | run in java.lang.Thread
编辑: 控制器代码:
import static org.springframework.http.HttpStatus.*
import com.xxx.domain.Users;
import grails.rest.RestfulController;
import grails.transaction.Transactional
@Transactional
class UsersController extends RestfulController {
static responseFormats = ['json', 'xml']
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
response.setHeader("Access-Control-Allow-Origin", "*")
respond Users.list(params), model:[usersInstanceCount: Users.count()]
}
def show(Users usersInstance) {
respond usersInstance
}
def create() {
respond new Users(params)
}
@Transactional
def save(Users usersInstance) {
if (usersInstance == null) {
notFound()
return
}
if (usersInstance.hasErrors()) {
respond usersInstance.errors, view:'create'
return
}
usersInstance.save flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.created.message', args: [message(code: 'users.label', default: 'Users'), usersInstance.id])
redirect usersInstance
}
'*' { respond usersInstance, [status: CREATED] }
}
}
def edit(Users usersInstance) {
respond usersInstance
}
@Transactional
def update(Users usersInstance) {
if (usersInstance == null) {
notFound()
return
}
if (usersInstance.hasErrors()) {
respond usersInstance.errors, view:'edit'
return
}
usersInstance.save flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.updated.message', args: [message(code: 'Users.label', default: 'Users'), usersInstance.id])
redirect usersInstance
}
'*'{ respond usersInstance, [status: OK] }
}
}
@Transactional
def delete(Users usersInstance) {
if (usersInstance == null) {
notFound()
return
}
usersInstance.delete flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.deleted.message', args: [message(code: 'Users.label', default: 'Users'), usersInstance.id])
redirect action:"index", method:"GET"
}
'*'{ render status: NO_CONTENT }
}
}
protected void notFound() {
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'users.label', default: 'Users'), params.id])
redirect action: "index", method: "GET"
}
'*'{ render status: NOT_FOUND }
}
}
}