同时请求的Grails org.hibernate.StaleObjectStateException

时间:2014-07-25 11:21:41

标签: database multithreading hibernate grails

我有一个带控制器的应用程序。执行控制器操作会使用数据库字段进行更新。当一次完成一个动作时,它可以正常工作。但是,当我同时执行两个操作时,我收到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如何解决它?

1 个答案:

答案 0 :(得分:0)

我认为你的问题如下,考虑到两个同时访问数据库的线程:

  1. 检索对象
  2. B检索对象
  3. 更新对象
  4. 保存对象
  5. B更新对象
  6. B保存对象 - > StaleObjectStateException
  7. 从数据库中检索对象时可以锁定对象。这导致“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
    }