GORM - Mapping DSL
Since Grails 1.0, Grails domain classes can be mapped onto many legacy schemas via an ORM DSL. This page takes you through what is possible with the ORM DSL.
None of this is necessary if you are happy to stick to the conventions defined by GORM for table, column names and so on. You only need this functionality if you need to in anyway tailor the way GORM maps onto legacy schemas or performs caching
Customising the table name
First to get started you need to define a static 'mapping' closure to your domain class:
class Person {
..
static mapping = { }
}
Now to customise the table name use the table call:
class Person {
..
static mapping = {
table 'people'
}
}Configuring the classes cache strategy
Hibernate features a second-level cache with a customizable cache provider. This needs to be configured in the grails-app/conf/DataSource.groovy file as follows:
hibernate {
cache.use_second_level_cache=true
cache.use_query_cache=true
cache.provider_class='org.hibernate.cache.EhCacheProvider'
}
You can of course customise these settings how you desire, for example if you want to use a distributed caching mechanism.
Now in your {{mapping}} closure to enable caching with the default settings do this:
class Person {
..
static mapping = {
table 'people'
cache true
}
}
This will configure a 'read-write' cache that includes both lazy and non-lazy properties. If you need to customise this further you can do:
class Person {
..
static mapping = {
table 'people'
cache usage:'read-only', include:'non-lazy'
}
}
The available cache usages are 'read-only', 'read-write', 'transactional' and 'nonstrict-read-write'. For a description of each refer to the
hibernate docs on the subject
Configuring the class' inheritance strategy
By default GORM classes uses table-per-hierarchy inheritance mapping. This has the disadvantage that columns cannot have a NOT-NULL constraint
applied to them at the db level. If you would prefer to use a table-per-subclass inheritance strategy you can do so as follows:
class Payment {
Long id
Long version
Integer amount static mapping = {
tablePerHierarchy false
}
}
class CreditCardPayment extends Payment {
String cardNumber
}
The mapping of the root {{Payment}} class specifies that it will not be using table-per-hierarchy mapping.
Enabling/Disabling Optimistic Locking (Versioning)
By default GORM domain classes are enabled for optimistic locking via a special 'version' column. You can disable this feature by doing the following:
class Person {
..
static mapping = {
table 'people'
version false
}
}
Note that if you disable optimistic locking you are essentially on your own with regards to concurrent updates and are open to the risk of users losing (due to data overriding) data unless you use pessimistic locking
Customising the id generation strategy
You can customise how Grails generates ids for the database using the DSL. By default Grails relies on the native database mechanism for generating ids. This is by far the best approach, but there are still many schemas that have different approaches to identity.
To deal with this Hibernate defines the concept of an id generator. You can customise this generator and the column is maps to as follows:
class Person {
..
static mapping = {
table 'people'
version false
id generator:'hilo', params:[table:'hi_value',column:'next_value',max_lo:100]
}
}
In this case we're using one of Hibernate's built in 'hilo' generators that uses a separate table to generate ids. There are many different generators that can be configured, refer to the
hibernate docs on the subject for more.
Note that if you want to merely customise the column that the id lives on you can do:
class Person {
..
static mapping = {
table 'people'
version false
columns {
id column:'person_id'
}
}
}Using Composite ids
If you so choose you can use composite ids with Grails domain classes instead of the the regular id. It is not an approach we recommend, but is available to you if you need it:
class Person {
String firstName
String lastName static mapping = {
id composite:['firstName', 'lastName']
}
}
The above will create a composite id of the {{firstName}} and {{lastName}} properties of the Person class. When you later need to retrieve an instance by id you have to use a prototype of the object itself:
def p = Person.get(new Person(firstName:"Fred", lastName:"Flintstone"))
println p.firstName
Customising Column attributes
It is also possible to customise the mapping for individual columns onto the db. For example if its the name you want to change you can do:
class Person {
String firstName
static mapping = {
table 'people'
version false
id column:'person_id'
columns {
firstName column:'First_Name'
}
}
}Customising eager/lazy fetching
By default GORM collections use lazy fetching. With the DSL you can customise whether a relationship is lazy or eager:
class Person {
String firstName
static hasMany = [addresses:Address]
static mapping = {
table 'people'
version false
id column:'person_id'
columns {
firstName column:'First_Name'
addresses lazy:false
}
}
}
class Address {
String street
String postCode
}Configuring database indices
To get the best performance out of your queries it is often necessary to tailor the table index definitions. How you tailor them is domain specific and a matter of monitoring usage patterns of your queries. With GORM's DSL you can specify which columns need to live in which indexes:
class Person {
String firstName
String address
static mapping = {
table 'people'
version false
id column:'person_id'
columns {
firstName column:'First_Name', index:'Name_Idx'
address column:'Address', index:'Name_Idx, Address_Index'
}
}
}Customizing the colum type
GORM supports configuration of Hibernate types via the DSL using the {{type}} attribute. This includes specifing user types that subclass the Hibernate org.hibernate.types.UserType class. As an example
if you had a PostCodeType you could use it as follows:
class Address {
String number
String postCode
static mapping = {
columns {
postCode type:PostCodeType
}
}
}
Alternatively if you just wanted to map it to one of Hibernate's basic types other than the default chosen by Grails you could use:
class Address {
String number
String postCode
static mapping = {
columns {
postCode type:'text'
}
}
}
This would make the {{postCode}} column map to the SQL TEXT or CLOB type depending on which database is being used.
Caching associations
As well as the ability to use Hibernate's second level cache to cache instances you can also cache collections (associations) of objects. For example:
class Person {
String firstName
static hasMany = [addresses:Address]
static mapping = {
table 'people'
version false
columns {
addresses column:'Address', cache:true
}
}
}
class Address {
String number
String postCode
}
This will enable a 'read-write' caching mechanism on the {{addresses}} collection. You can also use:
cache:'read-write' // or 'read-only' or 'transactional'
To further configure the cache usage.