我一直试图让 GORM 在集成测试中抛出乐观锁定错误。之前有人说过,如果不使用多个线程,就不可能测试并发更新错误,但即便如此,我发现我的测试用例的行为令人惊讶:
void testOptimisticLocking() {
new Widget(foo:"bar").save(flush:true)
// Get the widget and change a property
def widget = Widget.findByFoo("bar")
assertNotNull widget
widget.foo = "baz"
def widgetVersion = widget.version
println "widget: $widgetVersion" // >>> widget: 0
// Discard the widget without saving; Hibernate now knows nothing about it
widget.discard()
// Get a different instance of the same widget from the database,
// with the old value for foo
def sameWidget = Widget.findByFoo("bar")
assertNotNull sameWidget
assertFalse System.identityHashCode(sameWidget) == System.identityHashCode(widget)
// Change the foo property and save
sameWidget.foo = "bat"
sameWidget.save(flush:true)
// Check the version has incremented
println "sameWidget: $sameWidget.version" // >>> sameWidget: 1
assertTrue widgetVersion < sameWidget.version
// Check the database hold the "bat" widget
sameWidget.discard()
assertEquals 0, Widget.countByFoo("bar")
assertEquals 1, Widget.countByFoo("bat")
// Reattach the original widget and try to save it
assertFalse widget.isAttached()
widget.attach()
println "widget: $widget.version" // >>> widget: 0
assertEquals widgetVersion, widget.version
assertEquals "baz", widget.foo
// TEST FAILS HERE
// No error is thrown, and the update fails silently!
shouldFail(org.hibernate.StaleStateException) {
assertTrue widget.validate()
widget.save(flush:true)
println widget.foo // >>> baz
widget.discard()
println "baz: " + Widget.countByFoo("baz") // >>> baz: 0
println "bat: " + Widget.countByFoo("bat") // >>> bat: 1
}
}
重新挂载的Widget实例不会持久化到数据库中,但不会抛出异常!
我的测试用例相当做作,但我仍然对结果感到惊讶。
有人可以解释一下吗?