Невозможно изменить значение свойства класса домена в Grails
Я разрабатываю приложение Grails 2.3.7, и у меня возникают проблемы при изменении свойства домена с помощью поля выбора. Каждый раз, когда я пытаюсь изменить свойство и сохранить, я получаю HibernateException: identifier of an instance of Ethnicity was altered from X to Y
, Я не хочу менять ID этнической принадлежности, я просто хочу изменить ApplicationPerson
Этническая принадлежность от одного к другому.
Несколько вещей, на которые стоит обратить внимание:
- Я использую то же действие контроллера для создания и обновления человека.
- настройка
personInstance.ethnicity
вnull
прямо передpersonInstance.properties = params
сработает сохранение, но я не знаю почему, и я не хочу делать это для каждой ассоциации, которую я хочу изменить. - Я понимаю, что модель предметной области кажется странной. Это устаревшая БД, которую я не могу изменить.
Вот мои классы домена:
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, если это возможно, потому что это будут основные данные и хороший кандидат на кэширование.