2

一意の属性値を交換した後、Grails の検証が失敗する

こんにちは、ユーザーがさまざまな言語の翻訳を使用してカスタム列挙を作成できるインターフェイスを作成しようとしています。たとえば、ユーザーは「映画のジャンル」という列挙を作成できます。この列挙には、複数の言語用に 1 つ以上の列挙値翻訳が存在する可能性がある列挙値「Comedy」が存在する可能性があります。

特定の言語の翻訳は 1 つだけである必要があるため、enumeration-value-translation ドメイン クラスに一意の制約を追加しました。これらは現在私のドメインクラスです:

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')
    }
}

これはこれまでのところかなりうまくいきます。言語が入れ替わる方法で同じ列挙値の 2 つの列挙値翻訳を更新すると、問題が発生します。たとえば、私は

  • 列挙値: 「コメディ」

言語が「偶然に」混同されているいくつかの翻訳

  • コメディー
    • 言語: ドイツ語、値: "コメディ"
    • 言語: 英語、値 "Komödie"

ユーザーが言語を混同したことに気付いた場合、言語を交換して列挙を再度保存することをお勧めします。そして、これが私のエラーが発生する場所です。言語を交換した後、enumeration-value-translations の一意の制約が 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でそれらをフェッチし、言語を更新しただけです-最小限の例で試してみたところ、そこで機能しました...すべての列挙値と列挙値の翻訳のディープコピーを作成して保存するだけでも機能します代わりに(つまり、検証が実際に失敗するべきではないことを意味します)、しかし、これは実際に行うべき方法ではないと思います...

もう 1 つの奇妙な点は、データを反復処理した場合にのみ検証が失敗することです。データにまったく触れない場合、エラーは発生しませんが、データも保存されません。つまり、次の行によって検証がまったく評価されます。

enumeration.enumerationValues.each() { ev ->
    ev.enumerationValueTranslations.each() { evt ->

    }
}

だからこそ、重要な問題があるに違いないと強く信じています...他に知っておくべきことがあれば教えてください。

助けてくれてありがとう

4

1 に答える 1

3

もう一度試してみましょう:)

私は を見て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;
    }

取得したものを反復し、フィールド値が依然として一意性に違反していることresultsを確認する必要があります。交換の場合、他のインスタンスはキャッシュから選択され、新しいフィールド値を持つ必要があります。

UniqueConstraintしたがって、Grails にパッチを適用する人がいない限り、独自の子孫を作成して使用することをお勧めします。

于 2011-06-01T08:03:53.203 に答える