交换唯一属性值后 Grails 验证失败
嗨,我正在尝试创建一个界面,用户可以在其中创建一些带有不同语言翻译的自定义枚举。例如,用户可以创建枚举“电影类型”。对于该枚举,可能存在一个枚举值“Comedy”,对于该枚举值“Comedy”可能存在针对多种语言的一个或多个枚举值翻译。
由于特定语言只能有一种翻译,因此我向枚举值翻译域类添加了唯一约束。这些是我现在的域类:
class Enumeration {
String label
List<EnumerationValue> enumerationValues = new ArrayList<EnumerationValue>()
static hasMany = [ enumerationValues: EnumerationValue ]
static constraints = {
label(nullable: false, blank: false)
enumerationValues(nullable: true)
}
}
class EnumerationValue {
String label
List<EnumerationValueTranslation> enumerationValueTranslations = new ArrayList<EnumerationValueTranslation>()
static belongsTo = [ enumeration: Enumeration ]
static hasMany = [ enumerationValueTranslations: EnumerationValueTranslation ]
static constraints = {
label(nullable: false, blank: false, unique: 'enumeration')
enumeration(nullable: false)
enumerationValueTranslations(nullable: false)
}
}
class EnumerationValueTranslation {
String value
Language language
static belongsTo = [ enumerationValue: EnumerationValue ]
static constraints = {
value(nullable: false, blank: false)
language(nullable: true, unique: 'enumerationValue')
enumerationValue(nullable: false)
/* unique constraint as mentioned in description text */
language(unique: 'enumerationValue')
}
}
到目前为止,这工作得很好。当我以语言互换的方式更新同一枚举值的两个枚举值翻译时,就会出现问题。例如我有一个
- 枚举值:“喜剧”
以及一些语言“意外”混淆的翻译
- “喜剧”翻译
- 语言:德语,值:“喜剧”
- 语言:英语,值“Komodie”
如果用户意识到他混淆了语言,他可能想要交换语言并再次保存枚举。这就是我的错误发生的地方,因为交换语言后,枚举值翻译唯一约束验证为 false。
为了调试这个,我只是尝试打印出在处理参数之前和之后导致翻译的错误,所以:
Enumeration enumeration = Enumeration.get(params['id']);
println "before:"
enumeration.enumerationValues.each() { enumValue ->
enumValue.enumerationValueTranslations.each() { enumValueTr ->
println enumValueTr;
if(!enumValueTr.validate()) {
// print errors...
}
}
}
// swap languages:
// (this are the lines of codes that are actually executed, and cause the
// error. The actual processing of params looks different of course...)
// sets the language of "Comedy" to English
EnumerationValueTranslation.get(5).language = Language.get(1);
// sets the language of "Komödie" to German
EnumerationValueTranslation.get(6).language = Language.get(2);
println "after:"
enumeration.enumerationValues.each() { enumValue ->
enumValue.enumerationValueTranslations.each() { enumValueTr ->
println enumValueTr;
if(!enumValueTr.validate()) {
// print errors...
}
}
}
结果是:
before:
EnumerationValueTranslation(value: Fantasy, language: en_US, enumerationValue: Fantasy)
EnumerationValueTranslation(value: Phantasie, language: de_DE, enumerationValue: Fantasy)
EnumerationValueTranslation(value: Comedy, language: de_DE, enumerationValue: Comedy)
EnumerationValueTranslation(value: Komödie, language: en_US, enumerationValue: Comedy)
after:
EnumerationValueTranslation(value: Fantasy, language: en_US, enumerationValue: Fantasy)
EnumerationValueTranslation(value: Phantasie, language: de_DE, enumerationValue: Fantasy)
EnumerationValueTranslation(value: Comedy, language: en_US, enumerationValue: Comedy)
validation fails: Property [language] of class [Translation] with value [Language(code: en_US)] must be unique
EnumerationValueTranslation(value: Komödie, language: de_DE, enumerationValue: Comedy)
validation fails: Property [language] of class [Translation] with value [Language(code: de_DE)] must be unique
在这种状态下,我还没有删除或保存(或以任何方式刷新)任何内容 - 这只是更改对象后的结果。正如您所看到的,实际数据确实没有不一致,验证应该不会失败。
我更改翻译的方式可能有错误吗?我只是通过 ID 获取它们并简单地更新了语言 - 我在一个简约的示例中尝试了这一点,并且它在那里工作...... 如果我只是创建所有枚举值和枚举值翻译的深拷贝并存储它(这意味着验证确实不应该失败),它也可以工作,但我认为这实际上不是应该的方式完成...
另一个奇怪的事情是,只有当我迭代数据时验证才会失败。如果我根本不接触数据,则不会发生错误,但数据也不会保存,这意味着以下几行会导致验证被评估:
enumeration.enumerationValues.each() { ev ->
ev.enumerationValueTranslations.each() { evt ->
}
}
这就是为什么我坚信一定存在一些不平凡的问题...如果您还有其他需要了解的信息,请告诉我。
感谢您的帮助
最佳答案
让我再试一次:)
我正在看UniqueConstraint.processValidate()
,并且可以假设其逻辑不考虑交换情况。
特别是代码
boolean reject = false;
if (id != null) {
Object existing = results.get(0);
Object existingId = null;
try {
existingId = InvokerHelper.invokeMethod(existing, "ident", null);
}
catch (Exception e) {
// result is not a domain class
}
if (!id.equals(existingId)) {
reject = true;
}
}
else {
reject = true;
}
应迭代获得的结果
并验证字段值STILL是否违反唯一性。在交换的情况下,应从缓存中选取另一个实例并具有新的字段值。
所以我建议您创建一个自己的 UniqueConstraint
后代并使用它,除非有人要修补 Grails。
关于交换唯一属性值后 Grails 验证失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6192786/