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,这被解释为其他人在您的阅读和更新数据之间进行了更改。
我正在使用 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,这被解释为其他人在您的阅读和更新数据之间进行了更改。