Grails 什么时候检查对象陈旧性?

When does grails check for Object Staleness?

我正在使用 Grails 2.5.1,我有一个控制器调用服务方法,该方法偶尔会导致 StaleObjectStateException。服务方法中的代码在 obj.save() 调用周围有一个 try catch,它只是忽略异常。但是,只要发生这些冲突之一,日志中仍会打印错误,并向客户端返回错误。

我的游戏控制器代码:

def finish(String gameId) {
   def model = [:]
   Game game = gameService.findById(gameId)

   // some other work

   // this line is where the exception points to - NOT a line in GameService:
   model.game = GameSummaryView.fromGame(gameService.scoreGame(game))

   withFormat {
     json {
        render(model as JSON)
     }
   }
}

我的游戏服务代码:

Game scoreGame(Game game) {
   game.rounds.each { Round round ->
      // some other work
         try {
             scoreRound(round)
             if (round.save()) {
                 updated = true
             }
         } catch (StaleObjectStateException ignore) {
             // ignore and retry
         }
   }
}

堆栈跟踪表明异常是从我的 GameController.finish 方法生成的,它没有指向我的 GameService.scoreGame 方法中的任何代码。这对我来说意味着 Grails 在事务开始时检查是否过时,而不是在尝试对象 save/update 时检查?

这个异常我已经遇到过很多次了,通常我通过不遍历对象图来修复它。

例如,在这种情况下,我将删除 game.rounds 引用并将其替换为:

def rounds = Round.findAllByGameId(game.id)
rounds.each {
   // ....
}

但这意味着在创建事务时不会检查陈旧性,而且它并不总是实用的,在我看来有点违背了 Grails 惰性集合的目的。如果我想自己管理所有协会,我会的。

我已阅读有关 Pessimistic and Optimistic Locking 的文档,但我的代码遵循那里的示例。

我想了解更多关于 how/when Grails (GORM) 检查是否过时以及在何处处理它的信息?

您没有显示或讨论任何交易配置,但这可能是造成混淆的原因。根据您所看到的情况,我猜您的控制器中有 @Transactional 注释。我这么说是因为如果是这样的话,一个事务从那里开始,并且(假设你的服务是事务性的)服务方法加入当前事务。

在您调用 save() 的服务中,但您没有刷新会话。这对性能更好,特别是如果您在工作流的另一部分进行其他更改时 - 当您可以一次推送所有更改时,您不希望将两组或更多组更新推送到每个对象。由于您不刷新,并且由于事务不会像控制器未启动事务那样在方法结束时提交,因此仅在控制器方法完成且事务提交时才推送更新。

您最好将所有事务(和业务)逻辑移至该服务,并从您的控制器中删除每个 事务痕迹。避免 "fixing" 除非您愿意承受性能损失,否则请急切地刷新。

至于过时检查 - 它相当简单。当 Hibernate 生成 SQL 进行更改时,它的形式是 UPDATE tablename SET col1=?, col2=?, ..., colN=? where id=? and version=?。 id 显然会匹配,但是如果版本增加了,那么 where 子句的版本部分将不匹配并且 JDBC 更新计数将为 0,而不是 1,这被解释为其他人在您的阅读和更新数据之间进行了更改。