2. Topics
● Events and Auto Timestamping
● Custom ORM mapping
● Default Sort Order
● Validations and Constraints
3. Events and Auto Timestamping
GORM supports the registration of events as methods that get fired when certain events occurs such as
deletes, inserts and updates. The following is a list of supported events:
● beforeInsert - Executed before an object is initially persisted to the database. If you return false, the insert
will be cancelled.
● beforeUpdate - Executed before an object is updated. If you return false, the update will be cancelled.
● beforeDelete - Executed before an object is deleted. If you return false, the delete will be cancelled.
● beforeValidate - Executed before an object is validated
● afterInsert - Executed after an object is persisted to the database
● afterUpdate - Executed after an object has been updated
● afterDelete - Executed after an object has been deleted
● onLoad - Executed when an object is loaded from the database
To add an event simply register the relevant method with your domain class.
4. Example - The beforeInsert event
Fired before an object is saved to the database
class Person {
private static final Date NULL_DATE = new Date(0)
String firstName
String lastName
Date signupDate = NULL_DATE
def beforeInsert() {
if (signupDate == NULL_DATE) {
signupDate = new Date()
}
}
}
5. Automatic timestamping
If you define a dateCreated property it will be set to the current date for you when you create new instances.
Likewise, if you define a lastUpdated property it will be automatically be updated for you when you change
persistent instances.
class Person {
Date dateCreated
Date lastUpdated
}
6. Custom ORM mapping
Custom mappings are defined using a static mapping block defined within your domain class:
class Person {
…
static mapping = {
version false
autoTimestamp false
}
}
You can also configure global mappings in application.groovy (or an external config file) using this setting:
grails.gorm.default.mapping = {
version false
autoTimestamp false
}
7. Table and Column Names
Table names
The database table name which the class maps to can be customized using the table method:
class Person {
...
static mapping = {
table 'people'
}
}
In this case the class would be mapped to a table called people instead of the default name of person.
8. Column names
It is also possible to customize the mapping for individual columns onto the database. For example to change
the name you can do:
class Person {
String firstName
static mapping = {
table 'people'
firstName column: 'First_Name'
}
}
Here firstName is a dynamic method within the mapping Closure that has a single Map parameter. Since its
name corresponds to a domain class persistent field, the parameter values (in this case just "column") are used
to configure the mapping for that property.
9. Column type
GORM supports configuration of Hibernate types with the DSL using the type attribute. This includes specifying
user types that implement the Hibernate org.hibernate.usertype.UserType interface, which allows complete
customization of how a type is persisted.
class Address {
String number
String postCode
static mapping = {
postCode type: 'text'
}
}
10. Many-to-One/One-to-One Mappings
In the case of associations it is also possible to configure the foreign keys used to map associations. In the
case of a many-to-one or one-to-one association this is exactly the same as any regular column. For example
consider the following:
class Person {
String firstName
Address address
static mapping = {
table 'people'
firstName column: 'First_Name'
address column: 'Person_Address_Id'
}
}
11. One-to-Many Mapping
class Person {
static hasMany = [addresses: Address]
static mapping = {
addresses column: 'Person_Address_Id'
}
}
If you don’t want the column to be in the address table, but instead some intermediate join table you can use
the joinTable parameter:
class Person {
static hasMany = [addresses: Address]
static mapping = {
addresses joinTable: [name: 'Person_Addresses',
key: 'Person_Id',
column: 'Address_Id']
}
}
12. Many-to-Many Mapping
GORM, by default maps a many-to-many association using a join table.To change the column names you can
specify a column within the mappings for each class.
class Group {
...
static hasMany = [people: Person]
static mapping = {
people column: 'Group_Person_Id'
}
}
class Person {
...
static belongsTo = Group
static hasMany = [groups: Group]
static mapping = {
groups column: 'Group_Group_Id'
}
13. Many-to-Many Mapping
You can also specify the name of the join table to use:
class Group {
...
static mapping = {
people column: 'Group_Person_Id',
joinTable: 'PERSON_GROUP_ASSOCIATIONS'
}
}
class Person {
...
static mapping = {
groups column: 'Group_Group_Id',
joinTable: 'PERSON_GROUP_ASSOCIATIONS'
}
}
14. Default Sort Order
You can sort objects using query arguments such as those found in the list method:
def airports = Airport.list(sort:'name')
class Airport {
...
static mapping = {
sort "name"
}
}
class Airport {
...
static mapping = {
sort name: "desc"
}
}
15. Finally, you can configure sorting at the association level:
class Airport {
...
static hasMany = [flights: Flight]
static mapping = {
flights sort: 'number', order: 'desc'
}
}
In this case, the flights collection will always be sorted in descending order of flight number.
16. Grails allows you to apply constraints to a domain class that can then be used to validate a domain class
instance.
Constraints are applied using a "constraints" closure.
To validate a domain class you can call the "validate()" method on an instance.
class User {
String login
String password
String email
Date age
static constraints={
login(size:5..15,blank:false,unique:true)
password(size:5..15,blank:false)
email(email:true,blank:false)
age(min:new Date(),nullable:false)
}
}
Validations
17. Validating Constraints
def user = new User(params)
if(user.validate()){
//do something
} else {
user.errors.allErrors.each{
Println it
}
}
18. Custom Validators
class Response {
Survey survey
Answer answer
static constraints = {
survey blank: false
answer blank: false, validator: { val, Response obj -> val in obj.survey.answers }
}
}
The obj argument to the custom validator is the domain instance that is being validated, so we can access its
survey property and return a boolean to indicate whether the new value for the answer property, val, is valid.