When ... after another day of dealing with it, I think I hugged it. This answer contains a little more reason than the original description of the question, but this is because I found even more problems after I passed the problems with the Hibernate generator.
Problem # 1: Getting Oracle GUID()
Value
As Adam Hawks said in a reply, the Hibernate “hibernator” generator is not supported and only works for older versions of the Oracle dialect.
However, if you use the “assigned” Hibernate generator (which means that you want to manually set the primary keys and not automatically generate Hibernate), you can insert values ​​pulled from the Oracle SYS_GUID()
call.
Despite the fact that Hibernate dialects for newer Oracle do not support "guid", they still understand the SQL necessary to generate these values. If you are inside the controller, you can receive this SQL query with the following:
String guidSQL = grailsApplication.getMainContext().sessionFactory.getDialect().getSelectGUIDString()
If you are inside a domain class, you can still do this ... but you need to first enter a link to grailsApplication. You might want to do this in the controller, though ... more on this below.
If you're interested, the actual string returned here (for Oracle) is as follows:
select rawtohex(sys_guid()) from dual
You can execute this SQL and get the value of the generated identifier as follows:
String guid = grailsApplication.getMainContext().sessionFactory.currentSession.createSQLQuery(guidSQL).list().get(0)
Problem # 2: Actually, using this value in a Grails domain object
To use this GUID value in your Grails domain class, you need to use the "assigned" Hibernate generator. As mentioned earlier, this states that you want to set your own identifier manually, rather than letting Grails / GORM / Hibernate generate them automatically. Compare this modified piece of code with the one in my original question above:
... static mapping = { table name: "OWNER" version false id column: "OWNER_OID", generator: "assigned" name column: "NAME" ... } ...
In my domain class, I changed "guid" to "assigned". I also found that I needed to eliminate the " columns {}
" grouping block and move all the information about my column to the (weird) level.
Now, depending on which controller creates these domain objects ... it will generate a GUID, as described above, and connect it to the " id
" field. In a controller automatically created by Grails Scaffolding, the function will be " 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) }
You might be trying to apply this logic directly to a domain object in the " beforeInsert()
" function. It will definitely be cleaner and more elegant, but there are some known errors with Grails that prevent the identifier from being set to " beforeInsert()
". Unfortunately, you will have to adhere to this logic at the controller level.
Problem # 3: Make Grails / GORM / Hibernate Properly Saved
The simple truth is that Grails is primarily intended for new applications, and its support for legacy databases is quite spotty (fairness, however, it is slightly less spotty than other "dynamic" frameworks I tried). Even if you use a “designated” generator, Grails is sometimes confused when it continues to hold a domain object.
One of these problems is that the " .save()
" call sometimes tries to UPDATE when it needs to do an INSERT. Note that in the Controller snippet above, I added " insert: true
" as the parameter for calling " .save()
". This suggests that Grails / GORM / Hibernate is clearly trying to perform an INSERT operation, not an UPDATE.
All stars and planets must be in order for this to work correctly. If your domain class " static mapping {}
" does not set the Hibernate generator to " assigned
" and also sets " version false
", then Grails / GORM / Hibernate will still get confused and try to return UPDATE, not INSERT.
If you use Grails Scaffolding auto-generated controllers, then it is safe to use " insert: true
" in the controller's save()
function, since this function is called only when the new object is first saved. When the user edits an existing object, the update()
function is used instead "controller. However, if you are doing your own thing in your own code somewhere ... it will be important to check if the domain object is already in the database before you make a call to " .save()
", and only pass " insert: true
"if this is really the first insert.
Problem # 4: Using Natural Keys with Grails / GORM / Hibernate
A final note, not related to Oracle GUID values, but related to these Grails issues in general. Let's say that in the old database (for example, the one I came across) some of your tables use a natural key as the main key. Let's say that you have an OWNER_TYPE
table containing all the possible "types" of OWNER
, and the NAME
column is both an identifier read by a person and a primary key.
You will need to do a couple of other things to make this work with Grails Scaffolding. First, automatically generated views do not display an identifier field on the screen when users create new objects. You will need to insert some HTML in the corresponding View to add a field for the ID. If you specify the name " id
" in the field, the automatically created function "Save ()" of the controller will receive this value as " params.id
".
Secondly, you must make sure that the save()
auto-generator function correctly inserts the ID value. At the first generation, "save ()" starts by creating an instance of the domain object from the CGI parameters passed in the view:
def ownerTypeInstance = new OwnerType.get( params )
However, this does not process the identifier field that you added to the view. You still have to install it manually. If in the view you gave the HTML field the name " id
", then it will be available in " save()
" as " params.id
":
... ownerTypeInstance = new OwnerType() ownerTypeInstance.id = params.id
A piece of cake, huh? Perhaps "Problem # 5" finds out why you put yourself through all this pain, and not just write your CRUD interface manually using Spring Web MVC (or even vanilla JSP) in the first place! :)