- Introduction
NOTE: I got a lot of the information for this post from the "Advanced GORM kung fu" chapter of the Grails in Action book from Manning. I highly recommend this book.
- Environment
After you have installed the Oracle XE database: 1) enable the hr user and 2) change it's password.
1) ALTER USER hr ACCOUNT UNLOCK;
2) ALTER USER hr IDENTIFIED BY your_hr_password;
Verify that you can successfully connect to the hr database with you new password via sqlplus, SQL Developer or some other tool.
Here is a diagram of the hr database:

- Create the Application
- Create the grails application:
grails create-app hrReports - Copy the Oracle JDBC driver ojdbc14.jar to hrReports/lib. The Oracle JDBC Driver can be copied from $ORACLE_HOME/jdbc/lib or downloaded from the Oracle site.
- Create the grails domain classes Region, Country and Location:
grails create-domain-class Region
grails create-domain-class Country - Create the grails controller Region, Country and Location:
grails create-controller Region
grails create-controller Country
- Configure our DataSource
development {
dataSource {
driverClassName = "oracle.jdbc.OracleDriver"
url = "jdbc:oracle:thin@localhost:1521:xe
username = "hr"
password = "your_hr_password"
}
}
- Configure the Scaffolding
def scaffolding = true
Our front end development is complete.
- Create the Region Domain Object
Integer regionId
String regionName
The constraints are:
static constraints = {
regionId(blank:false, unique:true)
regionName(nullable:true, maxSize(25)
}
Since the Region Domain Class is mapped to the REGIONS database table, we need to add the following mapping:
static mapping = {
table 'REGIONS'
}
REGION_ID is the primary key and it is assigned by the user and not by a sequence or some other method. Additionally, hibernate expects the primary key to be named id. Hibernate also uses a field named version (which the DBA will not allow us to add to our legacy database) for optimistic locking. We address both of these issues by adding the following to our mapping closure:
version false
id generator:'assigned', column:'REGION_ID', type:'integer'
Now comes the funky part; dealing with a natural key as a primary key. We want to refer to the primary key as regionId in our groovy code and we also want the field to appear in the grails generated scafolding. We can accomplish this by changing the variable name from regionId to id (so hibernate will work) and create a transient variable named regionId. We create a getter and setter for regionId to tie it to the id field:
Integer idThis change allows hibernate to work (it has an id), the code to reference regionID and the mapping from the GORM DSL to Database columns to work as expected. If we are inserting a new record with this approach, we need to user the save(insert:true) since a non-null id indicates a persisten instance.
//regionId is the primary key
static transients = ['regionId']
void setRegionId(Integer regionid) {
id = regionId
}
void getRegionid() {
return id
}
Pull it all together and our Region.groovy Domain Class looks like this:
class Region {
Integer id
// regionId is the primary key
static transients = ['regionId']
void setRegionId(Integer regionId) {
id = regionId
}
Integer getRegionId() {
return id
}
String regionName
static constraints = {
id(blank:false, unique:true)
regionName(nullable:true, maxSize:25)
}
static mapping = {
table 'REGIONS'
version false
id generator:'assigned', column:'REGION_ID', type:'integer'
}
String toString() {
"$regionName"
}
}
- Create the Country Domain Object
- The primary key is a String and not an Integer
- There is a one-to-many relationship that is represented by the region field.
class Country {
String id
// countryId is the primary key
static transients = ['countryId']
void setCountryId(String countryId) {
id = countryId
}
String getCountryId() {
return id
}
String countryName
Region region
static constraints = {
id(blank:false, unique:true, maxSize:2)
countryName(nullable:true, maxSize:40)
region(nullable:true)
}
static mapping = {
table 'COUNTRIES'
version false
id column:'COUNTRY_ID', generator:'assigned', type:'string'
}
String toString() {
"$countryName"
}
}
- Conclusions
- If you made it this far, you really do care about GORM legacy DB mappings.
- It takes longer to write this blog post than to figure out and write the CRUD application using the GORM DSL mappings.
- It does seem to be possible to quickly map legacy databases with GORM DSL.
- This was completed in a couple of hours and in 224 LOC (not counting Unit Tests). I think the next times will be much quicker.
- Next Steps
9 comments: