我有一个带控制器的应用程序。执行控制器操作会使用数据库字段进行更新。当一次完成一个动作时,它可以正常工作。但是,当我同时执行两个操作时,我收到org.hibernate.StaleObjectStateException
错误
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
我需要控制器能够处理同步操作,例如通过对请求进行排队并按顺序执行操作。这有可能吗?或者我自己需要以某种方式照顾它?
(在这里使用synchronized
帮助吗?是正确的方式吗?)
数据库是mem / file,因为我只使用它很少。没有必要运行一个完整的数据库。
编辑:调用'层次结构'如下:这是入口点 - 控制器。
GameController
def doTurn() {
String login = params.login
// From stackoverflow
def c = Player.createCriteria()
def player = c.get {
eq "login", login
lock true
}
// def player = Player.findAllByLogin(login).get(0)
PlayerChoice choice = Utils.intToChoice(Integer.parseInt(params.choice))
int turnNumber = Integer.parseInt(params.turn)
gameFlowService.makeTurn(player, choice, turnNumber)
def res = new Utils().playerStateToJSON(player)
render res as JSON
}
它调用gameFlowService:
GameFlowService{
def makeTurn(Player player, PlayerChoice choice, int turnNumber) {
(...)
Game currentGame = player.currentGame
Turn lastTurn = currentGame.getLastTurn()
// I do stuff with Turn (if it exists) or create it
currentGame.createTurn()
currentGame.getLastTurn().makeChoice(player, choice)
if (currentGame!=null){
currentGame.save(flush: true, failOnError: true)
}
}
游戏:
Game {
Player player1
Player player2
int turnsToBePlayed
List turns
static hasMany = [turns: Turn]
static belongsTo = [tournament: Tournament]
public void createTurn() {
Turn turn = new Turn([game : this,
turnNumber: turns.size(),
player1 : player1,
player2 : player2,
choice1 : PlayerChoice.CHOICE_NOT_SET,
choice2 : PlayerChoice.CHOICE_NOT_SET,
points1 : 0,
points2 : 0])
this.addToTurns(turn)
this.save(flush: true, failOnError: true)
}
}
转过来:
class Turn {
int turnNumber
boolean completed = false
Player player1
Player player2
PlayerChoice choice1
PlayerChoice choice2
int points1
int points2
static belongsTo = [game: Game]
public void makeChoice(Player player, PlayerChoice playerChoice){
// This method only changes Turn's member variables
}
}
当我添加一个新的转向时,我只保存了Game实例 - 我被引导相信Hibernate会自动保留任何进一步的更改。
尽管使用了锁,我仍然得到同样的错误(stacktrace说它在游戏中发生在线:this.save(flush: true, failOnError: true)
)
编辑2: 我相信我的问题不是那么罕见。控制器操作会导致数据库中的更改,并且需要以某种方式序列化。 Hibernate / Grails如何解决它?
答案 0 :(得分:0)
我认为你的问题如下,考虑到两个同时访问数据库的线程:
从数据库中检索对象时可以锁定对象。这导致“select ... for update”,请参阅此链接以了解用法:
http://grails.org/doc/latest/ref/Domain%20Classes/lock.html
但是为了避免任何竞争条件,你必须使用锁定方法作为你的吸气剂,如下所示:
def airport = Airport.lock(10) // lock for update
airport.name = "Heathrow"
airport.save()
如果必须查询对象,可以在Criteria中使用lock。
这是Criteria的一个例子:
def c = Airport.createCriteria()
c.get {
eq "name", name
lock true
}