我正在使用grails 2.4.2,但怀疑这通常与ORM(休眠)有关。
我正在尝试在对象集合中重新分配序数属性。这种重新分配构成了对象的“重新洗牌”。
假设:
class Bar {
int i
static belongsTo = [foo:Foo]
static constraints = { i unique: 'foo' }
}
class Foo {
static hasMany = [bars:Bar]
static mapping = { bars sort: 'i' }
}
和
import grails.test.spock.IntegrationSpec
import grails.validation.ValidationException
class FooServiceIntegrationSpec extends IntegrationSpec {
public static final IntRange BAR_RANGE = 0..9
Foo foo
def setup() {
foo = Foo.build()
(BAR_RANGE).each {
Bar bar = Bar.build(i: it, foo: foo)
foo.addToBars(bar)
}
foo.save(flush:true).refresh()
}
void "reshuffling i-s doesn't work"() {
when:
List l = (BAR_RANGE).collect()
use(Collections) {l.shuffle()}
foo.bars.eachWithIndex{b,int index -> b.i=l[index]}
foo.save()
then:
ValidationException ex = thrown()
ex.message =~ 'must be unique'
}
}
如何实现上述改组尝试(I.E.不会超越唯一约束)?
谢谢:)
答案 0 :(得分:1)
由于ORM将逐个更新Bar
个实例,因此如果您首先删除Bar
的所有Foo
实例,则只能实现此目的
foo.bars*.delete(flush: true)
然后执行其余的测试代码。
为什么?
Hibernate将为每个Bar
实例创建一个Update语句,并且很可能在第一次更新时失败。这是一个例子。想象一下,您已经拥有Bar
个i = 4
个实例。由于i
被声明为唯一,因此更新将失败。
UPDATE bar SET i = 4 WHERE id = ?;
这就是为什么在开始刷新对数据库的更改之前,首先需要删除整个集合bars
的原因。希望这有助于解释问题。
答案 1 :(得分:0)
我不认为您应该将i
定义为唯一。您应该使用复合键,以合并foo.id
和i
。否则,您将遇到不同Foo实例之间的冲突。
顺便说一句,如果您将Foo.bars
定义为List
,则GORM会自动添加ix
位置列