Невозможно изменить значение свойства класса домена в Grails

Я разрабатываю приложение Grails 2.3.7, и у меня возникают проблемы при изменении свойства домена с помощью поля выбора. Каждый раз, когда я пытаюсь изменить свойство и сохранить, я получаю HibernateException: identifier of an instance of Ethnicity was altered from X to Y, Я не хочу менять ID этнической принадлежности, я просто хочу изменить ApplicationPersonЭтническая принадлежность от одного к другому.

Несколько вещей, на которые стоит обратить внимание:

  1. Я использую то же действие контроллера для создания и обновления человека.
  2. настройка personInstance.ethnicity в null прямо перед personInstance.properties = params сработает сохранение, но я не знаю почему, и я не хочу делать это для каждой ассоциации, которую я хочу изменить.
  3. Я понимаю, что модель предметной области кажется странной. Это устаревшая БД, которую я не могу изменить.

Вот мои классы домена:

class ApplicationPerson implements Serializable {
    Integer appId
    Integer applicationSequenceNumber
    String firstName
    String lastName
    Ethnicity ethnicity

    static mapping = {
        id composite: ['appId', 'applicationSequenceNumber'],
           generator: 'assigned'
    }
}

class Ethnicity {
    String code
    String description

    static mapping = {
        id name: 'code', generator: 'assigned'
    }
}

Вот мой _form.gsp обновить Ethnicity (Я удалил все остальные свойства, которые сохраняются просто отлично):

<div class="fieldcontain ${hasErrors(bean: personInstance, 
                                     field: 'ethnicity', 'error')} ">
    <label for="ethnicity">Ethnicity</label>
    <g:select id="ethnicity" 
              name="ethnicity.code" 
              from="${Ethnicity.list()}" 
              optionKey="code" 
              value="${personInstance?.ethnicity?.code}" />
</div>

И, наконец, мое действие контроллера, которое форма отправляет:

def save() {
    Application app = applicationService.getCurrentApplication()

    // Find/Create and save Person
    ApplicationPerson personInstance = app.person
    if (!personInstance) {
        personInstance = 
            new ApplicationPerson(appId: app.id, 
                                  applicationSequenceNumber: app.sequenceNumber)
    }
    personInstance.properties = params

    if (!personInstance.validate()) {
        respond personInstance.errors, view:'edit'
        return
    }

    personInstance.save flush:true
    redirect action: 'list'
}

1 ответ

Решение

Изменить имя в select элемент из ethnicity.code в personInstance.etnicity.code как показано ниже:

<g:select id="ethnicity" 
          name="personInstance.ethnicity.code" 
          from="${Ethnicity.list()}" 
          optionKey="code" 
          value="${personInstance?.ethnicity?.code}" />

Имя выбранной опции привязывается к params в качестве ключа и выбранное значение в качестве value против ключа. С помощью ethnicity.code будет пытаться изменить первичный ключ существующего ethnicity вместо того, чтобы изменить этническую принадлежность человека приложения.

ОБНОВИТЬ
Указанное выше изменение имени не является обязательным (может использоваться, если вам не нужно params быть назначенным как свойства класса домена). Прежнее название ethnicity.code должно работать так же, но нижеприведенные изменения также необходимы в действии контроллера для установки этнической принадлежности:

//or use params.ethnicity.code
//if name is ethnicity.code
person.ethnicity = Ethnicity.load(params.personInstance.ethnicity.code)

//or use params if name is ethnicity.code
person.properties = params.personInstance

Загрузить существующий Ethnicity на основании переданного в code а затем установить его в person перед настройкой свойств.

Проблема заключается в code быть первичным ключом Ethnicity, Если Ethnicity имел отдельный столбец идентификаторов (например, по умолчанию Long id) тогда ваша точная реализация будет работать с помощью привязки данных. Но поскольку упоминается, что вы работаете с устаревшей базой данных, я полагаю, что вы не сможете изменить таблицы, добавив еще один столбец для id, Таким образом, ваш лучший выбор будет load(Дешевый по сравнению с получением, так как строка загружается из кэша гибернации).

Вы также можете попробовать попробовать кэшировать домен Ethnicity, если это возможно, потому что это будут основные данные и хороший кандидат на кэширование.

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