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

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

Привет, я пытаюсь создать интерфейс, где пользователи могут создавать некоторые перечисления с переводами для разных языков. Например, пользователь может создать перечисление "Жанр фильма". Для этого перечисления может существовать "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')
    }
}

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

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

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

  • переводы для "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

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

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

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

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

    }
}

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

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

1 ответ

Решение

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

Я смотрю на 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.

Другие вопросы по тегам