我使用Grails 2.5.1,并且我有一个控制器调用服务方法,偶尔会导致StaleObjectStateException
。 service方法中的代码在obj.save()
调用周围有一个try catch,它忽略了异常。但是,每当发生其中一个冲突时,日志中仍会显示错误,并且会向客户端返回错误。
我的GameController代码:
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)
}
}
}
我的GameService代码:
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在启动事务时检查过时,而不是在尝试保存/更新对象时?
我多次遇到此异常,通常我会通过不遍历对象图来修复它。
例如,在这种情况下,我删除game.rounds
引用并将其替换为:
def rounds = Round.findAllByGameId(game.id)
rounds.each {
// ....
}
但这意味着在创建交易时不会检查陈旧性,并且它并不总是实用的,在我看来有点挫败了Grails懒惰集合的目的。如果我想自己管理所有的协会,我会。
我已阅读有关Pessimistic and Optimistic Locking的文档,但我的代码遵循那里的示例。
我想更多地了解Grails(GORM)如何/何时检查陈旧性以及在何处处理它?</ p>
答案 0 :(得分:3)
您不会显示或讨论任何交易配置,但这可能是造成混淆的原因。根据您所看到的内容,我猜您的控制器中有@Transactional
个注释。我这样说是因为如果是这种情况,那么事务就会从那里开始,并且(假设您的服务是事务性的)服务方法加入当前事务。
在您致电save()
的服务中,但您不会刷新会话。这对于性能更好,特别是如果工作流的另一部分中您进行了其他更改 - 当您可以一次性推送所有更改时,您不希望向每个对象推送两组或更多组更新。由于您没有刷新,并且由于事务未在方法结束时提交,如果控制器尚未启动事务,因此只有在控制器方法完成且事务提交时才会推送更新。 / p>
最好将所有事务(和业务)逻辑移动到服务中,并从控制器中删除每个事务跟踪。除非你愿意接受表演,否则不要急于冲洗“固定”这个。
至于陈旧性检查 - 它相当简单。当Hibernate生成SQL以进行更改时,它的格式为UPDATE tablename SET col1=?, col2=?, ..., colN=? where id=? and version=?
。 id显然会匹配,但是如果版本增加了,那么where子句的版本部分将不匹配,JDBC更新计数将为0而不是1,这被解释为意味着其他人在您的阅读和更新数据。