Вот так ... после еще одного дня борьбы с этим, я думаю, что я обнимаю вещь.Этот ответ охватывает гораздо больше вопросов, чем первоначальное описание вопроса, но это потому, что я обнаружил еще больше проблем после преодоления проблемы с генератором Hibernate.
Проблема № 1: Получение значения Oracle GUID()
Как указано в ответе Адама Хоукса, генератор Hibernate "guid" не поддерживается и работает только для более старых версий диалекта Oracle.
Однако, если вы используете генератор Hibernate "назначенный" (это означает, что выесли вы хотите установить первичные ключи вручную, а не автоматически генерировать их в Hibernate), то вы можете вставить значения, извлеченные из вызова Oracle SYS_GUID()
.
Даже если более новые диалекты Oracle Hibernate не поддерживают «guid» без проблемони все еще понимают SQL, необходимый для генерации этих значений.Если вы находитесь внутри контроллера, вы можете получить этот SQL-запрос следующим образом:
String guidSQL = grailsApplication.getMainContext().sessionFactory.getDialect().getSelectGUIDString()
Если вы находитесь внутри класса домена, вы все равно можете сделать это ... но высначала нужно будет добавить ссылку на приложение grails.Возможно, вы захотите сделать это в контроллере, хотя ... подробнее об этом ниже.
Если вам интересно, фактическая строка, возвращаемая здесь (для Oracle):
select rawtohex(sys_guid()) from dual
Вы можете выполнить этот SQL-запрос и получить сгенерированное значение идентификатора, например:
String guid = grailsApplication.getMainContext().sessionFactory.currentSession.createSQLQuery(guidSQL).list().get(0)
Проблема № 2: Фактически использование этого значения в объекте домена Grails
Для фактического использования этогоЗначение GUID в вашем доменном классе Grails, вам нужно использовать генератор Hibernate «назначен».Как упоминалось ранее, это объявляет, что вы хотите установить свои собственные идентификаторы вручную, а не позволять Grails / GORM / Hibernate генерировать их автоматически.Сравните этот измененный фрагмент кода с приведенным в моем исходном вопросе выше:
...
static mapping = {
table name: "OWNER"
version false
id column: "OWNER_OID", generator: "assigned"
name column: "NAME"
...
}
...
В своем классе домена я изменил «guid» на «назначенный».Я также обнаружил, что мне нужно исключить блок группировки "columns {}
" и переместить всю информацию о моем столбце на уровень (странный).
Теперь, в зависимости от того, какой контроллер создает эти объекты домена ... генерироватьGUID, как описано выше, и вставьте его в поле «id
» объекта.В контроллере, автоматически сгенерированном Grails Scaffolding, эта функция будет иметь вид "save()
":
def save() {
def ownerInstance = new Owner(params)
String guidSQL = grailsApplication.getMainContext().sessionFactory.getDialect().getSelectGUIDString()
ownerInstance.id = grailsApplication.getMainContext().sessionFactory.currentSession.createSQLQuery(guidSQL).list().get(0)
if (!ownerInstance.save(flush: true, insert: true)) {
render(view: "create", model: [ownerInstance: ownerInstance])
return
}
flash.message = message(code: 'default.created.message', args: [message(code: 'owner.label', default: 'Owner'), ownerInstance.id])
redirect(action: "show", id: ownerInstance.id)
}
Вы можете попробовать поместить эту логику непосредственно в объект домена, в функцию "beforeInsert()
",Это определенно было бы чище и элегантнее, но есть некоторые известные ошибки с Grails, которые не позволяют правильно устанавливать идентификаторы в "beforeInsert()
".К сожалению, вам придется сохранить эту логику на уровне контроллера.
Проблема № 3: Заставьте Grails / GORM / Hibernate хранить это правильно
Простая истина в том, что Grails в первую очередь предназначен дляДевственные новые приложения, и его поддержка унаследованных баз данных довольно нечеткая (хотя, если честно, , она немного менее пятнистая, чем другие "динамические" фреймворки, которые я пробовал ).Даже если вы используете «назначенный» генератор, Grails иногда запутывается, когда пытается сохранить объект домена.
Одна из таких проблем заключается в том, что вызов «.save()
» иногда пытается выполнить UPDATE, когда он долженделать вставку.Обратите внимание, что в приведенном выше фрагменте кода контроллера я добавил «insert: true
» в качестве параметра к вызову «.save()
».Это говорит Grails / GORM / Hibernate в явном виде о попытке выполнить операцию INSERT, а не операцию UPDATE.
Все звезды и планеты должны быть выровнены для правильной работы.Если блок класса static mapping {}
вашего домена не устанавливает генератор Hibernate на «assigned
», а также устанавливает «version false
», то Grails / GORM / Hibernate все равно будет сбит с толку и попытается выполнить ОБНОВЛЕНИЕ вместоВСТАВКА.
Если вы используете автоматически сгенерированные контроллеры Grails Scaffolding, тогда безопасно использовать «insert: true
» в функции «save()
» контроллера, потому что эта функция вызывается только при первом сохранении нового объекта,Когда пользователь редактирует существующий объект, вместо него используется функция контроллера «update()
».Тем не менее, если вы делаете что-то свое в своем собственном пользовательском коде где-то ... будет важно проверить, есть ли объект домена уже в базе данных, прежде чем делать вызов ".save()
", и передавать толькоПараметр «insert: true
», если это действительно первая вставка.
Проблема № 4: Использование естественных ключей с Grails / GORM / Hibernate
Последнее замечание, не относящееся кЗначения Oracle GUID, но связанные с этими проблемами Grails в целом.Предположим, что в устаревшей базе данных (например, той, с которой я имел дело) некоторые из ваших таблиц используют естественный ключ в качестве своего первичного ключа.Допустим, у вас есть таблица OWNER_TYPE
, содержащая все возможные «типы» OWNER
, а столбец NAME
является как понятным человеку идентификатором, так и первичным ключом.
Вы будетенужно сделать несколько других вещей, чтобы сделать эту работу с Grails Scaffolding.Во-первых, автоматически генерируемые виды не отображают поле идентификатора на экране, когда пользователи создают новые объекты.Вам нужно будет вставить некоторый HTML-код в соответствующее представление, чтобы добавить поле для идентификатора.Если вы дадите полю имя «id
», то функция «save ()» автоматически сгенерированного контроллера получит это значение как «params.id
».
Во-вторых, вы должны убедиться, чточто автоматически сгенерированная функция контроллера "save()
" вставляет значение идентификатора.При первом генерировании «save ()» начинается с создания экземпляра объекта домена из параметров CGI, передаваемых представлением:
def ownerTypeInstance = new OwnerType.get( params )
Однако это не обрабатывает поле идентификатора, добавленное вами в представление.Вам все еще нужно будет установить это вручную.Если в представлении вы задали для поля HTML имя «id
», то оно будет доступно в «save()
» как «params.id
»:
...
ownerTypeInstance = new OwnerType()
ownerTypeInstance.id = params.id
// Proceed to the ".save()" step, making sure to pass "insert: true"
...
Кусок торта, да?Возможно, «Проблема № 5» выясняет, почему вы сами переживаете всю эту боль, а не просто пишете свой CRUD-интерфейс вручную с Spring Web MVC (или даже с ванильным JSP) в первую очередь!:)