Сбой проверки Grails после обмена уникальными значениями атрибутов - PullRequest
2 голосов
/ 31 мая 2011

Сбой проверки Grails после обмена уникальными значениями атрибута

Привет, я пытаюсь создать интерфейс, в котором пользователи могут создавать некоторые пользовательские перечисления с переводами для разных языков. Например, пользователь может создать перечисление «Жанр фильма». Для этого перечисления может существовать перечисление-значение "Комедия", для которого может существовать один или несколько перечислений-значений-переводов для нескольких языков.

Поскольку для определенного языка должен быть только один перевод, я добавил уникальное ограничение в класс домена перечисления-значения-перевода. Это мои доменные классы прямо сейчас:

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

Пока это работает довольно хорошо. Моя проблема возникает, когда я обновляю два перечисления-значения-значения одного и того же значения-перечисления способом, которым обмениваются языки. Например, у меня есть

  • перечисление-значение: "Комедия"

и некоторые переводы, где язык "случайно" перепутан

  • переводы для "Comedy"
    • язык: немецкий, значение: "комедия"
    • язык: английский, значение "Komödie"

если пользователь узнает, что он перепутал язык, он может захотеть поменять языки и снова сохранить перечисление. И именно здесь происходит моя ошибка, потому что после замены языков уникальное ограничение перечисления-значения-трансляций становится ложным.

Чтобы отладить это, я просто попытался распечатать ошибку, вызывающую переводы до и после обработки параметров, поэтому:

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

в этом состоянии я не удалил, или сохранил (или каким-либо образом очищен) что угодно - это всего лишь результат после изменения объектов. И, как вы можете видеть, в реальных данных нет несоответствий, и проверка не должна завершиться неудачей.

Может ли быть ошибка в том, как я меняю переводы? Я просто выбрал их по идентификатору и просто обновил язык - я попробовал это на минималистичном примере, и это сработало там ... Это также работает, если я просто создаю глубокую копию всех перечислений-значений и перечислений-значений-преобразований и сохраняю их вместо этого (что означает, что проверка действительно не должна завершиться ошибкой), но я думаю, что это действительно не так, как должно быть сделано ...

Другая странная вещь заключается в том, что валидация завершается неудачей, только если я перебираю данные Если я вообще не касаюсь данных, ошибки не возникает, но данные также не сохраняются, а это означает, что следующие строки приводят к тому, что проверки проверяются вообще:

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

    }
}

, поэтому я твердо верю, что должна быть какая-то нетривиальная проблема ... пожалуйста, дайте мне знать, если есть что-то еще, что вам нужно знать.

спасибо за любую помощь

1 Ответ

3 голосов
/ 01 июня 2011

Позвольте мне сделать еще одну попытку:)

Я смотрю на 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 и проверять, что значение поля STILL нарушает уникальность.В случае обмена другой экземпляр должен быть выбран из кэша и иметь новое значение поля.

Поэтому я бы предложил вам создать собственного потомка UniqueConstraint и использовать его, если никто не собирается патчитьGrails.

...