I trying to use Grails Scaffolding to throw a quick CRUD application together around some legacy database tables. It is an Oracle database, and the primary key value is intended to be populated by Oracle’s GUID() function.
Based on this earlier StackOverflow question, I tried specifying “guid” as the Hibernate generator for this column in my Grails domain class:
...
static mapping = {
table name: "OWNER"
version false
columns {
id column: "OWNER_OID", generator: "guid"
name column: "NAME"
...
}
}
...
When I run my Grails app, viewing and even editing records works just fine. However, when I try to create a new record, things blow up with the Oracle error message “ORA-02289: sequence does not exist“.
I enabled SQL logging for my datasource, and see Grails/Hibernate trying to execute the following during a save operation:
select hibernate_sequence.nextval from dual
This doesn’t look right at all, and doesn’t match the generated SQL from that earlier StackOverflow question linked above. Does anyone see something I am missing here, or otherwise know how to make Grails/Hibernate populate a primary key column with Oracle GUID values?
Whew… after another day of wrestling with this, I think I have my arms around the thing. This answer covers a bit more ground than the original question description, but that’s because I found yet more problems after getting past the Hibernate generator issue.
Issue #1: Getting an Oracle
GUID()valueAs covered by Adam Hawkes’ answer, the “guid” Hibernate generator is unmaintained and only works for older versions of the Oracle dialect.
However, if you use the Hibernate generator “assigned” (meaning that you want to set primary keys manually rather than have Hibernate auto-generate them), then you can insert values pulled from an Oracle
SYS_GUID()call.Even though Hibernate’s newer Oracle dialects don’t support “guid” seamlessly, they still understand the SQL necessary to generate these values. If you are inside of a Controller, you can fetch that SQL query with the following:
If you are inside of a domain class instead, you can still do this… but you will need to first inject a reference to grailsApplication. You probably want to do this in a Controller, though… more on this below.
If you’re curious, the actual String returned here (for Oracle) is:
You can execute this SQL and fetch the generated ID value like this:
Issue #2: Actually using this value in a Grails domain object
To actually use this GUID value in your Grails domain class, you need to use the Hibernate generator “assigned”. As mentioned earlier, this declares that you want to set your own ID’s manually, rather than letting Grails/GORM/Hibernate generate them automatically. Compare this modified code snippet to the one in my original question above:
In my domain class, I changed “guid” to “assigned”. I also found that I needed to eliminate the “
columns {}” grouping block, and move all my column information up a level (weird).Now, in whichever Controller is creating these domain objects… generate a GUID as described above, and plug it into the object’s “
id” field. In a Controller generated automatically by Grails Scaffolding, the function will be “save()“:You might think to try putting this logic directly inside the domain object, in a “
beforeInsert()” function. That would definitely be cleaner and more elegant, but there are some known bugs with Grails that prevent ID’s from being set in “beforeInsert()” properly. Sadly, you’ll have to keep this logic at the Controller level.Issue #3: Make Grails/GORM/Hibernate store this properly
The plain truth is that Grails is primarily intended for virgin-new applications, and its support for legacy databases is pretty spotty (in fairness, though, it’s a bit less spotty than other “dynamic” frameworks I’ve tried). Even if you use the “assigned” generator, Grails sometimes gets confused when it goes to persist the domain object.
One such problem is that a “
.save()” call sometimes tries to do an UPDATE when it should be doing an INSERT. Notice that in the Controller snippet above, I have added “insert: true” as a parameter to the “.save()” call. This tells Grails/GORM/Hibernate explicitly to attempt an INSERT operation rather than an UPDATE one.All of the stars and planets must be in alignment for this to work right. If your domain class “
static mapping {}” block does not set the Hibernate generator to “assigned“, and also set “version false“, then Grails/GORM/Hibernate will still get confused and try to issue an UPDATE rather than an INSERT.If you are using auto-generated Grails Scaffolding controllers, then it is safe to use “
insert: true” in the Controller’s “save()” function, because that function in only called when saving a new object for the first time. When a user edits an existing object, the Controller’s “update()” function is used instead. However, if you are doing your own thing in your own custom code somewhere… it will be important to check on whether a domain object is already in the the database before you make a “.save()” call, and only pass the “insert: true” parameter if it really is a first-time insert.Issue #4: Using natural keys with Grails/GORM/Hibernate
One final note, not having to do with Oracle GUID values, but related to these Grails issues in general. Let’s say that in a legacy database (such as the one I’ve been dealing with), some of your tables use a natural key as their primary key. Say you have an
OWNER_TYPEtable, containing all the possible “types” ofOWNER, and theNAMEcolumn is both the human-readable identifier as well as the primary key.You’ll have to do a couple of other things to make this work with Grails Scaffolding. For one thing, the auto-generated Views do not show the ID field on the screen when users are creating new objects. You will have to insert some HTML to the relevant View to add a field for the ID. If you give the field a name of “
id“, then the auto-generated Controller’s “save()” function will receive this value as “params.id“.Secondly, you have to make sure that the auto-generated Controller’s “
save()” function properly inserts the ID value. When first generated, a “save()” starts off by instantiating a domain object from the CGI parameters passed by the View:However, this does not handle the ID field you added to your View. You will still need to set that manually. If on the View you gave the HTML field a name of “
id“, then it will be available in “save()” as “params.id“:Piece of cake, huh? Perhaps “Issue #5” is figuring out why you put yourself through all this pain, rather than just writing your CRUD interface by hand with Spring Web MVC (or even vanilla JSP’s) in the first place! 🙂