In this post I will demonstrate one way to use GORM (the Grails ORM) in a desktop application.
Grails is a wonderful web application framework. GORM is one of Grails’ biggest draws for me. However, in order to use Grails with GORM for my projects at work, I really need to be able to use the GORM domain objects from desktop applications as well as the web. I set out to discover how to do this. After many posts on the Grails user mailing list and several frustrating and sleepless nights, I have my first working example.
This example sets up a application that runs a Groovy script that has access to GORM domain classes in the dynamic way that Grails uses them. The domain classes have the id and version fields, as well as the GORM dynamic methods, injected at runtime. I am still not sure that I would want to code an entire Swing application in Groovy, but this is my first step toward being able to use GORM in a desktop application.
In order to follow along with this tutorial you will need to have setup Java (I have only tried Java 5), Groovy (I used 1.0), MySQL (I used 5.0.27-max) and Ant (I used 1.6.5).
Download the Example
The project code will be discussed, however I will not be discussing the libs needed. Just download the example zip file (8.5 MB) to follow along with the tutorial.
The Example Project
Once you have downloaded and extracted the example project you will see that you have the following directories and files:
The src directory as you would expect contains our src files that will be compiled. In this example this contains one file com/jweldin/gormtest/Test.groovy that compiles to com.jweldin.gormtest.Test.class and com.jweldin.gormtest.MyInjectionOperation.class.
The lib directory contains our necessary libraries to build and use the project.
The build.xml file is the ant build file used to build and run the project.
The grails-app directory contains two subdirectories domain and scripts. The grails implementation currently requires all “loaded” groovy files to be located under a directory named grails-app and all domain classes to be located under a directory named domain that is somewhere under the grails-app directory.
The grails-app/scripts directory contains the Main.groovy that is called from our loader class (in this case badly named Test) and would contain any other non-domain groovy scripts.
The grails-app/domain directory contains our domain groovy classes.
Running the Application
The application could be run two ways. You could package the class that loads the domain classes and starts the main script (in this example that class is Test) into a jar alone and run it with the grails-app in the class path. This would allow you to be able to constantly modify the domain groovy files and the called groovy script and then re-run the application without need to actually compile anything. You could also jar up a compiled version of the Test class as well as the grails-app folder containing the un-compiled groovy files and distribute it in a less dynamic form. Perhaps using the first method for development and the second for distribution would be a good idea.
In this example running the build.xml with the default target or the run target builds and runs the application and outputs some simple messages letting you know that the application performed correctly.
If all runs correctly then you should see the following at the end of the ant output:
[java] ----------- [java] Before Main [java] -----------[java] Adding book:The Bible by God [java] Adding book:The Dark Tower: The Gunslinger by Stephen King [java] Adding book:Accounting For Dummies, 3rd Edition by John A. Tracy [java] Adding book:It by Stephen King [java] Adding car:2006 Hyundai Tiburon SOMELONGVINNUMBER [java] Adding car:2001 Dodge Neon SOMEOTHERLONGVINNUMBER [java] Adding person:Jeremie Weldin (Tue Mar 20 02:09:39 EDT 2007) [java] Adding person:John Doe (Sun Dec 25 02:09:39 EST 2005) [java] All of the books: [java] The Dark Tower: The Gunslinger by Stephen King [java] Accounting For Dummies, 3rd Edition by John A. Tracy [java] It by Stephen King [java] All of the cars: [java] 2006 Hyundai Tiburon SOMELONGVINNUMBER [java] 2001 Dodge Neon SOMEOTHERLONGVINNUMBER [java] All of the people: [java] Jeremie Weldin (2007-03-20 02:09:39.0) [java] John Doe (2005-12-25 02:09:39.0) [java] All of the books by Stephen King: [java] The Dark Tower: The Gunslinger by Stephen King [java] It by Stephen King [java] All of the books ordered by name: [java] Accounting For Dummies, 3rd Edition by John A. Tracy [java] It by Stephen King [java] The Dark Tower: The Gunslinger by Stephen King [java] ----------- [java] After Main [java] ----------- [java] Done
You should also see that the tables car, person, book and song have been created in your database.
Next Steps
The next that thing I am going to be working on is getting the GORM classes to be loaded into the classloader used by the initially loaded class and not just in the dynamically loaded groovy script. I have not had to work with classloaders at all until this exercise, but I am hoping it is something relatively easy. I hope to be able to create a class with a static method that would load all of the domain classes, using GORM, for use in the class that calls the method.
** Disclaimer: due to the code being pretty self explanatory I have not gone into detail about the Test.groovy, however I will if someone requests the detail discussion.**
May 26th, 2007 at 6:10 am
Hi Jeremie,
THis is excellent. I have this working and running and is going to make a huge difference for me! I am running into a slight problem though that when I call save(), it doesn’t write to the database. I have tried MySql and MSSQL - i imagine this has to do with a hibernate setting or somehting? The Ids on the object and populated fine, however when I look at the table it is not there.
I have profiled using MSSQL Profiler and the commands are hitting the database, but it doesn’t seem to ’stick’. I thought this might have to do with a hibernate setting or something?
Thanks so much for your help, this is excellent work and is significantly helping my development.
September 27th, 2007 at 12:42 pm
Hi Jeremie
Thanx, very useful for me too, but i have the same problem, data won’t stay in the database. What can i change in my hibernate configuration? Dialect?
December 9th, 2007 at 2:44 pm
John, you may want to try a .save(flush:true). See http://grails.org/doc/1.0.x/ .
January 4th, 2008 at 7:15 am
you must add this in the main
props.put(”hibernate.connection.autocommit”,”true”);
and replace this
props.put(”hibernate.hbm2ddl.auto”,”create-drop”);
for this one
props.put(”hibernate.hbm2ddl.auto”,”update”);
January 8th, 2008 at 9:47 am
I have the same problem using Grails 1.0RC3 and a file-based HSQLDB. save(flush:true) didn’t help, any other ideas?
April 19th, 2008 at 5:11 pm
how to run it? I want to know it details
August 27th, 2008 at 2:14 pm
Hi Jeremie
Thanks for the work. When using an Oracle XE DB the tables get created but then I get the following stack trace… Any ideas why? Does grails/hibernate use an auto-increment key or something unique to MySQL? Is there some hibernate property I am forgetting to set? (groovy-1.5.6 grails-1.0.3)
Dennis
Caused by: java.lang.IncompatibleClassChangeError: Found interface groovy.lang.MetaClassRegistry, but class was expected
at org.codehaus.groovy.grails.commons.metaclass.DynamicMethodsMetaClass.(DynamicMethodsMetaClass.java:70)
at org.codehaus.groovy.grails.commons.metaclass.AbstractDynamicMethods.(AbstractDynamicMethods.java:70)
at org.codehaus.groovy.grails.commons.metaclass.AbstractDynamicMethods.(AbstractDynamicMethods.java:58)
at org.codehaus.groovy.grails.metaclass.DomainClassMethods.(DomainClassMethods.java:49)
at org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsHibernateUtil.configureDynamicMethodsFor(GrailsHibernateUtil.java:103)
at org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsHibernateUtil.configureDynamicMethods(GrailsHibernateUtil.java:65)
September 12th, 2008 at 12:29 pm
I get this exception, tables get created but imediatedly
after:
[java] INFO: schema export complete
i get this:
java.lang.IncompatibleClassChangeError: Found interface groovy.lang.MetaClassRegistry but class was expected
Waht might be the reason?
December 19th, 2008 at 8:56 pm
can you please update on using gorm in the latest groovy/grails versions
groovy-1.5.7
grails-1.0.4
you would save mortal lives
December 22nd, 2008 at 4:31 am
I’ve managed to make it work with those latest versions, what i am facing now in a slightly modified version of your tutorial is:
java.io.FileNotFoundException: class path resource [application.properties] cannot be opened because it does not exist
It loads up all the classes, creates the tables inserts the data, but i am stuck at this error.
December 22nd, 2008 at 4:34 am
“The next that thing I am going to be working on is getting the GORM classes to be loaded into the classloader used by the initially loaded class and not just in the dynamically loaded groovy script” -> this would be also important to know if you managed a way to do it