使用Oracle的GUID() - 在Grails / Hibernate中生成ID

时间:2012-02-09 19:09:36

标签: oracle hibernate grails

我尝试使用Grails Scaffolding将一个快速CRUD应用程序放在一些遗留数据库表周围。它是一个Oracle数据库,主键值旨在由Oracle的GUID()函数填充。

基于this earlier StackOverflow question,我尝试在Grails域类中为此列指定“guid”作为Hibernate生成器:

...
static mapping = {
    table name: "OWNER"
    version false
    columns {
        id column: "OWNER_OID", generator: "guid"
        name column: "NAME"
        ...
    }
}
...

当我运行我的Grails应用程序时,查看甚至编辑记录的工作正常。但是,当我尝试创建新记录时,事情会因Oracle错误消息“ORA-02289: sequence does not exist”而爆炸。

我为我的数据源启用了SQL日志记录,并看到Grails / Hibernate尝试在保存操作期间执行以下操作:

select hibernate_sequence.nextval from dual

这看起来并不正确,并且与上面链接的早期StackOverflow问题中生成的SQL不匹配。有没有人看到我在这里缺少的东西,或者知道如何让Grails / Hibernate用Oracle GUID值填充主键列?

2 个答案:

答案 0 :(得分:1)

哇...经过另一天的摔跤之后,我想我搂着这个东西。这个答案比原来的问题描述要多一些,但那是因为我在遇到Hibernate生成器问题后发现了更多的问题。

问题#1:获取Oracle GUID()

正如亚当霍克斯所述['回答," guid" Hibernate生成器是不受维护的,仅适用于旧版本的Oracle方言。

但是,如果您使用Hibernate生成器"已分配" (意味着您要手动设置主键而不是让Hibernate自动生成它们),然后您可以插入从Oracle SYS_GUID()调用中提取的值。

即使Hibernate的新甲骨文方言不支持" guid"无缝地,他们仍然了解生成这些值所必需的SQL。如果您位于Controller内部,则可以使用以下命令获取该SQL查询:

String guidSQL = grailsApplication.getMainContext().sessionFactory.getDialect().getSelectGUIDString()

如果您在域类中,您仍然可以执行此操作...但您需要首先注入对grailsApplication的引用。您可能希望在Controller中执行此操作,但更多信息如下所示。

如果您有点好奇,这里返回的实际字符串(对于Oracle)是:

select rawtohex(sys_guid()) from dual

您可以执行此SQL并获取生成的ID值,如下所示:

String guid = grailsApplication.getMainContext().sessionFactory.currentSession.createSQLQuery(guidSQL).list().get(0)

问题#2:实际上在Grails域对象中使用此值

要在Grails域类中实际使用此GUID值,您需要使用Hibernate生成器"已分配"。如前所述,这声明您希望手动设置自己的ID,而不是让Grails / GORM / Hibernate自动生成它们。将此修改后的代码段与上述原始问题中的代码段进行比较:

...
static mapping = {
    table name: "OWNER"
    version false
    id column: "OWNER_OID", generator: "assigned"
    name column: "NAME"
    ...
}
...

在我的域名课程中,我改变了#gu;"分配到#34;。我还发现我需要消除" columns {}"分组块,并将我的所有列信息上移一个级别(奇怪)。

现在,在创建这些域对象的控制器中......如上所述生成GUID,并将其插入对象" id"领域。在由Grails脚手架自动生成的控制器中,该功能将为" 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错误阻止ID被设置在" beforeInsert()"正常。遗憾的是,您必须将此逻辑保持在控制器级别。

问题#3:使Grails / GORM / Hibernate正确存储

显而易见的事实是,Grails主要用于处理新的应用程序,并且它对遗留数据库的支持非常不稳定(在公平性方面,它比其他"动态"框架我试过)。即使您使用"已分配"生成器,Grails有时会在持久保存域对象时感到困惑。

一个问题是" .save()"当它应该执行INSERT时,调用有时会尝试执行UPDATE。请注意,在上面的Controller代码段中,我添加了" insert: true"作为" .save()"的参数呼叫。这告诉Grails / GORM / Hibernate显式尝试INSERT操作而不是UPDATE操作。

所有的恒星和行星必须齐心协力才能正常工作。如果您的域类" static mapping {}"块没有将Hibernate生成器设置为" assigned",并且还设置" version false",然后Grails / GORM / Hibernate仍然会混淆尝试发出UPDATE而不是INSERT。

如果您使用自动生成的Grails脚手架控制器,那么使用" insert: true"是安全的。在控制器" save()"函数,因为该函数仅在第一次保存新对象时才被调用。当用户编辑现有对象时,控制器" update()"而是使用函数。但是,如果您在自己的自定义代码中使用自己的东西...在制作" .save()&#之前检查域对象是否已经在数据库中是很重要的。 34;打电话,只传递" insert: true"参数,如果它确实是第一次插入。

问题#4:使用带有Grails / GORM / Hibernate的自然键

最后一点,不是与Oracle GUID值有关,而是与这些Grails问题有关。让我们说在遗留数据库(例如我正在处理的数据库)中,你的一些表使用自然键作为主键。假设您有一个OWNER_TYPE表,其中包含所有可能的"类型" OWNER的{​​{1}}列,NAME列既是人类可读的标识符,也是主键。

你必须做其他一些事情来使用Grails脚手架。首先,当用户创建新对象时,自动生成的视图不会在屏幕上显示ID字段。您必须在相关视图中插入一些HTML才能为ID添加字段。如果您为该字段命名为" id",则自动生成的控制器"保存()"函数将收到此值为" params.id"。

其次,您必须确保自动生成的控制器" save()"函数正确插入ID值。首次生成时," save()"通过从View:

传递的CGI参数中实例化域对象开始
def ownerTypeInstance = new OwnerType.get( params )

但是,这不会处理您添加到View中的ID字段。您仍然需要手动设置。如果您在视图中为HTML字段指定了" id",那么它将在" save()"中提供。 as" params.id":

...
ownerTypeInstance = new OwnerType()
ownerTypeInstance.id = params.id 
// Proceed to the ".save()" step, making sure to pass "insert: true"
...

一块蛋糕,对吧?也许"问题#5"正在弄清楚为什么要让自己完成所有这些痛苦,而不是仅仅使用Spring Web MVC(甚至是vanilla JSP' s)手工编写CRUD界面! :)

答案 1 :(得分:0)

使用SYS_GUID()的支持取决于您使用的Oracle方言。查看hibernate source on GitHub,方言似乎只设置为使用Oracle9Dialect.javaOracle8iDialect.java中的Oracle生成的guid。因此,它不适用于9i或10g方言。

你应该向hibernate提交一个补丁,它将添加所需的功能,以启用与其他方言相同的功能。