1. Introduction

GORM is a data access framework with multiple backend implementations that allows you to rapidly write data access code with little effort for your favourite database.

There are currently several implementations of GORM. This documentation covers the original implementation of GORM which is based on the Hibernate ORM. Below you can find links to the other implementations:

As mentioned, GORM for Hibernate is the original implementation of GORM and has evolved dramatically over the years from a few meta-programming functions into a complete data access framework with multiple implementations for different datastores relational and NoSQL.

1.1. Release History

1.1.1. GORM 7.1

  • GORM 7.1 brings support for Apache Groovy 3

  • Default autowire the bean by type in the Data Service

  • Support for Java 14

  • Spring 5.3

  • Spring Boot 2.5

  • Hibernate 5.5

1.1.2. GORM 7.0

GORM 7.0 brings support for the latest versions of key dependencies including:

  • Java 8 minimum (Java 11 supported)

  • Hibernate 5.3 minimum

  • Spring 5.2 minimum

1.1.3. GORM 6.1

GORM 6.1 includes a variety of enhancements to GORM 6.0 including:

  • GORM Data Services

  • Multi-Tenancy Transformations

  • Support for Bean Validation API

  • Built-in Package Scanning

  • JPA Annotation Mapping Support

  • Hibernate transformations for dirty checking, managed entities and so on

  • HQL & SQL query escaping for GString queries

See the What’s New in GORM 6.1 guide for more information.

1.1.4. GORM 6.0

GORM 6.0 continues to evolve the new trait based model and includes the following new features:

  • Support for MongoDB 3.2.x drivers

  • Support for Neo4j 3.x drivers

  • Unified configuration model across all implementations

  • Unified Multiple Data Sources support for Hibernate, MongoDB and Neo4j

  • Multi Tenancy support for Hibernate, MongoDB and Neo4j

  • RxGORM for MongoDB built on MongoDB Rx drivers

  • RxGORM for REST built on RxNetty

1.1.5. GORM 5.0

GORM 5.0 replaced the majority of the custom AST transformations that power GORM with Groovy Traits.

Support for the MongoDB 3.x drivers, Neo4j 2.3.x and Hibernate 5.x was added.

1.1.6. GORM 4.0

GORM 4.0 continued to separate the GORM API from the Grails core APIs and was the first version to be support standalone execution outside of Grails.

GORM 4.0 was released in conjunction with Grails 3.0 and also featured auto configuration starters for Spring Boot.

Support was introduced for MongoDB 2.x drivers, Neo4j 2.2.x and Hibernate 4.3.x.

1.1.7. GORM 3.0

GORM 3.0 was the first release of GORM that was released separately to the Grails framework itself and was introduced in Grails 2.1.

More of the metaprogramming functions were refactored and replaced with AST transformations and APIs introduced that allowed GORM to operate against multiple database implementations including MongoDB.

The GORM API was separated from Hibernate and an SDK and TCK released for building compatible implementations.

1.1.8. GORM 2.0

GORM 2.0 evolved as part of Grails 2.0 and re-engineered some of the metaprogramming logic that relied on ExpandoMetaClass into a set of Groovy AST transformations.

These AST transformations relied on the Grails 2.x compiler infrastructure and hence this version of GORM was also only usable from within Grails.

1.1.9. GORM 1.0

The first version of GORM was a series of meta-programming functions that built on the capabilities of Groovy’s ExpandoMetaClass.

It was purely dynamic and integrated into the Grails framework and not usable without Grails and hence no actual distribution exists for this version and it is only available as part of Grails.

1.2. Upgrade Notes

1.2.1. Dependency Upgrades

GORM 7.1 supports Apache Groovy 3 and Java 14 Hibernate 5.5.x and Spring 5.3.x.

Each of these underlying components may have changes that require altering your application. These changes are beyond the scope of this documentation.

1.2.2. Default Autowire By Type inside GORM Data Services

A Grails Service (or a bean) inside GORM DataService will default to autowire by-type, For example:

./grails-app/services/example/BookService.groovy

package example

import grails.gorm.services.Service

@Service(Book)
abstract class BookService {

    TestService testRepo

    abstract Book save(String title, String author)

    void doSomething() {
        assert testRepo != null
    }
}

Please note that with autowire by-type as the default, when multiple beans for same type are found the application with throw Exception. Use the Spring `@Qualifier annotation for Fine-tuning Annotation Based Autowiring with Qualifiers.

2. Getting Started

To use GORM 8.1.1 for Hibernate in Grails 3 you can specify the following configuration in build.gradle:

dependencies {
    compile "org.grails.plugins:hibernate5:8.1.1"
    compile "org.hibernate:hibernate-ehcache"
}

If you are using a version of Grails 3 earlier than 3.3 then you may need to enforce the GORM version. If you are using Grails 3.2.7 or above this can be done by modifying the gormVersion setting in gradle.properties:

gormVersion=8.1.1

Otherwise if you are using an earlier version of Grails you can force the GORM version by adding the following block directly above the dependencies block:

build.gradle
configurations.all {
    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
        if( details.requested.group == 'org.grails' &&
            details.requested.name.startsWith('grails-datastore')) {
            details.useVersion("8.1.1")
        }
    }
}
dependencies {
    ...
}

2.1. Common Problems

If you receive an error that indicates a failure to resolve the grails-datastore-simple dependency you may need to add the following to build.gradle directly above the dependencies block:

build.gradle
configurations.all {
    exclude module:'grails-datastore-simple'
}

2.2. Configuring Different Hibernate Versions

There are various changes you have to make to your build depending on the version of Grails when using GORM 8.1.1.

2.2.1. Grails 3.2.x and above with Hibernate 4

Grails 3.2.x is based on Spring Boot 1.4.x which enforces Hibernate 5.0.x as the default version. If you want to continue to use Hibernate 4 you must explicitly declare the Hibernate 4 dependences in build.gradle.

build.gradle
dependencies {
    compile "org.grails.plugins:hibernate4"
    compile "org.hibernate:hibernate-core:4.3.10.Final"
    compile "org.hibernate:hibernate-ehcache:4.3.10.Final"
}

2.2.2. Grails 3.1.x and below with Hibernate 5

Grails 3.1.x and below are based on Spring Boot 1.3.x which enforces Hibernate 4 as the default version of Hibernate, hence you have to use explicit versions to depend on Hibernate 5:

build.gradle
dependencies {
    compile "org.grails.plugins:hibernate5"
    compile "org.hibernate:hibernate-core:5.1.0.Final"
    compile "org.hibernate:hibernate-ehcache:5.1.0.Final"
}

2.2.3. Grails 3.0.x Spring Version Conflicts

Grails 3.0.x enforces Spring 4.1.x as the Spring version, so if you want to use Hibernate 5 you must force an upgrade to Spring 4.2.x in build.gradle:

build.gradle
// the below is unnecessary in Grails 3.1 and above, but required in Grails 3.0.x
configurations.all {
    resolutionStrategy {
        eachDependency { DependencyResolveDetails details ->
            if(details.requested.group == 'org.springframework') {
                details.useVersion('4.2.3.RELEASE')
            }
        }
    }
}

The resolutionStrategy is needed to enforce an upgrade to Spring 4.2.x which is required by Hibernate 5 support. This block is not needed if you are using Grails 3.1 or above.

2.3. Using GORM in Spring Boot

To use GORM for Hibernate in Spring Boot add the necessary dependencies to your Boot application:

compile("org.grails:gorm-hibernate5-spring-boot:8.1.1")
compile "org.hibernate:hibernate-core"
compile "org.hibernate:hibernate-ehcache"
runtime "com.h2database:h2:1.4.192"
// for MySQL
// runtime "mysql:mysql-connector-java"

// for connection pooling
runtime "org.apache.tomcat:tomcat-jdbc:8.5.0"
runtime "org.apache.tomcat.embed:tomcat-embed-logging-log4j:8.5.0"

Then ensure you have configured a datasource and Hibernate as per the Spring Boot guide. For example in the case of MySQL:

hibernate:
    hbm2ddl:
        auto: update
    dialect: org.hibernate.dialect.MySQL5InnoDBDialect
spring:
    datasource:
        driverClassName: com.mysql.jdbc.Driver
        url:   jdbc:mysql://127.0.0.1:3306/gorm
        username: root
        password: ""
If if you prefer to use the Grails way of configuring the DataSource (with dataSource.url etc.) then you can add @EnableAutoConfiguration(exclude = DataSourceAutoConfiguration) to your Application class, which will allow GORM to take over configuring the data source.

Ensure your Boot Application class is annotated with ComponentScan, for example:

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.context.annotation.*

@Configuration
@EnableAutoConfiguration
@ComponentScan
class Application {
    static void main(String[] args) {
        SpringApplication.run Application, args
    }
}
Using ComponentScan without a value results in Boot scanning for classes in the same package or any package nested within the Application class package. If your GORM entities are in a different package specify the package name as the value of the ComponentScan annotation.

Finally create your GORM entities and ensure they are annotated with grails.persistence.Entity:

import grails.persistence.*

@Entity
class Person {
    String firstName
    String lastName
}

Note that Spring Boot does not include any kind of OpenSessionInView interceptor so if you try and invoke GORM methods in a Spring @Controller you may encounter a session not found error. To eliminate this problem make sure your @Controller methods are annotated with @Transactional. For example:

import org.springframework.transaction.annotation.Transactional
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class PersonController {

    @RequestMapping("/people")
    @Transactional(readOnly = true)
    public List<String> people() {
        Person.list().collect { Person p ->
            "$p.firstName $p.lastName".toString()
        }
    }
}

In addition, if you wish to return a GORM instance from a Spring @Controller, it should be noted that Spring uses Jackson for JSON marshalling, and Jackson will attempt to marshal the entire object to JSON, which can present an issue since GORM adds additional persistence related properties to your domain instance. To resolve this issue you should use @JsonIgnoreProperties on your GORM entity class to ignore any properties added by GORM:

import grails.persistence.*
import com.fasterxml.jackson.annotation.JsonIgnoreProperties

@Entity
@JsonIgnoreProperties(['dirtyPropertyNames', 'errors', 'dirty', 'attached', 'version'])
class Person {
    String firstName
    String lastName
}

2.4. Using GORM for Hibernate Outside Grails

If you wish to use GORM for Hibernate outside of a Grails application you should declare the necessary dependencies for GORM and the database you are using, for example in Gradle:

compile "org.grails:grails-datastore-gorm-hibernate5:8.1.1"
runtime "com.h2database:h2:1.4.192"
runtime "org.apache.tomcat:tomcat-jdbc:8.5.0"
runtime "org.apache.tomcat.embed:tomcat-embed-logging-log4j:8.5.0"
runtime "org.slf4j:slf4j-api:1.7.10"
The above example also uses the H2 Database and Tomcat connection pool. However other pool implementations are supported including commons-dbcp, tomcat pool or hikari. If a connection pool is not specified org.springframework.jdbc.datasource.DriverManagerDataSource is used, which creates a new connection to the database each time you request a connect. The latter will probably cause issues with an H2 in-memory database in that it will create a new in-memory database each time a connection is requested, losing previously created tables. Normal databases (MySql, Postgres or even file-based H2) are not affected.

Then create your entities in the src/main/groovy directory and annotate them with the grails.gorm.annotation.Entity annotation:

@Entity
class Person implements GormEntity<Person> { (1)
    String firstName
    String lastName
    static constraints = {
        firstName blank:false
        lastName blank:false
    }
}
1 Use of GormEntity is merely to aid IDE support outside of Grails. When used inside a Grails context, some IDEs will use the grails-app/domain location as a hint to enable code completion.

Then you need to place the bootstrap logic somewhere in your application which uses HibernateDatastore:

import org.grails.orm.hibernate.HibernateDatastore
Map configuration = [
    'hibernate.hbm2ddl.auto':'create-drop',
    'dataSource.url':'jdbc:h2:mem:myDB'
]
HibernateDatastore datastore = new HibernateDatastore( configuration, Person)

For more information on how to configure GORM see the Configuration section.

3. Quick Start Guide

A domain class can be created with the create-domain-class command if you are using Grails, or if you are not using Grails you can just create the .groovy file manually:

grails create-domain-class helloworld.Person

This will create a class at the location grails-app/domain/helloworld/Person.groovy such as the one below:

package helloworld

class Person {
}
If you have the configured the dataSource.dbCreate property and set it to "update", "create" or "create-drop", GORM will automatically generate/modify the database tables for you.

You can customize the class by adding properties:

class Person {
    String name
    Integer age
    Date lastVisit
}

Once you have a domain class try and manipulate it with console command in Grails by typing:

grails console

This loads an interactive GUI where you can run Groovy commands with access to the Spring ApplicationContext, GORM, etc.

Or if you are not using Grails here is a unit test template (using Spock) that can be run to test out the examples:

import spock.lang.*
import grails.gorm.annotation.Entity
import grails.transaction.Rollback
import org.grails.orm.hibernate.HibernateDatastore
import org.springframework.transaction.PlatformTransactionManager

class ExampleSpec extends Specification {

    @Shared @AutoCleanup HibernateDatastore hibernateDatastore
    @Shared PlatformTransactionManager transactionManager

    void setupSpec() {
       hibernateDatastore = new HibernateDatastore(Person)
       transactionManager = hibernateDatastore.getTransactionManager()
    }

    @Rollback
    void "test execute GORM standalone in a unit test"() {
       // your logic here
    }
}

@Entity
class Person {
    ...
}

3.1. Basic CRUD

Try performing some basic CRUD (Create/Read/Update/Delete) operations.

3.1.1. Create

To create a domain class use Map constructor to set its properties and call the save() method:

def p = new Person(name: "Fred", age: 40, lastVisit: new Date())
p.save()

The save() method will persist your class to the database using the underlying Hibernate ORM layer.

The save() method is defined by the GormEntity trait.

3.1.2. Read

GORM transparently adds an implicit id property to your domain class which you can use for retrieval:

def p = Person.get(1)
assert 1 == p.id

This uses the static get(id) method that expects a database identifier to read the Person object back from the database.

You can also load an object in a read-only state by using the read(id) method:

def p = Person.read(1)

In this case the underlying Hibernate engine will not do any dirty checking and the object will not be persisted. Note that if you explicitly call the save() method then the object is placed back into a read-write state.

In addition, you can also load a proxy for an instance by using the load(id) method:

def p = Person.load(1)

This incurs no database access until a method other than getId() is called. Hibernate then initializes the proxied instance, or throws an exception if no record is found for the specified id.

3.1.3. Update

To update an instance, change some properties and then call save() again:

def p = Person.get(1)
p.name = "Bob"
p.save()

3.1.4. Delete

To delete an instance use the delete() method:

def p = Person.get(1)
p.delete()

4. Configuration

GORM for Hibernate can be configured with the grails-app/conf/application.yml file when using Grails, the src/main/resources/application.yml file when using Spring Boot or by passing a Map or instanceof the PropertyResolver interface to the org.grails.orm.hibernate.HibernateDatastore class when used standalone.

All configuration options are read and materialized into an instance of HibernateConnectionSourceSettings.

4.1. Configuration Example

If you are using Grails or Spring Boot, the following is an example of configuration specified in application.yml:

dataSource:
    pooled: true
    dbCreate: create-drop
    url: jdbc:h2:mem:devDb
    driverClassName: org.h2.Driver
    username: sa
    password:
hibernate:
    cache:
        queries: false
        use_second_level_cache: true
        use_query_cache: false
        region.factory_class: org.hibernate.cache.ehcache.EhCacheRegionFactory

Each one of the settings under the dataSource block is set on the DataSourceSettings property of HibernateConnectionSourceSettings.

Whilst each setting under the hibernate block is set on the HibernateSettings property.

4.2. Configuration Reference

You can refer to the HibernateConnectionSourceSettings class for all available configuration options, but below is a table of the common ones:

name description default value

grails.gorm.flushMode

The flush mode to use

COMMIT

grails.gorm.failOnError

Whether to throw an exception on validation error

false

grails.gorm.default.mapping

The default mapping to apply to all classes

null

grails.gorm.default.constraints

The default constraints to apply to all classes

null

grails.gorm.multiTenancy.mode

The multi tenancy mode

NONE

The following are common configuration options for the SQL connection:

name description default value

dataSource.url

The JDBC url

jdbc:h2:mem:grailsDB

dataSource.driverClassName

The class of the JDBC driver

detected from URL

dataSource.username

The JDBC username

null

dataSource.password

The JDBC password

null

dataSource.jndiName

The name of the JNDI resource for the DataSource

null

dataSource.pooled

Whether the connection is pooled

true

dataSource.lazy

Whether a LazyConnectionDataSourceProxy should be used

true

dataSource.transactionAware

Whether a TransactionAwareDataSourceProxy should be used

true

dataSource.readOnly

Whether the DataSource is read-only

false

dataSource.options

A map of options to pass to the underlying JDBC driver

null

And the following are common configuration options for Hibernate:

name description default value

hibernate.dialect

The hibernate dialect to use

detected automatically from DataSource

hibernate.readOnly

Whether Hibernate should be read-only

false

hibernate.configClass

The configuration class to use

HibernateMappingContextConfiguration

hibernate.hbm2ddl.auto

Whether to create the tables on startup

none

hibernate.use_second_level_cache

Whether to use the second level cache

true

hibernate.cache.queries

Whether to cache queries (see Caching Queries)

false

hibernate.cache.use_query_cache

Enables the query cache

false

hibernate.configLocations

Location of additional Hibernate XML configuration files

hibernate.packagesToScan

In addition, any additional settings that start with hibernate. are passed through to Hibernate, so if there is any specific feature of Hibernate you wish to configure that is possible.

The above table covers the common configuration options. For all configuration refer to properties of the HibernateConnectionSourceSettings class.

4.3. The Default Mapping & Constraints

The grails.gorm.default.mapping and grails.gorm.default.constraints settings deserve special mention. These define the default ORM mapping and the default Validation Constraints used by each entity.

4.3.1. Altering the Default Database Mapping

You may have reason to want to change how all domain classes map to the database. For example, by default GORM uses the native id generation strategy of the database, whether that be an auto-increment column or a sequence.

If you wish to globally change all domain classes to use a uuid strategy then you can specify that in the default mapping:

grails-app/conf/application.groovy
grails.gorm.default.mapping = {
        cache true
        id generator:'uuid'
}

As you can see you can assign a closure that is equivalent to the mapping block used to customize how a domain class maps to a database table.

Because the setting is Groovy configuration it must go into a Groovy-aware configuration format. This can be grails-app/conf/application.groovy in Grails, or src/main/resources/application.groovy in Spring Boot.

4.3.2. Altering the Default Constraints

For validation, GORM applies a default set of constraints to all domain classes.

For example, by default all properties of GORM classes are not nullable by default. This means a value has to be supplied for each property, otherwise you will get a validation error.

In most cases this is what you want, but if you are dealing with a large number of columns, it may prove inconvinient.

You can alter the default constraints using Groovy configuration using the grails.gorm.default.constraints setting:

grails-app/conf/application.groovy
grails.gorm.default.constraints = {
    '*'(nullable: true, size: 1..20)
}

In the above example, all properties are allowed to be nullable by default, but limited to a size of between 1 and 20.

4.4. Hibernate Customization

If you want to hook into GORM and customize how Hibernate is configured there are a variety of ways to achieve that when using GORM.

Firstly, as mentioned previously, any configuration you specify when configuring GORM for Hibernate will be passed through to Hibernate so you can configure any setting of Hibernate itself.

For more advanced configuration you may want to configure or supply a new HibernateConnectionSourceFactory instance or a HibernateMappingContextConfiguration or both.

4.4.1. The HibernateConnectionSourceFactory

The HibernateConnectionSourceFactory is used to create a new Hibernate SessionFactory on startup.

If you are using Spring, it is registered as a Spring bean using the name hibernateConnectionSourceFactory and therefore can be overridden.

If you are not using Spring it can be passed to the constructor of the HibernateDatastore class on instantiation.

The HibernateConnectionSourceFactory has a few useful setters that allow you to specify a Hibernate Interceptor or MetadataContributor (Hibernate 5+ only).

4.4.2. The HibernateMappingContextConfiguration

HibernateMappingContextConfiguration is built by the HibernateConnectionSourceFactory, but a customized version can be specified using the hibernate.configClass setting in your configuration:

grails-app/conf/application.yml
hibernate:
        configClass: com.example.MyHibernateMappingContextConfiguration

The customized version should extend HibernateMappingContextConfiguration and using this class you can add additional classes, packages, hbm.cfg.xml files and so on.

5. Domain Modelling in GORM

When building applications you have to consider the problem domain you are trying to solve. For example if you were building an Amazon-style bookstore you would be thinking about books, authors, customers and publishers to name a few.

These are modeled in GORM as Groovy classes, so a Book class may have a title, a release date, an ISBN number and so on. The next few sections show how to model the domain in GORM.

Consider the following domain class:

grails-app/domain/org/bookstore/Book.groovy
package org.bookstore

class Book {
}

This class will map automatically to a table in the database called book (the same name as the class).

This behaviour is customizable through the ORM Domain Specific Language

Now that you have a domain class you can define its properties as Java types. For example:

package org.bookstore

class Book {
    String title
    Date releaseDate
    String ISBN
}

Each property is mapped to a column in the database, where the convention for column names is all lower case separated by underscores. For example releaseDate maps onto a column release_date. The SQL types are auto-detected from the Java types, but can be customized with Constraints or the ORM DSL.

5.1. Association in GORM

Relationships define how domain classes interact with each other. Unless specified explicitly at both ends, a relationship exists only in the direction it is defined.

5.1.1. Many-to-one and one-to-one

A many-to-one relationship is the simplest kind, and is defined with a property of the type of another domain class. Consider this example:

Example A
class Face {
    Nose nose
}
class Nose {
}

In this case we have a unidirectional many-to-one relationship from Face to Nose. To make this relationship bidirectional define the other side as follows (and see the section on controlling the ends of the association just below):

Example B
class Face {
    Nose nose
}
class Nose {
    static belongsTo = [face:Face]
}

In this case we use the belongsTo setting to say that Nose "belongs to" Face. The result of this is that we can create a Face, attach a Nose instance to it and when we save or delete the Face instance, GORM will save or delete the Nose. In other words, saves and deletes will cascade from Face to the associated Nose:

new Face(nose:new Nose()).save()

The example above will save both face and nose. Note that the inverse is not true and will result in an error due to a transient Face:

new Nose(face:new Face()).save() // will cause an error

Now if we delete the Face instance, the Nose will go too:

def f = Face.get(1)
f.delete() // both Face and Nose deleted

To make the relationship a true one-to-one, use the hasOne property on the owning side, e.g. Face:

Example C
class Face {
    static hasOne = [nose:Nose]
}
class Nose {
    Face face
}

Note that using this property puts the foreign key on the inverse table to the example A, so in this case the foreign key column is stored in the nose table inside a column called face_id.

hasOne only works with bidirectional relationships.

Finally, it’s a good idea to add a unique constraint on one side of the one-to-one relationship:

class Face {
    static hasOne = [nose:Nose]

    static constraints = {
        nose unique: true
    }
}
class Nose {
    Face face
}
Controlling the ends of the association

Occasionally you may find yourself with domain classes that have multiple properties of the same type. They may even be self-referential, i.e. the association property has the same type as the domain class it’s in. Such situations can cause problems because GORM may guess incorrectly the type of the association. Consider this simple class:

class Person {
    String name
    Person parent

    static belongsTo = [ supervisor: Person ]

    static constraints = { supervisor nullable: true }
}

As far as GORM is concerned, the parent and supervisor properties are two directions of the same association. So when you set the parent property on a Person instance, GORM will automatically set the supervisor property on the other Person instance. This may be what you want, but if you look at the class, what we in fact have are two unidirectional relationships.

To guide GORM to the correct mapping, you can tell it that a particular association is unidirectional through the mappedBy property:

class Person {
    String name
    Person parent

    static belongsTo = [ supervisor: Person ]

    static mappedBy = [ supervisor: "none", parent: "none" ]

    static constraints = { supervisor nullable: true }
}

You can also replace "none" with any property name of the target class. And of course this works for normal domain classes too, not just self-referential ones. Nor is the mappedBy property limited to many-to-one and one-to-one associations: it also works for one-to-many and many-to-many associations as you’ll see in the next section.

If you have a property called "none" on your domain class, this approach won’t work currently! The "none" property will be treated as the reverse direction of the association (or the "back reference"). Fortunately, "none" is not a common domain class property name.
Replacing a many-to-one collection

Given these GORM entities:

class Book {
    String name
    static hasMany = [reviews: Review]
}
class Review {
    String author
    String quote
    static belongsTo = [book: Book]
}

Imagine you have a book with two reviews:

new Book(name: 'Daemon')
                .addToReviews(new Review(quote: 'Daemon does for surfing the Web what Jaws did for swimming in the ocean.', author: 'Chicago Sun-Times'))
                .addToReviews(new Review(quote: 'Daemon is wet-yourself scary, tech-savvy, mind-blowing!', author: 'Paste Magazine'))
                .save()

You could create a method to replace the reviews collection as illustrated next:

Book replaceReviews(Serializable idParam, List<Review> newReviews) {
    Book book = Book.where { id == idParam }.join('reviews').get()
    clearReviews(book)
    newReviews.each { book.addToReviews(it) }
    book.save()
}

void clearReviews(Book book) {
    List<Serializable> ids = []
    book.reviews.collect().each {
        book.removeFromReviews(it)
        ids << it.id
    }
    Review.executeUpdate("delete Review r where r.id in :ids", [ids: ids])
}

Alternatively you could leverage cascade behaviour.

class Book {
    String name
    static hasMany = [reviews: Review]
    static mappping = {
        reviews cascade: 'all-delete-orphan'
    }
}
class Review {
    String author
    String quote
    static belongsTo = [book: Book]
}

The cascade behaviour takes cares of deleting every orphan Review. Thus, invoking .clear() suffices to remove the book’s previous reviews.

Book replaceReviews(Serializable idParam, List<Review> newReviews) {
    Book book = Book.where { id == idParam }.join('reviews').get()
    book.reviews.clear()
    newReviews.each { book.addToReviews(it) }
    book.save()
}

5.1.2. One-to-many

A one-to-many relationship is when one class, example Author, has many instances of another class, example Book. With GORM you define such a relationship with the hasMany setting:

class Author {
    static hasMany = [books: Book]

    String name
}
class Book {
    String title
}

In this case we have a unidirectional one-to-many. GORM will, by default, map this kind of relationship with a join table.

The ORM DSL allows mapping unidirectional relationships using a foreign key association instead

GORM will automatically inject a property of type java.util.Set into the domain class based on the hasMany setting. This can be used to iterate over the collection:

def a = Author.get(1)

for (book in a.books) {
    println book.title
}
The default fetch strategy used by GORM is "lazy", which means that the collection will be lazily initialized on first access. This can lead to the N+1 if you are not careful.

If you need "eager" fetching you can use the ORM DSL or specify eager fetching as part of a query

The default cascading behaviour is to cascade saves and updates, but not deletes unless a belongsTo is also specified:

class Author {
    static hasMany = [books: Book]

    String name
}
class Book {
    static belongsTo = [author: Author]
    String title
}

If you have two properties of the same type on the many side of a one-to-many you have to use mappedBy to specify which the collection is mapped:

class Airport {
    static hasMany = [flights: Flight]
    static mappedBy = [flights: "departureAirport"]
}
class Flight {
    Airport departureAirport
    Airport destinationAirport
}

This is also true if you have multiple collections that map to different properties on the many side:

class Airport {
    static hasMany = [outboundFlights: Flight, inboundFlights: Flight]
    static mappedBy = [outboundFlights: "departureAirport",
                       inboundFlights: "destinationAirport"]
}
class Flight {
    Airport departureAirport
    Airport destinationAirport
}

5.1.3. Many-to-many

GORM supports many-to-many relationships by defining a hasMany on both sides of the relationship and having a belongsTo on the owned side of the relationship:

class Book {
    static belongsTo = Author
    static hasMany = [authors:Author]
    String title
}
class Author {
    static hasMany = [books:Book]
    String name
}

GORM maps a many-to-many using a join table at the database level. The owning side of the relationship, in this case Author, takes responsibility for persisting the relationship and is the only side that can cascade saves across.

For example this will work and cascade saves:

new Author(name:"Stephen King")
        .addToBooks(new Book(title:"The Stand"))
        .addToBooks(new Book(title:"The Shining"))
        .save()

However this will only save the Book and not the authors!

new Book(name:"Groovy in Action")
        .addToAuthors(new Author(name:"Dierk Koenig"))
        .addToAuthors(new Author(name:"Guillaume Laforge"))
        .save()

This is the expected behaviour as, just like Hibernate, only one side of a many-to-many can take responsibility for managing the relationship.

5.1.4. Basic Collection Types

As well as associations between different domain classes, GORM also supports mapping of basic collection types. For example, the following class creates a nicknames association that is a Set of String instances:

class Person {
    static hasMany = [nicknames: String]
}

GORM will map an association like the above using a join table. You can alter various aspects of how the join table is mapped using the joinTable argument:

class Person {

    static hasMany = [nicknames: String]

    static mapping = {
       nicknames joinTable: [name: 'bunch_o_nicknames',
                           key: 'person_id',
                           column: 'nickname',
                           type: "text"]
    }
}

The example above will map to a table that looks like the following:

bunch_o_nicknames Table

---------------------------------------------
| person_id         |     nickname          |
---------------------------------------------
|   1               |      Fred             |
---------------------------------------------

5.2. Composition in GORM

As well as associations, GORM supports the notion of composition. In this case instead of mapping classes onto separate tables a class can be "embedded" within the current table. For example:

class Person {
    Address homeAddress
    Address workAddress
    static embedded = ['homeAddress', 'workAddress']
}

class Address {
    String number
    String code
}

The resulting mapping would looking like this:

5.2.2 composition
If you define the Address class in a separate Groovy file in the grails-app/domain directory you will also get an address table. If you don’t want this to happen use Groovy’s ability to define multiple classes per file and include the Address class below the Person class in the grails-app/domain/Person.groovy file. Another option is to define the Address class in src/main/groovy/Address.groovy and annotate it with grails.gorm.annotation.Entity

5.3. Inheritance in GORM

GORM supports inheritance both from abstract base classes and concrete persistent GORM entities. For example:

class Content {
     String author
}
class BlogEntry extends Content {
    URL url
}
class Book extends Content {
    String ISBN
}
class PodCast extends Content {
    byte[] audioStream
}

In the above example we have a parent Content class and then various child classes with more specific behaviour.

5.3.1. Considerations

At the database level GORM by default uses table-per-hierarchy mapping with a discriminator column called class so the parent class (Content) and its subclasses (BlogEntry, Book etc.), share the same table.

Table-per-hierarchy mapping has a down side in that you cannot have non-nullable properties with inheritance mapping. An alternative is to use table-per-subclass which can be enabled with the ORM DSL

However, excessive use of inheritance and table-per-subclass can result in poor query performance due to the use of outer join queries. In general our advice is if you’re going to use inheritance, don’t abuse it and don’t make your inheritance hierarchy too deep.

5.3.2. Polymorphic Queries

The upshot of inheritance is that you get the ability to polymorphically query. For example using the list() method on the Content super class will return all subclasses of Content:

def content = Content.list() // list all blog entries, books and podcasts
content = Content.findAllByAuthor('Joe Bloggs') // find all by author

def podCasts = PodCast.list() // list only podcasts

5.4. Sets, Lists and Maps

5.4.1. Sets of Objects

By default when you define a relationship with GORM it is a java.util.Set which is an unordered collection that cannot contain duplicates. In other words when you have:

class Author {
    static hasMany = [books: Book]
}

The books property that GORM injects is a java.util.Set. Sets guarantee uniqueness but not order, which may not be what you want. To have custom ordering you configure the Set as a SortedSet:

class Author {

    SortedSet books

    static hasMany = [books: Book]
}

In this case a java.util.SortedSet implementation is used which means you must implement java.lang.Comparable in your Book class:

class Book implements Comparable {

    String title
    Date releaseDate = new Date()

    int compareTo(obj) {
        releaseDate.compareTo(obj.releaseDate)
    }
}

The result of the above class is that the Book instances in the books collection of the Author class will be ordered by their release date.

5.4.2. Lists of Objects

To keep objects in the order which they were added and to be able to reference them by index like an array you can define your collection type as a List:

class Author {

    List books

    static hasMany = [books: Book]
}

In this case when you add new elements to the books collection the order is retained in a sequential list indexed from 0 so you can do:

author.books<<0>> // get the first book

The way this works at the database level is Hibernate creates a books_idx column where it saves the index of the elements in the collection to retain this order at the database level.

When using a List, elements must be added to the collection before being saved, otherwise Hibernate will throw an exception (org.hibernate.HibernateException: null index column for collection):

// This won't work!
def book = new Book(title: 'The Shining')
book.save()
author.addToBooks(book)

// Do it this way instead.
def book = new Book(title: 'Misery')
author.addToBooks(book)
author.save()

5.4.3. Bags of Objects

If ordering and uniqueness aren’t a concern (or if you manage these explicitly) then you can use the Hibernate Bag type to represent mapped collections.

The only change required for this is to define the collection type as a Collection:

class Author {

   Collection books

   static hasMany = [books: Book]
}

Since uniqueness and order aren’t managed by Hibernate, adding to or removing from collections mapped as a Bag don’t trigger a load of all existing instances from the database, so this approach will perform better and require less memory than using a Set or a List.

5.4.4. Maps of Objects

If you want a simple map of string/value pairs GORM can map this with the following:

class Author {
    Map books // map of ISBN:book names
}

def a = new Author()
a.books = ['1590597583':"My Book"]
a.save()

In this case the key and value of the map MUST be strings.

If you want a Map of objects then you can do this:

class Book {

    Map authors

    static hasMany = [authors: Author]
}

def a = new Author(name:"Stephen King")

def book = new Book()
book.authors = [stephen:a]
book.save()

The static hasMany property defines the type of the elements within the Map. The keys for the map must be strings.

5.4.5. A Note on Collection Types and Performance

The Java Set type doesn’t allow duplicates. To ensure uniqueness when adding an entry to a Set association Hibernate has to load the entire associations from the database. If you have a large numbers of entries in the association this can be costly in terms of performance.

The same behavior is required for List types, since Hibernate needs to load the entire association to maintain order. Therefore it is recommended that if you anticipate a large numbers of records in the association that you make the association bidirectional so that the link can be created on the inverse side. For example consider the following code:

def book = new Book(title:"New Grails Book")
def author = Author.get(1)
book.author = author
book.save()

In this example the association link is being created by the child (Book) and hence it is not necessary to manipulate the collection directly resulting in fewer queries and more efficient code. Given an Author with a large number of associated Book instances if you were to write code like the following you would see an impact on performance:

def book = new Book(title:"New Grails Book")
def author = Author.get(1)
author.addToBooks(book)
author.save()

You could also model the collection as a Hibernate Bag as described above.

6. Persistence Basics

A key thing to remember about GORM is that under the surface GORM is using Hibernate for persistence. If you are coming from a background of using ActiveRecord or iBatis/MyBatis, Hibernate’s "session" model may feel a little strange.

If you are using Grails, then Grails automatically binds a Hibernate session to the currently executing request. This lets you use the save() and delete methods as well as other GORM methods transparently.

If you are not using Grails then you have to make sure that a session is bound to the current request. One way to to achieve that is with the withNewSession(Closure) method:

Book.withNewSession {
        // your logic here
}

Another option is to bind a transaction using the withTransaction(Closure) method:

Book.withTransaction {
        // your logic here
}

6.1. Transactional Write-Behind

A useful feature of Hibernate over direct JDBC calls and even other frameworks is that when you call save() or delete() it does not necessarily perform any SQL operations at that point. Hibernate batches up SQL statements and executes them as late as possible, often at the end of the request when flushing and closing the session.

If you are using Grails this typically done for you automatically, which manages your Hibernate session. If you are using GORM outside of Grails then you may need to manually flush the session at the end of your operation.

Hibernate caches database updates where possible, only actually pushing the changes when it knows that a flush is required, or when a flush is triggered programmatically. One common case where Hibernate will flush cached updates is when performing queries since the cached information might be included in the query results. But as long as you’re doing non-conflicting saves, updates, and deletes, they’ll be batched until the session is flushed. This can be a significant performance boost for applications that do a lot of database writes.

Note that flushing is not the same as committing a transaction. If your actions are performed in the context of a transaction, flushing will execute SQL updates but the database will save the changes in its transaction queue and only finalize the updates when the transaction commits.

6.2. Saving and Updating

An example of using the save() method can be seen below:

def p = Person.get(1)
p.save()

This save will be not be pushed to the database immediately - it will be pushed when the next flush occurs. But there are occasions when you want to control when those statements are executed or, in Hibernate terminology, when the session is "flushed". To do so you can use the flush argument to the save method:

def p = Person.get(1)
p.save(flush: true)

Note that in this case all pending SQL statements including previous saves, deletes, etc. will be synchronized with the database. This also lets you catch any exceptions, which is typically useful in highly concurrent scenarios involving optimistic locking:

def p = Person.get(1)
try {
    p.save(flush: true)
}
catch (org.springframework.dao.DataIntegrityViolationException e) {
    // deal with exception
}

Another thing to bear in mind is that GORM validates a domain instance every time you save it. If that validation fails the domain instance will not be persisted to the database. By default, save() will simply return null in this case, but if you would prefer it to throw an exception you can use the failOnError argument:

def p = Person.get(1)
try {
    p.save(failOnError: true)
}
catch (ValidationException e) {
    // deal with exception
}

You can even change the default behaviour with a setting in application.groovy, as described in the section on configuration. Just remember that when you are saving domain instances that have been bound with data provided by the user, the likelihood of validation exceptions is quite high and you won’t want those exceptions propagating to the end user.

6.3. Deleting Objects

An example of the delete() method can be seen below:

def p = Person.get(1)
p.delete()

As with saves, Hibernate will use transactional write-behind to perform the delete; to perform the delete in-place you can use the flush argument:

def p = Person.get(1)
p.delete(flush: true)

Using the flush argument lets you catch any errors that occur during a delete. A common error that may occur is if you violate a database constraint, although this is normally down to a programming or schema error. The following example shows how to catch a DataIntegrityViolationException that is thrown when you violate the database constraints:

import org.springframework.dao.*

def p = Person.get(1)

try {
    p.delete(flush: true)
}
catch (DataIntegrityViolationException e) {
    // handle the error
}

In order to perform a batch delete there are a couple of ways to achieve that. One way is to use a Where Query:

Person.where {
        name == "Fred"
}.deleteAll()

Another alternative is to use an HQL statement within the executeUpdate(…​) method:

Customer.executeUpdate("delete Customer c where c.name = :oldName",
                       [oldName: "Fred"])

6.4. Understanding Cascading Updates and Deletes

It is critical that you understand how cascading updates and deletes work when using GORM. The key part to remember is the belongsTo setting which controls which class "owns" a relationship.

Whether it is a one-to-one, one-to-many or many-to-many, defining belongsTo will result in updates cascading from the owning class to its dependant (the other side of the relationship), and for many-/one-to-one and one-to-many relationships deletes will also cascade.

If you do not define belongsTo then no cascades will happen and you will have to manually save each object (except in the case of the one-to-many, in which case saves will cascade automatically if a new instance is in a hasMany collection).

Here is an example:

class Airport {
    String name
    static hasMany = [flights: Flight]
}
class Flight {
    String number
    static belongsTo = [airport: Airport]
}

If I now create an Airport and add some ``Flight``s to it I can save the Airport and have the updates cascaded down to each flight, hence saving the whole object graph:

new Airport(name: "Gatwick")
        .addToFlights(new Flight(number: "BA3430"))
        .addToFlights(new Flight(number: "EZ0938"))
        .save()

Conversely if I later delete the Airport all Flight instances associated with it will also be deleted:

def airport = Airport.findByName("Gatwick")
airport.delete()

The above examples are called transitive persistence and are controlled via the belongsTo and the cascade policy. If I were to remove belongsTo then the above cascading deletion code would not work.

Unidirectional Many-To-One without belongsTo

For example, consider the following domain model:

class Location {
    String city
}

class Author {
    String name
    Location location
}

It looks simple, right? And it is. Just set the location property to a Location instance and you have linked an author to a location. But see what happens when we run the following code:

def a = new Author(name: "Niall Ferguson", location: new Location(city: "Boston"))
a.save()

An exception is thrown. If you look at the ultimate “caused by” exception, you’ll see the message “not-null property references a null or transient value: Author.location”. What’s going on?

A transient instance is one that isn’t attached to a Hibernate session. As you can see from the code, we are setting the Author.location property to a new Location instance, not one retrieved from the database. Hence the instance is transient. The obvious fix is to make the Location instance persistent by saving it:

def l = new Location(city: "Boston")
l.save()

def a = new Author(name: "Niall Ferguson", location: l)
a.save()

Another option is to alter the cascade policy for the association. There are two ways to do that. One way is to define belongsTo on the Location class:

class Location {
    String city

    static belongsTo = Author
}

Note that this above syntax does not make the association bidirectional since no property is defined. A bidirectional example would be:

class Location {
    String city

    static belongsTo = [author:Author]
}

Alternatively if you prefer that the Location class has nothing to do with the Author class you can define the cascade policy in Author:

class Author {
    String name
    Location location

    static mapping = {
        location cascade:'save-update'
    }
}

The above example will configure the cascade policy to cascade saves and updates, but not deletes.

Bidirectional one-to-many with belongsTo
class A { static hasMany = [bees: B] }
class B { static belongsTo = [a: A] }

In the case of a bidirectional one-to-many where the many side defines a belongsTo then the cascade strategy is set to "ALL" for the one side and "NONE" for the many side.

What this means is that whenever an instance of A is saved or updated. So will any instances of B. And, critically, whenever any instance of A is deleted so will all the associated instances of B!

Unidirectional One-to-Many
class A { static hasMany = [bees: B] }
class B {  }

In the case of a unidirectional one-to-many where the many side defines no belongsTo then the cascade strategy is set to "SAVE-UPDATE".

Since the belongsTo is not defined, this means that saves and updates will be cascaded from A to B, however deletes will not cascade!

Only when you define belongsTo in B or alter the cascading strategy of A will deletes be cascaded.

Bidirectional One-to-Many, no belongsTo
class A { static hasMany = [bees: B] }
class B { A a }

In the case of a bidirectional one-to-many where the many side does not define a belongsTo then the cascade strategy is set to "SAVE-UPDATE" for the one side and "NONE" for the many side.

So exactly like the previous case of a undirectional One-to-Many, without belongsTo definition no delete operations will be cascaded, but crucially saves and updates will by default. If you do not want saves and updates to cacade then you must alter the cascade policy of A:

class A {
    static hasMany = [bees: B]
    static mapping = {
        bees cascade:"none"
    }
}
Unidirectional Many-to-One with belongsTo
class A {  }
class B { static belongsTo = [a: A] }

In the case of a unidirectional many-to-one association that defines a belongsTo then the cascade strategy is set to "ALL" for the owning side of the relationship (A→B) and "NONE" from the side that defines the belongsTo (B→A)

You may be wondering why this association is a many-to-one and not a one-to-one. The reason is because it is possible to have multiple instances of B associated to the same instance of A. If you wish to define this association as a true one-to-one association a unique constraint is required:

class B {
    static belongsTo = [a: A]
    static constraints = {
        a unique:true
    }
}

Note that if you need further control over cascading behaviour, you can use the ORM DSL.

6.5. Eager and Lazy Fetching

Associations in GORM are by default lazy. This is best explained by example:

class Airport {
    String name
    static hasMany = [flights: Flight]
}
class Flight {
    String number
    Location destination
    static belongsTo = [airport: Airport]
}
class Location {
    String city
    String country
}

Given the above domain classes and the following code:

def airport = Airport.findByName("Gatwick")
for (flight in airport.flights) {
    println flight.destination.city
}

GORM will execute a single SQL query to fetch the Airport instance, another to get its flights, and then 1 extra query for each iteration over the flights association to get the current flight’s destination. In other words you get N+1 queries (if you exclude the original one to get the airport).

6.6. Configuring Eager Fetching

An alternative approach that avoids the N+1 queries is to use eager fetching, which can be specified as follows:

class Airport {
    String name
    static hasMany = [flights: Flight]
    static mapping = {
        flights lazy: false
    }
}

In this case the flights association will be loaded at the same time as its Airport instance, although a second query will be executed to fetch the collection. You can also use fetch: 'join' instead of lazy: false , in which case GORM will only execute a single query to get the airports and their flights. This works well for single-ended associations, but you need to be careful with one-to-manys. Queries will work as you’d expect right up to the moment you add a limit to the number of results you want. At that point, you will likely end up with fewer results than you were expecting. The reason for this is quite technical but ultimately the problem arises from GORM using a left outer join.

So, the recommendation is currently to use fetch: 'join' for single-ended associations and lazy: false for one-to-manys.

Be careful how and where you use eager loading because you could load your entire database into memory with too many eager associations. You can find more information on the mapping options in the section on the ORM DSL.

6.7. Altering Fetch Strategy for a Query

Rather than configuring join fetching as the default for an association, it may be better to alter the join strategy only for the queries that require it. This can be done using the fetch argument to most GORM methods:

// Using the list method
Author.list(fetch: [location: 'join']).each { a ->
    println a.location.city
}
// Using a dynamic finder
Author.findAllByNameLike("John%", [ sort: 'name', order: 'asc', fetch: [location: 'join'] ]).each { a->
    ...
}

Or using the join method when using Where Queries or criteria:

Author.where {
    name == "Stephen King"
}.join('location')
 .list()

6.8. Using Batch Fetching

Although eager fetching is appropriate for some cases, it is not always desirable. If you made everything eager you could quite possibly load your entire database into memory resulting in performance and memory problems. An alternative to eager fetching is to use batch fetching. You can configure Hibernate to lazily fetch results in "batches". For example:

class Airport {
    String name
    static hasMany = [flights: Flight]
    static mapping = {
        flights batchSize: 10
    }
}

In this case, due to the batchSize argument, when you iterate over the flights association, Hibernate will fetch results in batches of 10. For example if you had an Airport that had 30 flights, if you didn’t configure batch fetching you would get 1 query to fetch the Airport and then 30 queries to fetch each flight. With batch fetching you get 1 query to fetch the Airport and 3 queries to fetch each Flight in batches of 10. In other words, batch fetching is an optimization of the lazy fetching strategy. Batch fetching can also be configured at the class level as follows:

class Flight {
    ...
    static mapping = {
        batchSize 10
    }
}

6.9. Pessimistic and Optimistic Locking

6.9.1. Optimistic Locking

By default GORM classes are configured for optimistic locking. Optimistic locking is a feature of Hibernate which involves storing a version value in a special version column in the database that is incremented after each update.

The version column gets read into a version property that contains the current versioned state of persistent instance which you can access:

def airport = Airport.get(10)

println airport.version

When you perform updates Hibernate will automatically check the version property against the version column in the database and if they differ will throw a StaleObjectException. This will roll back the transaction if one is active.

This is useful as it allows a certain level of atomicity without resorting to pessimistic locking that has an inherit performance penalty. The downside is that you have to deal with this exception if you have highly concurrent writes. This requires flushing the session:

def airport = Airport.get(10)

try {
    airport.name = "Heathrow"
    airport.save(flush: true)
}
catch (org.springframework.dao.OptimisticLockingFailureException e) {
    // deal with exception
}

The way you deal with the exception depends on the application. You could attempt a programmatic merge of the data or go back to the user and ask them to resolve the conflict.

Alternatively, if it becomes a problem you can resort to pessimistic locking.

The version will only be updated after flushing the session.

6.9.2. Pessimistic Locking

Pessimistic locking is equivalent to doing a SQL "SELECT * FOR UPDATE" statement and locking a row in the database. This has the implication that other read operations will be blocking until the lock is released.

In GORM pessimistic locking is performed on an existing instance with the lock() method:

def airport = Airport.get(10)
airport.lock() // lock for update
airport.name = "Heathrow"
airport.save()

GORM will automatically deal with releasing the lock for you once the transaction has been committed.

However, in the above case what we are doing is "upgrading" from a regular SELECT to a SELECT..FOR UPDATE and another thread could still have updated the record in between the call to get() and the call to lock().

To get around this problem you can use the static lock(id) method that takes an id just like get(id):

def airport = Airport.lock(10) // lock for update
airport.name = "Heathrow"
airport.save()

In this case only SELECT..FOR UPDATE is issued.

As well as the lock(id) method you can also obtain a pessimistic locking using queries. For example using a dynamic finder:

def airport = Airport.findByName("Heathrow", [lock: true])

Or using criteria:

def airport = Airport.createCriteria().get {
    eq('name', 'Heathrow')
    lock true
}

6.10. Modification Checking

Once you have loaded and possibly modified a persistent domain class instance, it isn’t straightforward to retrieve the original values. If you try to reload the instance using get(id) Hibernate will return the current modified instance from its Session cache.

Reloading using another query would trigger a flush which could cause problems if your data isn’t ready to be flushed yet. So GORM provides some methods to retrieve the original values that Hibernate caches when it loads the instance (which it uses for dirty checking).

6.10.1. isDirty

You can use the isDirty() method to check if any field has been modified:

def airport = Airport.get(10)
assert !airport.isDirty()

airport.properties = params
if (airport.isDirty()) {
   // do something based on changed state
}
isDirty() does not currently check collection associations, but it does check all other persistent properties and associations.

You can also check if individual fields have been modified:

def airport = Airport.get(10)
assert !airport.isDirty()

airport.properties = params
if (airport.isDirty('name')) {
   // do something based on changed name
}

6.10.2. isDirty and Proxies

Dirty checking uses the equals() method to determine if a property has changed. In the case of associations, it is important to recognize that if the association is a proxy, comparing properties on the domain that are not related to the identifier will initialize the proxy, causing another database query.

If the association does not define equals() method, then the default Groovy behavior of verifying the instances are the same will be used. Because proxies are not the same instance as an instance loaded from the database, which can cause confusing behavior. It is recommended to implement the equals() method if you need to check the dirtiness of an association. For example:

class Author {
    Long id
    String name

     /**
     * This ensures that if either or both of the instances
     * have a null id (new instances), they are not equal.
     */
    @Override
    boolean equals(o) {
        if (!(o instanceof Author)) return false
        if (this.is(o)) return true
        Author that = (Author) o
        if (id !=null && that.id !=null) return id == that.id
        return false
    }
}

class Book {
    Long id
    String title
    Author author
}

6.10.3. getDirtyPropertyNames

You can use the getDirtyPropertyNames() method to retrieve the names of modified fields; this may be empty but will not be null:

def airport = Airport.get(10)
assert !airport.isDirty()

airport.properties = params
def modifiedFieldNames = airport.getDirtyPropertyNames()
for (fieldName in modifiedFieldNames) {
   // do something based on changed value
}

6.10.4. getPersistentValue

You can use the getPersistentValue(fieldName) method to retrieve the value of a modified field:

def airport = Airport.get(10)
assert !airport.isDirty()

airport.properties = params
def modifiedFieldNames = airport.getDirtyPropertyNames()
for (fieldName in modifiedFieldNames) {
    def currentValue = airport."$fieldName"
    def originalValue = airport.getPersistentValue(fieldName)
    if (currentValue != originalValue) {
        // do something based on changed value
    }
}

7. Querying with GORM

GORM supports a number of powerful ways to query from dynamic finders, to criteria to Hibernate’s object oriented query language HQL. Depending on the complexity of the query you have the following options in order of flexibility and power:

  • Dynamic Finders

  • Where Queries

  • Criteria Queries

  • Hibernate Query Language (HQL)

In addition, Groovy’s ability to manipulate collections with GPath and methods like sort, findAll and so on combined with GORM results in a powerful combination.

However, let’s start with the basics.

7.1. Listing instances

Use the list() method to obtain all instances of a given class:

def books = Book.list()

The list() method supports arguments to perform pagination:

def books = Book.list(offset:10, max:20)

as well as sorting:

def books = Book.list(sort:"title", order:"asc")

Here, the sort argument is the name of the domain class property that you wish to sort on, and the order argument is either asc for *asc*ending or desc for *desc*ending.

7.2. Retrieval by Database Identifier

The second basic form of retrieval is by database identifier using the get(id) method:

def book = Book.get(23)

You can also obtain a list of instances for a set of identifiers using getAll():

def books = Book.getAll(23, 93, 81)

7.3. Dynamic Finders

GORM supports the concept of dynamic finders. A dynamic finder looks like a static method invocation, but the methods themselves don’t actually exist in any form at the code level.

Instead, a method is auto-magically generated using code synthesis at runtime, based on the properties of a given class. Take for example the Book class:

class Book {
    String title
    Date releaseDate
    Author author
}
class Author {
    String name
}

The Book class has properties such as title, releaseDate and author. These can be used by the findBy* and findAllBy* methods in the form of "method expressions":

def book = Book.findByTitle("The Stand")

book = Book.findByTitleLike("Harry Pot%")

book = Book.findByReleaseDateBetween(firstDate, secondDate)

book = Book.findByReleaseDateGreaterThan(someDate)

book = Book.findByTitleLikeOrReleaseDateLessThan("%Something%", someDate)

7.3.1. Method Expressions

A method expression in GORM is made up of the prefix such as findBy* followed by an expression that combines one or more properties. The basic form is:

Book.findBy(<<Property>><<Comparator>><<Boolean Operator>>)?<<Property>><<Comparator>>

The tokens marked with a ? are optional. Each comparator changes the nature of the query. For example:

def book = Book.findByTitle("The Stand")

book =  Book.findByTitleLike("Harry Pot%")

In the above example the first query is equivalent to equality whilst the latter, due to the Like comparator, is equivalent to a SQL like expression.

The possible comparators include:

  • InList - In the list of given values

  • LessThan - less than a given value

  • LessThanEquals - less than or equal a give value

  • GreaterThan - greater than a given value

  • GreaterThanEquals - greater than or equal a given value

  • Like - Equivalent to a SQL like expression

  • Ilike - Similar to a Like, except case insensitive

  • NotEqual - Negates equality

  • InRange - Between the from and to values of a Groovy Range

  • Rlike - Performs a Regexp LIKE in MySQL or Oracle otherwise falls back to Like

  • Between - Between two values (requires two arguments)

  • IsNotNull - Not a null value (doesn’t take an argument)

  • IsNull - Is a null value (doesn’t take an argument)

Notice that the last three require different numbers of method arguments compared to the rest, as demonstrated in the following example:

def now = new Date()
def lastWeek = now - 7
def book = Book.findByReleaseDateBetween(lastWeek, now)

books = Book.findAllByReleaseDateIsNull()
books = Book.findAllByReleaseDateIsNotNull()

7.3.2. Boolean logic (AND/OR)

Method expressions can also use a boolean operator to combine two or more criteria:

def books = Book.findAllByTitleLikeAndReleaseDateGreaterThan(
                      "%Java%", new Date() - 30)

In this case we’re using And in the middle of the query to make sure both conditions are satisfied, but you could equally use Or:

def books = Book.findAllByTitleLikeOrReleaseDateGreaterThan(
                      "%Java%", new Date() - 30)

You can combine as many criteria as you like, but they must all be combined with And or all Or. If you need to combine And and Or or if the number of criteria creates a very long method name, just convert the query to a Criteria or HQL query.

7.3.3. Querying Associations

Associations can also be used within queries:

def author = Author.findByName("Stephen King")

def books = author ? Book.findAllByAuthor(author) : []

In this case if the Author instance is not null we use it in a query to obtain all the Book instances for the given Author.

7.3.4. Pagination and Sorting

The same pagination and sorting parameters available on the list() method can also be used with dynamic finders by supplying a map as the final parameter:

def books = Book.findAllByTitleLike("Harry Pot%",
               [max: 3, offset: 2, sort: "title", order: "desc"])

7.4. Where Queries

The where() method builds on the support for Detached Criteria by providing an enhanced, compile-time checked query DSL for common queries. The where method is more flexible than dynamic finders, less verbose than criteria and provides a powerful mechanism to compose queries.

7.4.1. Basic Querying

The where() method accepts a closure that looks very similar to Groovy’s regular collection methods. The closure should define the logical criteria in regular Groovy syntax, for example:

def query = Person.where {
   firstName == "Bart"
}
Person bart = query.find()

The returned object is a DetachedCriteria instance, which means it is not associated with any particular database connection or session. This means you can use the where method to define common queries at the class level:

import grails.gorm.*

class Person {
    static DetachedCriteria<Person> simpsons = where {
         lastName == "Simpson"
    }
    ...
}
...
Person.simpsons.each { Person p ->
    println p.firstname
}

Query execution is lazy and only happens upon usage of the DetachedCriteria instance. If you want to execute a where-style query immediately there are variations of the findAll and find methods to accomplish this:

def results = Person.findAll {
     lastName == "Simpson"
}
def results = Person.findAll(sort:"firstName") {
     lastName == "Simpson"
}
Person p = Person.find { firstName == "Bart" }

Each Groovy operator maps onto a regular criteria method. The following table provides a map of Groovy operators to methods:

Operator Criteria Method Description

==

eq

Equal to

!=

ne

Not equal to

>

gt

Greater than

<

lt

Less than

>=

ge

Greater than or equal to

<=

le

Less than or equal to

in

inList

Contained within the given list

==~

like

Like a given string

=~

ilike

Case insensitive like

It is possible use regular Groovy comparison operators and logic to formulate complex queries:

def query = Person.where {
    (lastName != "Simpson" && firstName != "Fred") || (firstName == "Bart" && age > 9)
}
def results = query.list(sort:"firstName")

The Groovy regex matching operators map onto like and ilike queries unless the expression on the right hand side is a Pattern object, in which case they map onto an rlike query:

def query = Person.where {
     firstName ==~ ~/B.+/
}
Note that rlike queries are only supported if the underlying database supports regular expressions

A between criteria query can be done by combining the in keyword with a range:

def query = Person.where {
     age in 18..65
}

Finally, you can do isNull and isNotNull style queries by using null with regular comparison operators:

def query = Person.where {
     middleName == null
}

7.4.2. Query Composition

Since the return value of the where method is a DetachedCriteria instance you can compose new queries from the original query:

DetachedCriteria<Person> query = Person.where {
     lastName == "Simpson"
}
DetachedCriteria<Person> bartQuery = query.where {
     firstName == "Bart"
}
Person p = bartQuery.find()

Note that you cannot pass a closure defined as a variable into the where method unless it has been explicitly cast to a DetachedCriteria instance. In other words the following will produce an error:

def callable = {
    lastName == "Simpson"
}
def query = Person.where(callable)

The above must be written as follows:

import grails.gorm.DetachedCriteria

def callable = {
    lastName == "Simpson"
} as DetachedCriteria<Person>
def query = Person.where(callable)

As you can see the closure definition is cast (using the Groovy as keyword) to a DetachedCriteria instance targeted at the Person class.

7.4.3. Conjunction, Disjunction and Negation

As mentioned previously you can combine regular Groovy logical operators (|| and &&) to form conjunctions and disjunctions:

def query = Person.where {
    (lastName != "Simpson" && firstName != "Fred") || (firstName == "Bart" && age > 9)
}

You can also negate a logical comparison using !:

def query = Person.where {
    firstName == "Fred" && !(lastName == 'Simpson')
}

7.4.4. Property Comparison Queries

If you use a property name on both the left hand and right side of a comparison expression then the appropriate property comparison criteria is automatically used:

def query = Person.where {
   firstName == lastName
}

The following table described how each comparison operator maps onto each criteria property comparison method:

Operator Criteria Method Description

==

eqProperty

Equal to

!=

neProperty

Not equal to

>

gtProperty

Greater than

<

ltProperty

Less than

>=

geProperty

Greater than or equal to

leProperty

Less than or equal to

7.4.5. Querying Associations

Associations can be queried by using the dot operator to specify the property name of the association to be queried:

def query = Pet.where {
    owner.firstName == "Joe" || owner.firstName == "Fred"
}

You can group multiple criterion inside a closure method call where the name of the method matches the association name:

def query = Person.where {
    pets { name == "Jack" || name == "Joe" }
}

This technique can be combined with other top-level criteria:

def query = Person.where {
     pets { name == "Jack" } || firstName == "Ed"
}

For collection associations it is possible to apply queries to the size of the collection:

def query = Person.where {
       pets.size() == 2
}

The following table shows which operator maps onto which criteria method for each size() comparison:

Operator Criteria Method Description

==

sizeEq

The collection size is equal to

!=

sizeNe

The collection size is not equal to

>

sizeGt

The collection size is greater than

<

sizeLt

The collection size is less than

>=

sizeGe

The collection size is greater than or equal to

sizeLe

The collection size is less than or equal to

7.4.6. Query Aliases and Sorting

If you define a query for an association an alias is automatically generated for the query. For example the following query:

def query = Pet.where {
    owner.firstName == "Fred"
}

Will generate an alias for the owner association such as owner_alias_0. These generated aliases are fine for most cases, but are not useful if you want to later sort or use a projection on the results. For example the following query will fail:

// fails because a dynamic alias is used
Pet.where {
    owner.firstName == "Fred"
}.list(sort:"owner.lastName")

If you plan to sort the results then an explicit alias should be used and these can be defined by simply declaring a variable in the where query:

def query = Pet.where {
    def o1 = owner (1)
    o1.firstName == "Fred" (2)
}.list(sort:'o1.lastName') (3)
1 Define an alias called o1
2 Use the alias in the query itself
3 Use the alias to sort the results

By assigning the name of an association to a local variable it will automatically become an alias usable within the query itself and also for the purposes of sorting or projecting the results.

7.4.7. Subqueries

It is possible to execute subqueries within where queries. For example to find all the people older than the average age the following query can be used:

final query = Person.where {
  age > avg(age)
}

The following table lists the possible subqueries:

Method Description

avg

The average of all values

sum

The sum of all values

max

The maximum value

min

The minimum value

count

The count of all values

property

Retrieves a property of the resulting entities

You can apply additional criteria to any subquery by using the of method and passing in a closure containing the criteria:

def query = Person.where {
  age > avg(age).of { lastName == "Simpson" } && firstName == "Homer"
}

Since the property subquery returns multiple results, the criterion used compares all results. For example the following query will find all people younger than people with the surname "Simpson":

Person.where {
    age < property(age).of { lastName == "Simpson" }
}

7.4.8. More Advanced Subqueries in GORM

The support for subqueries has been extended. You can now use in with nested subqueries

def results = Person.where {
    firstName in where { age < 18 }.firstName
}.list()

Criteria and where queries can be seamlessly mixed:

def results = Person.withCriteria {
    notIn "firstName", Person.where { age < 18 }.firstName
 }

Subqueries can be used with projections:

def results = Person.where {
    age > where { age > 18 }.avg('age')
}

Correlated queries that span two domain classes can be used:

def employees = Employee.where {
    region.continent in ['APAC', "EMEA"]
    }.id()
    def results = Sale.where {
    employee in employees && total > 100000
    }.employee.list()

And support for aliases (cross query references) using simple variable declarations has been added to where queries:

def query = Employee.where {
    def em1 = Employee
    exists Sale.where {
        def s1 = Sale
        def em2 = employee
        return em2.id == em1.id
    }.id()
}
def results = query.list()

7.4.9. Other Functions

There are several functions available to you within the context of a query. These are summarized in the table below:

Method Description

second

The second of a date property

minute

The minute of a date property

hour

The hour of a date property

day

The day of the month of a date property

month

The month of a date property

year

The year of a date property

lower

Converts a string property to lower case

upper

Converts a string property to upper case

length

The length of a string property

trim

Trims a string property

Currently functions can only be applied to properties or associations of domain classes. You cannot, for example, use a function on a result of a subquery.

For example the following query can be used to find all pet’s born in 2011:

def query = Pet.where {
    year(birthDate) == 2011
}

You can also apply functions to associations:

def query = Person.where {
    year(pets.birthDate) == 2009
}

7.4.10. Batch Updates and Deletes

Since each where method call returns a DetachedCriteria instance, you can use where queries to execute batch operations such as batch updates and deletes. For example, the following query will update all people with the surname "Simpson" to have the surname "Bloggs":

DetachedCriteria<Person> query = Person.where {
    lastName == 'Simpson'
}
int total = query.updateAll(lastName:"Bloggs")
Note that one limitation with regards to batch operations is that join queries (queries that query associations) are not allowed.

To batch delete records you can use the deleteAll method:

DetachedCriteria<Person> query = Person.where {
    lastName == 'Simpson'
}
int total = query.deleteAll()

7.5. Criteria

Criteria is an advanced way to query that uses a Groovy builder to construct potentially complex queries. It is a much better approach than building up query strings using a StringBuilder.

Criteria can be used either with the createCriteria() or withCriteria(closure) methods.

The builder uses Hibernate’s Criteria API. The nodes on this builder map the static methods found in the Restrictions class of the Hibernate Criteria API. For example:

def c = Account.createCriteria()
def results = c {
    between("balance", 500, 1000)
    eq("branch", "London")
    or {
        like("holderFirstName", "Fred%")
        like("holderFirstName", "Barney%")
    }
    maxResults(10)
    order("holderLastName", "desc")
}

This criteria will select up to 10 Account objects in a List matching the following criteria:

  • balance is between 500 and 1000

  • branch is London

  • holderFirstName starts with Fred or Barney

The results will be sorted in descending order by holderLastName.

If no records are found with the above criteria, an empty List is returned.

7.5.1. Conjunctions and Disjunctions

As demonstrated in the previous example you can group criteria in a logical OR using an or { } block:

or {
    between("balance", 500, 1000)
    eq("branch", "London")
}

This also works with logical AND:

and {
    between("balance", 500, 1000)
    eq("branch", "London")
}

And you can also negate using logical NOT:

not {
    between("balance", 500, 1000)
    eq("branch", "London")
}

All top level conditions are implied to be AND’d together.

7.5.2. Querying Associations

Associations can be queried by having a node that matches the property name. For example say the Account class had many Transaction objects:

class Account {
    ...
    static hasMany = [transactions: Transaction]
    ...
}

We can query this association by using the property name transactions as a builder node:

def c = Account.createCriteria()
def now = new Date()
def results = c.list {
    transactions {
        between('date', now - 10, now)
    }
}

The above code will find all the Account instances that have performed transactions within the last 10 days. You can also nest such association queries within logical blocks:

def c = Account.createCriteria()
def now = new Date()
def results = c.list {
    or {
        between('created', now - 10, now)
        transactions {
            between('date', now - 10, now)
        }
    }
}

Here we find all accounts that have either performed transactions in the last 10 days OR have been recently created in the last 10 days.

7.5.3. Querying with Projections

Projections may be used to customise the results. Define a "projections" node within the criteria builder tree to use projections. There are equivalent methods within the projections node to the methods found in the Hibernate Projections class:

def c = Account.createCriteria()

def numberOfBranches = c.get {
    projections {
        countDistinct('branch')
    }
}

When multiple fields are specified in the projection, a List of values will be returned. A single value will be returned otherwise.

7.5.4. Transforming Projection Results

If the raw value or simple object array returned by the criteria method doesn’t suit your needs, the result can be transformed with a ResultTransformer. Let’s say we want to transform the criteria results into a Map so that we can easily reference values by key:

def c = Account.createCriteria()

def accountsOverview = c.get {
    resultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP)
    projections {
        sum('balance', 'allBalances')
        countDistinct('holderLastName', 'lastNames')
    }
}

// accountsOverview.allBalances
// accountsOverview.lastNames

Note that we’ve added an alias to each projection as an additional parameter to be used as the key. For this to work, all projections must have aliases defined, otherwise the corresponding map entry will not be built.

We can also transform the result into an object of our choosing via the Transformers.aliasToBean() method. In this case, we’ll transform it into an AccountsOverview:

class AccountsOverview {
    Number allBalances
    Number lastNames
}
def c = Account.createCriteria()

def accountsOverview = c.get {
    resultTransformer(Transformers.aliasToBean(AccountsOverview))
    projections {
        sum('balance', 'allBalances')
        countDistinct('holderLastName', 'lastNames')
    }
}

// accountsOverview instanceof AccountsOverview

Each alias must have a corresponding property or explicit setter on the bean otherwise an exception will be thrown.

7.5.5. SQL Projections

The criteria DSL provides access to Hibernate’s SQL projection API.

// Box is a domain class...
class Box {
    int width
    int height
}
// Use SQL projections to retrieve the perimeter and area of all of the Box instances...
def c = Box.createCriteria()

def results = c.list {
    projections {
      sqlProjection '(2 * (width + height)) as perimeter, (width * height) as area', ['perimeter', 'area'], [INTEGER, INTEGER]
    }
}

The first argument to the sqlProjection method is the SQL which defines the projections. The second argument is a list of Strings which represent column aliases corresponding to the projected values expressed in the SQL. The third argument is a list of org.hibernate.type.Type instances which correspond to the projected values expressed in the SQL. The API supports all org.hibernate.type.Type objects but constants like INTEGER, LONG, FLOAT etc. are provided by the DSL which correspond to all of the types defined in org.hibernate.type.StandardBasicTypes.

Consider that the following table represents the data in the BOX table.

width height

2

7

2

8

2

9

4

9

The query above would return results like this:

[[18, 14], [20, 16], [22, 18], [26, 36]]

Each of the inner lists contains the 2 projected values for each Box, perimeter and area.

Note that if there are other references in scope wherever your criteria query is expressed that have names that conflict with any of the type constants described above, the code in your criteria will refer to those references, not the type constants provided by the DSL. In the unlikely event of that happening you can disambiguate the conflict by referring to the fully qualified Hibernate type. For example StandardBasicTypes.INTEGER instead of INTEGER.

If only 1 value is being projected, the alias and the type do not need to be included in a list.

def results = c.list {
    projections {
      sqlProjection 'sum(width * height) as totalArea', 'totalArea', INTEGER
    }
}

That query would return a single result with the value of 84 as the total area of all of the Box instances.

The DSL supports grouped projections with the sqlGroupProjection method.

def results = c.list {
    projections {
        sqlGroupProjection 'width, sum(height) as combinedHeightsForThisWidth', 'width', ['width', 'combinedHeightsForThisWidth'], [INTEGER, INTEGER]
    }
}

The first argument to the sqlGroupProjection method is the SQL which defines the projections. The second argument represents the group by clause that should be part of the query. That string may be single column name or a comma separated list of column names. The third argument is a list of Strings which represent column aliases corresponding to the projected values expressed in the SQL. The fourth argument is a list of org.hibernate.type.Type instances which correspond to the projected values expressed in the SQL.

The query above is projecting the combined heights of boxes grouped by width and would return results that look like this:

[[2, 24], [4, 9]]

Each of the inner lists contains 2 values. The first value is a box width and the second value is the sum of the heights of all of the boxes which have that width.

7.5.6. Using SQL Restrictions

You can access Hibernate’s SQL Restrictions capabilities.

def c = Person.createCriteria()

def peopleWithShortFirstNames = c.list {
    sqlRestriction "char_length(first_name) <= 4"
}

SQL Restrictions may be parameterized to deal with SQL injection vulnerabilities related to dynamic restrictions.

def c = Person.createCriteria()

def peopleWithShortFirstNames = c.list {
    sqlRestriction "char_length(first_name) < ? AND char_length(first_name) > ?", [maxValue, minValue]
}
Note that the parameter there is SQL. The first_name attribute referenced in the example refers to the persistence model, not the object model like in HQL queries. The Person property named firstName is mapped to the first_name column in the database and you must refer to that in the sqlRestriction string.

Also note that the SQL used here is not necessarily portable across databases.

7.5.7. Using Scrollable Results

You can use Hibernate’s ScrollableResults feature by calling the scroll method:

def results = crit.scroll {
    maxResults(10)
}
def f = results.first()
def l = results.last()
def n = results.next()
def p = results.previous()

def future = results.scroll(10)
def accountNumber = results.getLong('number')

To quote the documentation of Hibernate ScrollableResults:

A result iterator that allows moving around within the results by arbitrary increments. The Query / ScrollableResults pattern is very similar to the JDBC PreparedStatement / ResultSet pattern and the semantics of methods of this interface are similar to the similarly named methods on ResultSet.

Contrary to JDBC, columns of results are numbered from zero.

7.5.8. Setting properties in the Criteria instance

If a node within the builder tree doesn’t match a particular criterion it will attempt to set a property on the Criteria object itself. This allows full access to all the properties in this class. This example calls setMaxResults and setFirstResult on the Criteria instance:

import org.hibernate.FetchMode as FM
...
def results = c.list {
    maxResults(10)
    firstResult(50)
    fetchMode("aRelationship", FM.JOIN)
}

7.5.9. Querying with Eager Fetching

In the section on Eager and Lazy Fetching we discussed how to declaratively specify fetching to avoid the N+1 SELECT problem. However, this can also be achieved using a criteria query:

def criteria = Task.createCriteria()
def tasks = criteria.list{
    eq "assignee.id", task.assignee.id
    join 'assignee'
    join 'project'
    order 'priority', 'asc'
}

Notice the usage of the join method: it tells the criteria API to use a JOIN to fetch the named associations with the Task instances. It’s probably best not to use this for one-to-many associations though, because you will most likely end up with duplicate results. Instead, use the select fetch mode:

import org.hibernate.FetchMode as FM
...
def results = Airport.withCriteria {
    eq "region", "EMEA"
    fetchMode "flights", FM.SELECT
}

Although this approach triggers a second query to get the flights association, you will get reliable results - even with the maxResults option.

fetchMode and join are general settings of the query and can only be specified at the top-level, i.e. you cannot use them inside projections or association constraints.

An important point to bear in mind is that if you include associations in the query constraints, those associations will automatically be eagerly loaded. For example, in this query:

def results = Airport.withCriteria {
    eq "region", "EMEA"
    flights {
        like "number", "BA%"
    }
}

the flights collection would be loaded eagerly via a join even though the fetch mode has not been explicitly set.

7.5.10. Method Reference

If you invoke the builder with no method name such as:

c { ... }

The build defaults to listing all the results and hence the above is equivalent to:

c.list { ... }
Method Description

list

This is the default method. It returns all matching rows.

get

Returns a unique result set i.e. just one row. The criteria has to be formed in a way that it only queries one row. This method is not to be confused with a limit to just the first row.

scroll

Returns a scrollable result set.

listDistinct

If subqueries or associations are used one may end up with the same row multiple times in the result set. This allows listing only distinct entities and is equivalent to DISTINCT_ROOT_ENTITY of the CriteriaSpecification class.

count

Returns the number of matching rows.

7.5.11. Combining Criteria

You can combine multiple criteria closures in the following way:

def emeaCriteria = {
    eq "region", "EMEA"
}

def results = Airport.withCriteria {
    emeaCriteria.delegate = delegate
    emeaCriteria()
    flights {
        like "number", "BA%"
    }
}

This technique requires that each criteria must refer to the same domain class (i.e. Airport). A more flexible approach is to use Detached Criteria, as described in the following section.

7.6. Detached Criteria

Detached Criteria are criteria queries that are not associated with any given database session/connection. Supported since Grails 2.0, Detached Criteria queries have many uses including allowing you to create common reusable criteria queries, execute subqueries and execute batch updates/deletes.

7.6.1. Building Detached Criteria Queries

The primary point of entry for using the Detached Criteria is the DetachedCriteria class which accepts a domain class as the only argument to its constructor:

import grails.gorm.*
...
def criteria = new DetachedCriteria(Person)

Once you have obtained a reference to a detached criteria instance you can execute where queries or criteria queries to build up the appropriate query. To build a normal criteria query you can use the build method:

def criteria = new DetachedCriteria(Person).build {
    eq 'lastName', 'Simpson'
}

Note that methods on the DetachedCriteria instance do not mutate the original object but instead return a new query. In other words, you have to use the return value of the build method to obtain the mutated criteria object:

def criteria = new DetachedCriteria(Person).build {
    eq 'lastName', 'Simpson'
}
def bartQuery = criteria.build {
    eq 'firstName', 'Bart'
}

7.6.2. Executing Detached Criteria Queries

Unlike regular criteria, Detached Criteria are lazy, in that no query is executed at the point of definition. Once a Detached Criteria query has been constructed then there are a number of useful query methods which are summarized in the table below:

Method Description

list

List all matching entities

get

Return a single matching result

count

Count all matching records

exists

Return true if any matching records exist

deleteAll

Delete all matching records

updateAll(Map)

Update all matching records with the given properties

As an example the following code will list the first 4 matching records sorted by the firstName property:

def criteria = new DetachedCriteria(Person).build {
    eq 'lastName', 'Simpson'
}
def results = criteria.list(max:4, sort:"firstName")

You can also supply additional criteria to the list method:

def results = criteria.list(max:4, sort:"firstName") {
    gt 'age', 30
}

To retrieve a single result you can use the get or find methods (which are synonyms):

Person p = criteria.find() // or criteria.get()

The DetachedCriteria class itself also implements the Iterable interface which means that it can be treated like a list:

def criteria = new DetachedCriteria(Person).build {
    eq 'lastName', 'Simpson'
}
criteria.each {
    println it.firstName
}

In this case the query is only executed when the each method is called. The same applies to all other Groovy collection iteration methods.

You can also execute dynamic finders on DetachedCriteria just like on domain classes. For example:

def criteria = new DetachedCriteria(Person).build {
    eq 'lastName', 'Simpson'
}
def bart = criteria.findByFirstName("Bart")

7.6.3. Using Detached Criteria for Subqueries

Within the context of a regular criteria query you can use DetachedCriteria to execute subquery. For example if you want to find all people who are older than the average age the following query will accomplish that:

def results = Person.withCriteria {
     gt "age", new DetachedCriteria(Person).build {
         projections {
             avg "age"
         }
     }
     order "firstName"
 }

Notice that in this case the subquery class is the same as the original criteria query class (i.e. Person) and hence the query can be shortened to:

def results = Person.withCriteria {
     gt "age", {
         projections {
             avg "age"
         }
     }
     order "firstName"
 }

If the subquery class differs from the original criteria query then you will have to use the original syntax.

In the previous example the projection ensured that only a single result was returned (the average age). If your subquery returns multiple results then there are different criteria methods that need to be used to compare the result. For example to find all the people older than the ages 18 to 65 a gtAll query can be used:

def results = Person.withCriteria {
    gtAll "age", {
        projections {
            property "age"
        }
        between 'age', 18, 65
    }

    order "firstName"
}

The following table summarizes criteria methods for operating on subqueries that return multiple results:

Method Description

gtAll

greater than all subquery results

geAll

greater than or equal to all subquery results

ltAll

less than all subquery results

leAll

less than or equal to all subquery results

eqAll

equal to all subquery results

neAll

not equal to all subquery results

7.6.4. Batch Operations with Detached Criteria

The DetachedCriteria class can be used to execute batch operations such as batch updates and deletes. For example, the following query will update all people with the surname "Simpson" to have the surname "Bloggs":

def criteria = new DetachedCriteria(Person).build {
    eq 'lastName', 'Simpson'
}
int total = criteria.updateAll(lastName:"Bloggs")
Note that one limitation with regards to batch operations is that join queries (queries that query associations) are not allowed within the DetachedCriteria instance.

To batch delete records you can use the deleteAll method:

def criteria = new DetachedCriteria(Person).build {
    eq 'lastName', 'Simpson'
}
int total = criteria.deleteAll()

7.7. Hibernate Query Language (HQL)

GORM classes also support Hibernate’s query language HQL, a very complete reference for which can be found in the Hibernate documentation of the Hibernate documentation.

GORM provides a number of methods that work with HQL including find, findAll and executeQuery.

An example of a query can be seen below:

def results =
      Book.findAll("from Book as b where b.title like 'Lord of the%'")

7.7.1. Named Parameters

In this case the value passed to the query is hard coded, however you can equally use named parameters:

def results =
      Book.findAll("from Book as b " +
                   "where b.title like :search or b.author like :search",
                   [search: "The Shi%"])
def author = Author.findByName("Stephen King")
def books = Book.findAll("from Book as book where book.author = :author",
                         [author: author])

7.7.2. Multiline Queries

Use the triple quoted strings to separate the query across multiple lines:

def results = Book.findAll("""
from Book as b,
     Author as a
where b.author = a and a.surname = :surname""", [surname:'Smith'])

7.7.3. Pagination and Sorting

You can also perform pagination and sorting whilst using HQL queries. To do so simply specify the pagination options as a Map at the end of the method call and include an "ORDER BY" clause in the HQL:

def results =
      Book.findAll("from Book as b where " +
                   "b.title like 'Lord of the%' " +
                   "order by b.title asc",
                   [max: 10, offset: 20])

8. Advanced GORM Features

The following sections cover more advanced usages of GORM including caching, custom mapping and events.

8.1. 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 operation 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.

Do not attempt to flush the session within an event (such as with obj.save(flush:true)). Since events are fired during flushing this will cause a StackOverflowError.

8.1.1. 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()
      }
   }
}

8.1.2. The beforeUpdate event

Fired before an existing object is updated

class Person {

   def securityService

   String firstName
   String lastName
   String lastUpdatedBy

   static constraints = {
      lastUpdatedBy nullable: true
   }

   static mapping = {
      autowire true
   }

   def beforeUpdate() {
      lastUpdatedBy = securityService.currentAuthenticatedUsername()
   }
}

Notice the usage of autowire true above. This is required for the bean securityService to be injected.

8.1.3. The beforeDelete event

Fired before an object is deleted.

class Person {
   String name

   def beforeDelete() {
      ActivityTrace.withNewSession {
         new ActivityTrace(eventName: "Person Deleted", data: name).save()
      }
   }
}

Notice the usage of withNewSession method above. Since events are triggered whilst Hibernate is flushing using persistence methods like save() and delete() won’t result in objects being saved unless you run your operations with a new Session.

Fortunately the withNewSession method lets you share the same transactional JDBC connection even though you’re using a different underlying Session.

8.1.4. The beforeValidate event

Fired before an object is validated.

class Person {
   String name

   static constraints = {
       name size: 5..45
   }

   def beforeValidate() {
       name = name?.trim()
   }
}

The beforeValidate method is run before any validators are run.

Validation may run more often than you think. It is triggered by the validate() and save() methods as you’d expect, but it is also typically triggered just before the view is rendered as well. So when writing beforeValidate() implementations, make sure that they can handle being called multiple times with the same property values.

GORM supports an overloaded version of beforeValidate which accepts a List parameter which may include the names of the properties which are about to be validated. This version of beforeValidate will be called when the validate method has been invoked and passed a List of property names as an argument.

class Person {
   String name
   String town
   Integer age

   static constraints = {
       name size: 5..45
       age range: 4..99
   }

   def beforeValidate(List propertiesBeingValidated) {
      // do pre validation work based on propertiesBeingValidated
   }
}

def p = new Person(name: 'Jacob Brown', age: 10)
p.validate(['age', 'name'])
Note that when validate is triggered indirectly because of a call to the save method that the validate method is being invoked with no arguments, not a List that includes all of the property names.

Either or both versions of beforeValidate may be defined in a domain class. GORM will prefer the List version if a List is passed to validate but will fall back on the no-arg version if the List version does not exist. Likewise, GORM will prefer the no-arg version if no arguments are passed to validate but will fall back on the List version if the no-arg version does not exist. In that case, null is passed to beforeValidate.

8.1.5. The onLoad/beforeLoad event

Fired immediately before an object is loaded from the database:

class Person {
   String name
   Date dateCreated
   Date lastUpdated

   def onLoad() {
      log.debug "Loading ${id}"
   }
}

beforeLoad() is effectively a synonym for onLoad(), so only declare one or the other.

8.1.6. The afterLoad event

Fired immediately after an object is loaded from the database:

class Person {
   String name
   Date dateCreated
   Date lastUpdated

   def afterLoad() {
      name = "I'm loaded"
   }
}

8.1.7. Custom Event Listeners

To register a custom event listener you need to subclass AbstractPersistenceEventListener (in package org.grails.datastore.mapping.engine.event) and implement the methods onPersistenceEvent and supportsEventType. You also must provide a reference to the datastore to the listener. The simplest possible implementation can be seen below:

public MyPersistenceListener(final Datastore datastore) {
    super(datastore)
}

@Override
protected void onPersistenceEvent(final AbstractPersistenceEvent event) {
    switch(event.eventType) {
        case PreInsert:
            println "PRE INSERT \${event.entityObject}"
        break
        case PostInsert:
            println "POST INSERT \${event.entityObject}"
        break
        case PreUpdate:
            println "PRE UPDATE \${event.entityObject}"
        break;
        case PostUpdate:
            println "POST UPDATE \${event.entityObject}"
        break;
        case PreDelete:
            println "PRE DELETE \${event.entityObject}"
        break;
        case PostDelete:
            println "POST DELETE \${event.entityObject}"
        break;
        case PreLoad:
            println "PRE LOAD \${event.entityObject}"
        break;
        case PostLoad:
            println "POST LOAD \${event.entityObject}"
        break;
    }
}

@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
    return true
}

The AbstractPersistenceEvent class has many subclasses (PreInsertEvent, PostInsertEvent etc.) that provide further information specific to the event. A cancel() method is also provided on the event which allows you to veto an insert, update or delete operation.

Once you have created your event listener you need to register it. If you are using Spring this can be done via the ApplicationContext:

HibernateDatastore datastore = applicationContext.getBean(HibernateDatastore)
applicationContext.addApplicationListener new MyPersistenceListener(datastore)

If you are not using Spring then you can register the event listener using the getApplicationEventPublisher() method:

HibernateDatastore datastore = ... // get a reference to the datastore
datastore.getApplicationEventPublisher()
         .addApplicationListener new MyPersistenceListener(datastore)

8.1.8. Hibernate Events

It is generally encouraged to use the non-Hibernate specific API described above, but if you need access to more detailed Hibernate events then you can define custom Hibernate-specific event listeners.

You can also register event handler classes in an application’s grails-app/conf/spring/resources.groovy or in the doWithSpring closure in a plugin descriptor by registering a Spring bean named hibernateEventListeners. This bean has one property, listenerMap which specifies the listeners to register for various Hibernate events.

The values of the Map are instances of classes that implement one or more Hibernate listener interfaces. You can use one class that implements all of the required interfaces, or one concrete class per interface, or any combination. The valid Map keys and corresponding interfaces are listed here:

Name Interface

auto-flush

AutoFlushEventListener

merge

MergeEventListener

create

PersistEventListener

create-onflush

PersistEventListener

delete

DeleteEventListener

dirty-check

DirtyCheckEventListener

evict

EvictEventListener

flush

FlushEventListener

flush-entity

FlushEntityEventListener

load

LoadEventListener

load-collection

InitializeCollectionEventListener

lock

LockEventListener

refresh

RefreshEventListener

replicate

ReplicateEventListener

save-update

SaveOrUpdateEventListener

save

SaveOrUpdateEventListener

update

SaveOrUpdateEventListener

pre-load

PreLoadEventListener

pre-update

PreUpdateEventListener

pre-delete

PreDeleteEventListener

pre-insert

PreInsertEventListener

pre-collection-recreate

PreCollectionRecreateEventListener

pre-collection-remove

PreCollectionRemoveEventListener

pre-collection-update

PreCollectionUpdateEventListener

post-load

PostLoadEventListener

post-update

PostUpdateEventListener

post-delete

PostDeleteEventListener

post-insert

PostInsertEventListener

post-commit-update

PostUpdateEventListener

post-commit-delete

PostDeleteEventListener

post-commit-insert

PostInsertEventListener

post-collection-recreate

PostCollectionRecreateEventListener

post-collection-remove

PostCollectionRemoveEventListener

post-collection-update

PostCollectionUpdateEventListener

For example, you could register a class AuditEventListener which implements PostInsertEventListener, PostUpdateEventListener, and PostDeleteEventListener using the following in an application:

beans = {

   auditListener(AuditEventListener)

   hibernateEventListeners(HibernateEventListeners) {
      listenerMap = ['post-insert': auditListener,
                     'post-update': auditListener,
                     'post-delete': auditListener]
   }
}

or use this in a plugin:

def doWithSpring = {

   auditListener(AuditEventListener)

   hibernateEventListeners(HibernateEventListeners) {
      listenerMap = ['post-insert': auditListener,
                     'post-update': auditListener,
                     'post-delete': auditListener]
   }
}

8.1.9. 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.

If this is not the behaviour you want you can disable this feature with:

class Person {
   Date dateCreated
   Date lastUpdated
   static mapping = {
      autoTimestamp false
   }
}
If you have nullable: false constraints on either dateCreated or lastUpdated, your domain instances will fail validation - probably not what you want. Omit constraints from these properties unless you disable automatic timestamping.

It is also possible to disable the automatic timestamping temporarily. This is most typically done in the case of a test where you need to define values for the dateCreated or lastUpdated in the past. It may also be useful for importing old data from other systems where you would like to keep the current values of the timestamps.

Timestamps can be temporarily disabled for all domains, a specified list of domains, or a single domain. To get started, you need to get a reference to the AutoTimestampEventListener. If you already have access to the datastore, you can execute the getAutoTimestampEventListener method. If you don’t have access to the datastore, inject the autoTimestampEventListener bean.

Once you have a reference to the event listener, you can execute withoutDateCreated, withoutLastUpdated, or withoutTimestamps. The withoutTimestamps method will temporarily disable both dateCreated and lastUpdated.

Example:

//Only the dateCreated property handling will be disabled for only the Foo domain
autoTimestampEventListener.withoutDateCreated(Foo) {
    new Foo(dateCreated: new Date() - 1).save(flush: true)
}

//Only the lastUpdated property handling will be disabled for only the Foo and Bar domains
autoTimestampEventListener.withoutLastUpdated(Foo, Bar) {
    new Foo(lastUpdated: new Date() - 1, bar: new Bar(lastUpdated: new Date() + 1)).save(flush: true)
}

//All timestamp property handling will be disabled for all domains
autoTimestampEventListener.withoutTimestamps {
    new Foo(dateCreated: new Date() - 2, lastUpdated: new Date() - 1).save(flush: true)
    new Bar(dateCreated: new Date() - 2, lastUpdated: new Date() - 1).save(flush: true)
    new FooBar(dateCreated: new Date() - 2, lastUpdated: new Date() - 1).save(flush: true)
}
Because the timestamp handling is only disabled for the duration of the closure, you must flush the session during the closure execution!

8.2. Custom ORM Mapping

GORM domain classes can be mapped onto many legacy schemas with an Object Relational Mapping DSL (domain specific language). The following sections 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 names, column names and so on. You only needs this functionality if you need to tailor the way GORM maps onto legacy schemas or configures caching

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
}

It has the same syntax as the standard mapping block but it applies to all your domain classes! You can then override these defaults within the mapping block of a domain class.

8.2.1. 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.

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.

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. As an example if you had a PostCodeType you could use it as follows:

class Address {

    String number
    String postCode

    static mapping = {
        postCode type: PostCodeType
    }
}

Alternatively if you just wanted to map it to one of Hibernate’s basic types other than the default chosen by GORM you could use:

class Address {

    String number
    String postCode

    static mapping = {
        postCode type: 'text'
    }
}

This would make the postCode column map to the default large-text type for the database you’re using (for example TEXT or CLOB).

See the Hibernate documentation regarding Basic Types for further information.

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'
    }
}

By default the address association would map to a foreign key column called address_id. By using the above mapping we have changed the name of the foreign key column to Person_Adress_Id.

One-to-Many Mapping

With a bidirectional one-to-many you can change the foreign key column used by changing the column name on the many side of the association as per the example in the previous section on one-to-one associations. However, with unidirectional associations the foreign key needs to be specified on the association itself. For example given a unidirectional one-to-many relationship between Person and Address the following code will change the foreign key in the address table:

class Person {

    String firstName

    static hasMany = [addresses: Address]

    static mapping = {
        table 'people'
        firstName column: 'First_Name'
        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 {

    String firstName

    static hasMany = [addresses: Address]

    static mapping = {
        table 'people'
        firstName column: 'First_Name'
        addresses joinTable: [name: 'Person_Addresses',
                              key: 'Person_Id',
                              column: 'Address_Id']
    }
}
Many-to-Many Mapping

GORM, by default maps a many-to-many association using a join table. For example consider this many-to-many association:

class Group {
    ...
    static hasMany = [people: Person]
}
class Person {
    ...
    static belongsTo = Group
    static hasMany = [groups: Group]
}

In this case GORM will create a join table called group_person containing foreign keys called person_id and group_id referencing the person and group tables. To change the column names you can specify a column within the mappings for each class.

class Group {
   ...
   static mapping = {
       people column: 'Group_Person_Id'
   }
}
class Person {
   ...
   static mapping = {
       groups column: 'Group_Group_Id'
   }
}

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'
   }
}

8.2.2. Caching Strategy

Setting up caching

Hibernate features a second-level cache with a customizable cache provider. This needs to be configured in the grails-app/conf/application.yml file as follows:

hibernate:
  cache:
    use_second_level_cache: true
    provider_class: net.sf.ehcache.hibernate.EhCacheProvider
    region:
       factory_class: org.hibernate.cache.ehcache.EhCacheRegionFactory

You can customize any of these settings, for example to use a distributed caching mechanism.

For further reading on caching and in particular Hibernate’s second-level cache, refer to the Hibernate documentation on the subject.
Caching instances

Call the cache method in your mapping block to enable caching with the default settings:

class Person {
    ...
    static mapping = {
        table 'people'
        cache true
    }
}

This will configure a read-write cache that includes both lazy and non-lazy properties. You can customize this further:

class Person {
    ...
    static mapping = {
        table 'people'
        cache usage: 'read-only', include: 'non-lazy'
    }
}
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
        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.

Caching Queries

In order for the results of queries to be cached, you must enable caching in your mapping:

hibernate:
  cache:
    use_query_cache: true

To enable query caching for all queries created by dynamic finders, GORM etc. you can specify:

hibernate:
  cache:
    queries: true    # This implicitly sets  `use_query_cache=true`

You can cache queries such as dynamic finders and criteria. To do so using a dynamic finder you can pass the cache argument:

def person = Person.findByFirstName("Fred", [cache: true])

You can also cache criteria queries:

def people = Person.withCriteria {
    like('firstName', 'Fr%')
    cache true
}
Cache usages

Below is a description of the different cache settings and their usages:

  • read-only - If your application needs to read but never modify instances of a persistent class, a read-only cache may be used.

  • read-write - If the application needs to update data, a read-write cache might be appropriate.

  • nonstrict-read-write - If the application only occasionally needs to update data (i.e. if it is very unlikely that two transactions would try to update the same item simultaneously) and strict transaction isolation is not required, a nonstrict-read-write cache might be appropriate.

  • transactional - The transactional cache strategy provides support for fully transactional cache providers such as JBoss TreeCache. Such a cache may only be used in a JTA environment and you must specify hibernate.transaction.manager_lookup_class in the grails-app/conf/application.groovy file’s hibernate config.

8.2.3. Inheritance Strategies

By default GORM classes use table-per-hierarchy inheritance mapping. This has the disadvantage that columns cannot have a NOT-NULL constraint applied to them at the database level. If you would prefer to use a table-per-subclass inheritance strategy you can do so as follows:

class Payment {
    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 for all child classes.

8.2.4. Custom Database Identity

You can customize how GORM generates identifiers for the database using the DSL. By default GORM 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 customize the id generator and the column it 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.

For more information on the different Hibernate generators refer to the Hibernate reference documentation

Although you don’t typically specify the id field (GORM adds it for you) you can still configure its mapping like the other properties. For example to customise the column for the id property you can do:

class Person {
    ...
    static mapping = {
        table 'people'
        version false
        id column: 'person_id'
    }
}

8.2.5. Composite Primary Keys

GORM supports the concept of composite identifiers (identifiers composed from 2 or more properties). It is not an approach we recommend, but is available to you if you need it:

import org.apache.commons.lang.builder.HashCodeBuilder

class Person implements Serializable {

    String firstName
    String lastName

    boolean equals(other) {
        if (!(other instanceof Person)) {
            return false
        }

        other.firstName == firstName && other.lastName == lastName
    }

    int hashCode() {
        def builder = new HashCodeBuilder()
        builder.append firstName
        builder.append lastName
        builder.toHashCode()
    }

    static mapping = {
        id composite: ['firstName', 'lastName']
    }
}

The above will create a composite id of the firstName and lastName properties of the Person class. To retrieve an instance by id you use a prototype of the object itself:

def p = Person.get(new Person(firstName: "Fred", lastName: "Flintstone"))
println p.firstName

Domain classes mapped with composite primary keys must implement the Serializable interface and override the equals and hashCode methods, using the properties in the composite key for the calculations. The example above uses a HashCodeBuilder for convenience but it’s fine to implement it yourself.

Another important consideration when using composite primary keys is associations. If for example you have a many-to-one association where the foreign keys are stored in the associated table then 2 columns will be present in the associated table.

For example consider the following domain class:

class Address {
    Person person
}

In this case the address table will have an additional two columns called person_first_name and person_last_name. If you wish the change the mapping of these columns then you can do so using the following technique:

class Address {
    Person person
    static mapping = {
        columns {
                    person {
                column name: "FirstName"
                column name: "LastName"
                        }
        }
    }
}

8.2.6. 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 are used in which indexes:

class Person {
    String firstName
    String address
    static mapping = {
        table 'people'
        version false
        id column: 'person_id'
        firstName column: 'First_Name', index: 'Name_Idx'
        address column: 'Address', index: 'Name_Idx,Address_Index'
    }
}

Note that you cannot have any spaces in the value of the index attribute; in this example index:'Name_Idx, Address_Index' will cause an error.

8.2.7. Optimistic Locking and Versioning

As discussed in the section on Optimistic and Pessimistic Locking, by default GORM uses optimistic locking and automatically injects a version property into every class which is in turn mapped to a version column at the database level.

If you’re mapping to a legacy schema that doesn’t have version columns (or there’s some other reason why you don’t want/need this feature) you can disable this with the version method:

class Person {
    ...
    static mapping = {
        table 'people'
        version false
    }
}
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 data (due to data overriding) unless you use pessimistic locking
Version columns types

By default GORM maps the version property as a Long that gets incremented by one each time an instance is updated. But Hibernate also supports using a Timestamp, for example:

import java.sql.Timestamp

class Person {

    ...
    Timestamp version

    static mapping = {
        table 'people'
    }
}

There’s a slight risk that two updates occurring at nearly the same time on a fast server can end up with the same timestamp value but this risk is very low. One benefit of using a Timestamp instead of a Long is that you combine the optimistic locking and last-updated semantics into a single column.

8.2.8. Eager and Lazy Fetching

Lazy Collections

As discussed in the section on Eager and Lazy fetching, GORM collections are lazily loaded by default but you can change this behaviour with the ORM DSL. There are several options available to you, but the most common ones are:

  • lazy: false

  • fetch: join

and they’re used like this:

class Person {

    String firstName
    Pet pet

    static hasMany = [addresses: Address]

    static mapping = {
        addresses lazy: false
        pet fetch: 'join'
    }
}
class Address {
    String street
    String postCode
}
class Pet {
    String name
}

The first option, lazy: false , ensures that when a Person instance is loaded, its addresses collection is loaded at the same time with a second SELECT. The second option is basically the same, except the collection is loaded with a JOIN rather than another SELECT. Typically you want to reduce the number of queries, so fetch: 'join' is the more appropriate option. On the other hand, it could feasibly be the more expensive approach if your domain model and data result in more and larger results than would otherwise be necessary.

For more advanced users, the other settings available are:

  • batchSize: N

  • lazy: false, batchSize: N

where N is an integer. These let you fetch results in batches, with one query per batch. As a simple example, consider this mapping for Person:

class Person {

    String firstName
    Pet pet

    static mapping = {
        pet batchSize: 5
    }
}

If a query returns multiple Person instances, then when we access the first pet property, Hibernate will fetch that Pet plus the four next ones. You can get the same behaviour with eager loading by combining batchSize with the lazy: false option.

You can find out more about these options in the Hibernate user guide. Note that ORM DSL does not currently support the "subselect" fetching strategy.

Lazy Single-Ended Associations

In GORM, one-to-one and many-to-one associations are by default lazy. Non-lazy single ended associations can be problematic when you load many entities because each non-lazy association will result in an extra SELECT statement. If the associated entities also have non-lazy associations, the number of queries grows significantly!

Use the same technique as for lazy collections to make a one-to-one or many-to-one association non-lazy/eager:

class Person {
    String firstName
}
class Address {

    String street
    String postCode

    static belongsTo = [person: Person]

    static mapping = {
        person lazy: false
    }
}

Here we configure GORM to load the associated Person instance (through the person property) whenever an Address is loaded.

Lazy Associations and Proxies

Hibernate uses runtime-generated proxies to facilitate single-ended lazy associations; Hibernate dynamically subclasses the entity class to create the proxy.

Consider the previous example but with a lazily-loaded person association: Hibernate will set the person property to a proxy that is a subclass of Person. When you call any of the getters (except for the id property) or setters on that proxy, Hibernate will load the entity from the database.

Unfortunately this technique can produce surprising results. Consider the following example classes:

class Pet {
    String name
}
class Dog extends Pet {
}
class Person {
    String name
    Pet pet
}

Proxies can have confusing behavior when combined with inheritance. Because the proxy is only a subclass of the parent class, any attempt to cast or access data on the subclass will fail. Assuming we have a single Person instance with a Dog as the pet.

The code below will not fail because directly querying the Pet table does not require the resulting objects to be proxies because they are not lazy.

def pet = Pet.get(1)
assert pet instanceof Dog

The following code will fail because the association is lazy and the pet instance is a proxy.

def person = Person.get(1)
assert person.pet instanceof Dog

If the only goal is to check if the proxy is an instance of a class, there is one helper method available to do so that works with proxies. Take special care in using it though because it does cause a call to the database to retrieve the association data.

def person = Person.get(1)
assert person.pet.instanceOf(Dog)

There are a couple of ways to approach this issue. The first rule of thumb is that if it is known ahead of time that the association data is required, join the data in the query of the Person. For example, the following assertion is true.

def person = Person.where { id == 1 }.join("pet").get()
assert person.pet instanceof Dog

In the above example the pet association is no longer lazy because it is being retrieved along with the Person and thus no proxies are necessary. There are cases when it makes sense for a proxy to be returned, mostly in the case where its impossible to know if the data will be used or not. For those cases in order to access properties of the subclasses, the proxy must be unwrapped. To unwrap a proxy inject an instance of ProxyHandler and pass the proxy to the unwrap method.

def person = Person.get(1)
assert proxyHandler.unwrap(person.pet) instanceof Dog

For cases where dependency injection is impractical or not available, a helper method GrailsHibernateUtil.unwrapIfProxy(Object) can be used instead.

Unwrapping a proxy is different than initializing it. Initializing a proxy simply populates the underlying instance with data from the database, however unwrapping a returns the inner target.

8.2.9. Custom Cascade Behaviour

As described in the section on cascading updates, the primary mechanism to control the way updates and deletes cascade from one association to another is the static belongsTo property.

However, the ORM DSL gives you complete access to Hibernate’s transitive persistence capabilities using the cascade attribute.

Valid settings for the cascade attribute include:

  • merge - merges the state of a detached association

  • save-update - cascades only saves and updates to an association

  • delete - cascades only deletes to an association

  • lock - useful if a pessimistic lock should be cascaded to its associations

  • refresh - cascades refreshes to an association

  • evict - cascades evictions (equivalent to discard() in GORM) to associations if set

  • all - cascade all operations to associations

  • all-delete-orphan - Applies only to one-to-many associations and indicates that when a child is removed from an association then it should be automatically deleted. Children are also deleted when the parent is.

To specify the cascade attribute simply define one or more (comma-separated) of the aforementioned settings as its value:

class Person {

    String firstName

    static hasMany = [addresses: Address]

    static mapping = {
        addresses cascade: "all-delete-orphan"
    }
}
class Address {
    String street
    String postCode
}

8.2.10. Custom Hibernate Types

You saw in an earlier section that you can use composition (with the embedded property) to break a table into multiple objects. You can achieve a similar effect with Hibernate’s custom user types. These are not domain classes themselves, but plain Java or Groovy classes. Each of these types also has a corresponding "meta-type" class that implements org.hibernate.usertype.UserType.

The Hibernate reference manual has some information on custom types, but here we will focus on how to map them in GORM. Let’s start by taking a look at a simple domain class that uses an old-fashioned (pre-Java 1.5) type-safe enum class:

class Book {

    String title
    String author
    Rating rating

    static mapping = {
        rating type: RatingUserType
    }
}

All we have done is declare the rating field the enum type and set the property’s type in the custom mapping to the corresponding UserType implementation. That’s all you have to do to start using your custom type. If you want, you can also use the other column settings such as "column" to change the column name and "index" to add it to an index.

Custom types aren’t limited to just a single column - they can be mapped to as many columns as you want. In such cases you explicitly define in the mapping what columns to use, since Hibernate can only use the property name for a single column. Fortunately, GORM lets you map multiple columns to a property using this syntax:

class Book {

    String title
    Name author
    Rating rating

    static mapping = {
        author type: NameUserType, {
            column name: "first_name"
            column name: "last_name"
        }
        rating type: RatingUserType
    }
}

The above example will create "first_name" and "last_name" columns for the author property. You’ll be pleased to know that you can also use some of the normal column/property mapping attributes in the column definitions. For example:

column name: "first_name", index: "my_idx", unique: true

The column definitions do not support the following attributes: type, cascade, lazy, cache, and joinTable.

One thing to bear in mind with custom types is that they define the SQL types for the corresponding database columns. That helps take the burden of configuring them yourself, but what happens if you have a legacy database that uses a different SQL type for one of the columns? In that case, override the column’s SQL type using the sqlType attribute:

class Book {

    String title
    Name author
    Rating rating

    static mapping = {
        author type: NameUserType, {
            column name: "first_name", sqlType: "text"
            column name: "last_name", sqlType: "text"
        }
        rating type: RatingUserType, sqlType: "text"
    }
}

Mind you, the SQL type you specify needs to still work with the custom type. So overriding a default of "varchar" with "text" is fine, but overriding "text" with "yes_no" isn’t going to work.

8.2.11. Derived Properties

A derived property is one that takes its value from a SQL expression, often but not necessarily based on the value of one or more other persistent properties. Consider a Product class like this:

class Product {
    Float price
    Float taxRate
    Float tax
}

If the tax property is derived based on the value of price and taxRate properties then is probably no need to persist the tax property. The SQL used to derive the value of a derived property may be expressed in the ORM DSL like this:

class Product {
    Float price
    Float taxRate
    Float tax

    static mapping = {
        tax formula: 'PRICE * TAX_RATE'
    }
}

Note that the formula expressed in the ORM DSL is SQL so references to other properties should relate to the persistence model not the object model, which is why the example refers to PRICE and TAX_RATE instead of price and taxRate.

With that in place, when a Product is retrieved with something like Product.get(42), the SQL that is generated to support that will look something like this:

select
    product0_.id as id1_0_,
    product0_.version as version1_0_,
    product0_.price as price1_0_,
    product0_.tax_rate as tax4_1_0_,
    product0_.PRICE * product0_.TAX_RATE as formula1_0_
from
    product product0_
where
    product0_.id=?

Since the tax property is derived at runtime and not stored in the database it might seem that the same effect could be achieved by adding a method like getTax() to the Product class that simply returns the product of the taxRate and price properties. With an approach like that you would give up the ability query the database based on the value of the tax property. Using a derived property allows exactly that. To retrieve all Product objects that have a tax value greater than 21.12 you could execute a query like this:

Product.findAllByTaxGreaterThan(21.12)

Derived properties may be referenced in the Criteria API:

Product.withCriteria {
    gt 'tax', 21.12f
}

The SQL that is generated to support either of those would look something like this:

select
    this_.id as id1_0_,
    this_.version as version1_0_,
    this_.price as price1_0_,
    this_.tax_rate as tax4_1_0_,
    this_.PRICE * this_.TAX_RATE as formula1_0_
from
    product this_
where
    this_.PRICE * this_.TAX_RATE>?
Because the value of a derived property is generated in the database and depends on the execution of SQL code, derived properties may not have GORM constraints applied to them. If constraints are specified for a derived property, they will be ignored.

8.2.12. Custom Naming Strategy

By default GORM uses Hibernate’s ImprovedNamingStrategy to convert domain class Class and field names to SQL table and column names by converting from camel-cased Strings to ones that use underscores as word separators. You can customize these on a per-class basis in the mapping closure but if there’s a consistent pattern you can specify a different NamingStrategy class to use.

Configure the class name to be used in grails-app/conf/application.groovy in the hibernate section, e.g.

dataSource {
    pooled = true
    dbCreate = "create-drop"
    ...
}

hibernate {
    cache.use_second_level_cache = true
    ...
    naming_strategy = com.myco.myproj.CustomNamingStrategy
}

You can also specify the name of the class and it will be loaded for you:

hibernate {
    ...
    naming_strategy = 'com.myco.myproj.CustomNamingStrategy'
}

A third option is to provide an instance if there is some configuration required beyond calling the default constructor:

hibernate {
    ...
    def strategy = new com.myco.myproj.CustomNamingStrategy()
    // configure as needed
    naming_strategy = strategy
}

You can use an existing class or write your own, for example one that prefixes table names and column names:

package com.myco.myproj

import org.hibernate.cfg.ImprovedNamingStrategy
import org.hibernate.util.StringHelper

class CustomNamingStrategy extends ImprovedNamingStrategy {

    String classToTableName(String className) {
        "table_" + StringHelper.unqualify(className)
    }

    String propertyToColumnName(String propertyName) {
        "col_" + StringHelper.unqualify(propertyName)
    }
}

8.3. Default Sort Order

You can sort objects using query arguments such as those found in the list method:

def airports = Airport.list(sort:'name')

However, you can also declare the default sort order for a collection in the mapping:

class Airport {
    ...
    static mapping = {
        sort "name"
    }
}

The above means that all collections of Airport instances will by default be sorted by the airport name. If you also want to change the sort order, use this syntax:

class Airport {
    ...
    static mapping = {
        sort name: "desc"
    }
}

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.

These mappings will not work for default unidirectional one-to-many or many-to-many relationships because they involve a join table. See this issue for more details. Consider using a SortedSet or queries with sort parameters to fetch the data you need.

9. Programmatic Transactions

GORM’s transaction management is built on Spring and uses Spring’s Transaction abstraction for dealing with programmatic transactions.

However, GORM classes have been enhanced to make this simpler with the withTransaction(Closure) method. This method has a single parameter, a Closure, which has a single parameter which is a Spring TransactionStatus instance.

9.1. Using the withTransaction Method

Here’s an example of using withTransaction in a controller methods:

def transferFunds() {
    Account.withTransaction { status ->
        def source = Account.get(params.from)
        def dest = Account.get(params.to)

        def amount = params.amount.toInteger()
        if (source.active) {
            if (dest.active) {
                source.balance -= amount
                dest.amount += amount
            }
            else {
                status.setRollbackOnly()
            }
        }
    }
}

In this example we rollback the transaction if the destination account is not active.

Also, if an Exception (both checked or runtime exception) or Error is thrown during the process the transaction will automatically be rolled back..

GORM versions prior to 6.0.0 did not roll back transactions for a checked Exception.

You can also use "save points" to rollback a transaction to a particular point in time if you don’t want to rollback the entire transaction. This can be achieved through the use of Spring’s SavePointManager interface.

The withTransaction method deals with the begin/commit/rollback logic for you within the scope of the block.

9.2. Using TransactionService

Since GORM 6.1, if you need more flexibility then instead you can instead take advantage of the TransactionService, which can be obtained by looking it up from from the HibernateDatastore:

import grails.gorm.transactions.*

TransactionService transactionService = datastore.getService(TransactionService)

Or via dependency injection:

import grails.gorm.transactions.*

@Autowired TransactionService transactionService

Once you have an instance then there are various including withTransaction, withRollback, withNewTransaction etc. which helps with the construction of programmatic transactions.

10. GORM Data Services

Introduced in GORM 6.1, Data Services take the work out of implemented service layer logic by adding the ability to automatically implement abstract classes or interfaces using GORM logic.

To illustrate what GORM Data Services are about let’s walk through an example.

10.1. Data Service Basics

10.1.1. Writing a Simple Data Service

In a Grails application you can create a Data Service in either src/main/groovy or grails-app/services. To write a Data Service you should create either an interface (although abstract classes can also be used, more about that later) and annotate it with the grails.gorm.services.Service annotation with the domain class the service applies to:

@Service(Book)
interface BookService {
    Book getBook(Serializable id)
}

The @Service annotation is an AST transformation that will automatically implement the service for you. You can then obtain the service via Spring autowiring:

@Autowired BookService bookService

Or if you are using GORM standalone by looking it up from the HibernateDatastore instance:

BookService bookService = hibernateDatastore.getService(BookService)
The above example also works in Spock unit tests that extend HibernateSpec

10.1.2. How Does it Work?

The @Service transformation will look at the the method signatures of the interface and make a best effort to find a way to implement each method.

If a method cannot be implemented then a compilation error will occur. At this point you have the option to use an abstract class instead and provide an implementation yourself.

The @Service transformation will also generate a META-INF/services file for the service so it can be discovered via the standard Java service loader. So no additional configuration is necessary.

10.1.3. Advantages of Data Services

There are several advantages to Data Services that make them worth considering to abstract your persistence logic.

  • Type Safety - Data service method signatures are compile time checked and compilation will fail if the types of any parameters don’t match up with properties in your domain class

  • Testing - Since Data Services are interfaces this makes them easy to test via Spock Mocks

  • Performance - The generated services are statically compiled and unlike competing technologies in the Java space no proxies are created so runtime performance doesn’t suffer

  • Transaction Management - Each method in a Data Service is wrapped in an appropriate transaction (a read-only transaction in the case of read operations) that can be easily overridden.

10.1.4. Abstract Class Support

If you come across a method that GORM doesn’t know how to implement, then you can provide an implementation by using an abstract class.

For example:

interface IBookService {
    Book getBook(Serializable id)
    Date someOtherMethod()
}
@Service(Book)
abstract class BookService implements IBookService {

   @Override
   Date someOtherMethod() {
      // impl
   }
}

In this case GORM will implement the interface methods that have not been defined by the abstract class.

In addition, all public methods of the domain class will be automatically wrapped in the appropriate transaction handling.

What this means is that you can define protected abstract methods that are non-transactional in order to compose logic. For example:

@Service(Book)
abstract class BookService  {

   protected abstract Book getBook(Serializable id) (1)

   protected abstract Author getAuthor(Serializable id) (1)

   Book updateBook(Serializable id, Serializable authorId) { (2)
      Book book = getBook(id)
      if(book != null) {
          Author author = getAuthor(authorId)
          if(author == null) {
              throw new IllegalArgumentException("Author does not exist")
          }
          book.author = author
          book.save()
      }
      return book
   }
}
1 Two protected abstract methods are defined that are not wrapped in transaction handling
2 The updateBook method uses the two methods that are implemented automatically by GORM and being public is automatically made transactional.
If you have public methods that you do not wish to be transactional, then you can annotate them with @NotTransactional

10.2. Data Service Queries

GORM Data Services will implement queries for you using a number of different strategies and conventions.

It does this by looking at the return type of a method and the method stem and picking the most appropriate implementation.

The following table summarizes the conventions:

Table 1. Data Service Conventions
Method Stem Description Possible Return Types

count*

Count the number of results

Subclass of Number or Observable<Number>

countBy*

Dynamic Finder Count the number of results

Subclass of Number or Observable<Number>

delete*

Delete an instance for the given arguments

T, void, subclass of Number or Observable<Number>

find*, get*, list* or retrieve*

Query for the given parameters

T, Iterable<T>, T[], List<T>, Observable<T>

findBy*, listBy*, findAllBy* or getBy*

Dynamic finder query for given parameters

T, Iterable<T>, T[], List<T>, Observable<T>

save*, store*, or persist*

Save a new instance

T or Observable<T>

update*

Updates an existing instance. First parameter should be id

T or Observable<T>

The conventions are extensible (more on that later), in terms of queries there are two distinct types.

10.2.1. Simple Queries

Simple queries are queries that use the arguments of the method. For example:

@Service(Book)
interface BookService {
    Book findBook(String title)
}

In the example above the Data Service will generate the implementation based on the fact that the title parameter matches the title property of the Book class both in terms of name and type.

If you were to misspell the title parameter or use an incorrect type then a compilation error will occur.

You can alter the return type to return more results:

@Service(Book)
interface BookService {
    List<Book> findBooks(String title)
}

And if you wish to control pagination and query arguments you can add an args parameter that should be a Map:

@Service(Book)
interface BookService {
    List<Book> findBooks(String title, Map args)
}

In this case the following query will control pagination and ordering:

List<Book> books = bookService.findBooks(
    "The Stand",
    [offset:10, max:10, sort:'title', order:'desc']
)

You can include multiple parameters in the query:

@Service(Book)
interface BookService {
    List<Book> findBooks(String title, Date publishDate)
}

In this case a conjunction (AND) query will be executed. If you need to do a disjunction (OR) then it is time you learn about Dynamic Finder-style queries.

10.2.2. Dynamic Finder Queries

Dynamic finder styles queries use the stem plus the word By and then a Dynamic Finder expression.

For example:

@Service(Book)
interface BookService {
    List<Book> findByTitleAndPublishDateGreaterThan(String title, Date publishDate)
}

The signature above will produce a dynamic finder query using the method signature expression.

The possible method expressions are the same as those possible with GORM’s static Dynamic Finders

In this case the names of the properties to query are inferred from the method signature and the parameter names are not critical. If you misspell the method signature a compilation error will occur.

10.2.3. Where Queries

If you have a more complex query then you may want to consider using the @Where annotation:

    @Where({ title ==~ pattern && releaseDate > fromDate })
    Book searchBooks(String pattern, Date fromDate)

With the @Where annotation the method name can be anything you want and query is expressed within a closure passed with the @Where annotation.

The query will be type checked against the parameters and compilation will fail if you misspell a parameter or property name.

The syntax is the same as what is passed to GORM’s static where method, see the section on Where Queries for more information.

10.3. Query Joins

You can specify query joins using the @Join annotation:

import static javax.persistence.criteria.JoinType.*

@Service(Book)
interface BookService {
    @Join('author')
    Book find(String title) (1)

    @Join(value='author', type=LEFT) (2)
    Book findAnother(String title)
}
1 Join on the author property
2 Join on the author property using a LEFT OUTER join

10.3.1. JPA-QL Queries

If you need even more flexibility, then HQL queries can be used via the @Query annotation:

@Query("from $Book as book where book.title like $pattern")
Book searchByTitle(String pattern)

Note that in the example above, if you misspell the pattern parameter passed to the query a compilation error will occur.

However, if you were to incorrectly input the title property no error would occur since it is part of the String and not a variable.

You can resolve this by declaring the value of book within the passed GString:

@Query("from ${Book book} where ${book.title} like $pattern")
Book searchByTitle(String pattern)

In the above example if you misspell the title property then a compilation error will occur. This is extremely powerful as it gives you the ability to type check HQL queries, which has always been one of the disadvantages of using them in comparison to criteria.

This support for type checked queries extends to joins. For example consider this query:

@Query("""
 from ${Book book} (1)
 inner join ${Author author = book.author} (2)
 where $book.title = $title and $author.name = $author""") (3)
Book find(String title, String author)
1 Using from to define the root query
2 Use inner join and a declaration to define the association to join on
3 Apply any conditions in the where clause

10.3.2. Query Projections

There are a few ways to implement projections. One way is to is to use the convention T find[Domain Class][Property]. For example say the Book class has a releaseDate property of type Date:

@Service(Book)
interface BookService {
   Date findBookReleaseDate(String title)
}

This also works for multiple results:

@Service(Book)
interface BookService {
   List<Date> findBookReleaseDate(String publisher)
}

And you can use the Map argument to provide ordering and pagination if necessary:

@Service(Book)
interface BookService {
   List<Date> findBookReleaseDate(String publisher, Map args)
}
JPA-QL Projections

You can also use a JPA-QL query to perform a projection:

@Service(Book)
interface BookService {
   @Query("select $b.releaseDate from ${Book b} where $b.publisher = $publisher order by $b.releaseDate")
   List<Date> findBookReleaseDates(String publisher)
}
Interface Projections

Sometimes you want to expose a more limited set of a data to the calling class. In this case it is possible to use interface projections.

For example:

class Author {
    String name
    Date dateOfBirth (1)
}
interface AuthorInfo {
    String getName() (2)
}
@Service(Author)
interface AuthorService {
   AuthorInfo find(String name) (3)
}
1 The domain class Author has a property called dateOfBirth that we do not want to make available to the client
2 You can define an interface that only exposes the properties you want to expose
3 Return the interface from the service.
If a property exists on the interface but not on the domain class you will receive a compilation error.

10.4. Data Service Write Operations

Write operations in Data Services are automatically wrapped in a transaction. You can modify the transactional attributes by simply adding the @Transactional transformation to any method.

The following sections discuss the details of the different write operations.

10.4.1. Create

To create a new entity the method should return the new entity and feature either the parameters to be used to create the entity or the entity itself.

For example:

@Service(Book)
interface BookService {
    Book saveBook(String title)

    Book saveBook(Book newBook)
}

If any of the parameters don’t match up to a property on the domain class then a compilation error will occur.

If a validation error occurs then a ValidationException will be thrown from the service.

10.4.2. Update

Update operations are similar to Create operations, the main difference being that the first argument should be the id of the object to update.

For example:

@Service(Book)
interface BookService {
    Book updateBook(Serializable id, String title)
}

If any of the parameters don’t match up to a property on the domain class then a compilation error will occur.

If a validation error occurs then a ValidationException will be thrown from the service.

You can also implement update operations using JPA-QL:

@Query("update ${Book book} set ${book.title} = $newTitle where $book.title = $oldTitle")
Number updateTitle(String newTitle, String oldTitle)

10.4.3. Delete

Delete operations can either return void or return the instance that was deleted. In the latter case an extra query is required to fetch the entity prior to issue a delete.

@Service(Book)
interface BookService {
    Number deleteAll(String title)

    void delete(Serializable id)
}

You can also implement delete operations using JPA-QL:

@Query("delete ${Book book} where $book.title = $title")
void delete(String title)

Or via where queries:

@Where({ title == title && releaseDate > date })
void delete(String title, Date date)

10.5. Validating Data Services

GORM Data Services have built in support for javax.validation annotations for method parameters.

You will need to have a javax.validation implementation on your classpath (such as hibernate-validator and then simply annotate your method parameters using the appropriate annotation. For example:

import javax.validation.constraints.*

@Service(Book)
interface BookService {

    Book find(@NotNull String title)
}

In the above example the NotNull constraint is applied to the title property. If null is passed to the method a ConstraintViolationException exception will be thrown.

10.6. RxJava Support

GORM Data Services also support returning RxJava 1.x rx.Observable or rx.Single types.

RxJava 2.x support is planned for a future release

To use the RxJava support you need to ensure that the grails-datastore-gorm-rx dependencies is on the classpath by adding the following to build.gradle:

build.gradle
compile "org.grails:grails-datastore-gorm-rx:8.1.1"

For example:

import rx.*

@Service(Book)
interface BookService {
   Single<Book> findOne(String title)
}

When a rx.Single is used then a single result is returned. To query multiple results use an rx.Observable instead:

import rx.*

@Service(Book)
interface BookService {
   Observable<Book> findBooks(String title)
}

For regular GORM entities, GORM will by default execute the persistence operation using RxJava’s IO Scheduler.

For RxGORM entities where the underlying database supports non-blocking access the database driver will schedule the operation accordingly.

You can run the operation on a different scheduler using the RxSchedule annotation:

import rx.*
import grails.gorm.rx.services.RxSchedule
import grails.gorm.services.Service
import rx.schedulers.Schedulers

@Service(Book)
interface BookService {

   @RxSchedule(scheduler = { Schedulers.newThread() })
   Observable<Book> findBooks(String title)
}

11. Multiple Data Sources

GORM supports the notion of multiple data sources where multiple individual SQL DataSource instances can be configured and switched between.

11.1. Configuring Multiple Data Sources

To configure multiple data sources you need to use the dataSources setting. For example in application.yml:

dataSource:
    pooled: true
    dbCreate: create-drop
    url: jdbc:h2:mem:books
    driverClassName: org.h2.Driver
    username: sa
    password:
dataSources:
    moreBooks:
        url: jdbc:h2:mem:moreBooks
        hibernate:
            readOnly: true
    evenMoreBooks:
        url: jdbc:h2:mem:evenMoreBooks

You can configure individual settings for each data source. If a setting is not specified by default the setting is inherited from the default data source, so in the example above there is no need to specify the driverClassName for each data source if the same driver is used for all.

For more information on configuration see the Configuration section.

11.2. Mapping Domain Classes to Data Sources

If a domain class has no DataSource configuration, it defaults to the standard 'dataSource'. Set the datasource property in the mapping block to configure a non-default DataSource. For example, if you want to use the ZipCode domain to use a DataSource called 'lookup', configure it like this:

class ZipCode {

   String code

   static mapping = {
      datasource 'lookup'
   }
}

A domain class can also use two or more configured DataSource instances. Use the datasources property with a list of names to configure more than one, for example:

class ZipCode {

   String code

   static mapping = {
      datasources(['lookup', 'auditing'])
   }
}

If a domain class uses the default DataSource and one or more others, you can use the ConnectionSource.DEFAULT constant to indicate that:

import org.grails.datastore.mapping.core.connections.*

class ZipCode {

   String code

   static mapping = {
      datasources(['lookup', ConnectionSource.DEFAULT])
   }
}

If a domain class uses all configured DataSource instances use the value ALL:

import org.grails.datastore.mapping.core.connections.*

class ZipCode {

   String code

   static mapping = {
      datasource ConnectionSource.ALL
   }
}

11.3. Data Source Namespaces

If a domain class uses more than one DataSource then you can use the namespace implied by each DataSource name to make GORM calls for a particular DataSource. For example, consider this class which uses two DataSource instances:

class ZipCode {

   String code

   static mapping = {
      datasources(['lookup', 'auditing'])
   }
}

The first DataSource specified is the default when not using an explicit namespace, so in this case we default to lookup. But you can call GORM methods on the auditing DataSource with the DataSource name, for example:

def zipCode = ZipCode.auditing.get(42)
...
zipCode.auditing.save()

As you can see, you add the DataSource to the method call in both the static case and the instance case.

You can use Where queries:

def results = ZipCode.where {
    code ==~ '995%'
}.withConnection('auditing').list()

or Criteria queries:

def c = ZipCode.auditing.createCriteria()
def results = c.list {
    like('code','995%')
}

11.4. The ConnectionSources API

Introduced in GORM 6.0, the ConnectionSources API allows you to introspect the data sources configured for the application:

@Autowired
HibernateDatastore hibernateDatastore
...
ConnectionSources<SessionFactory, HibernateConnectionSourceSettings> connectionSources
                                        = hibernateDatastore.getConnectionSources()

for(ConnectionSource<SessionFactory, HibernateConnectionSourceSettings> connectionSource in connectionSources) {
        println "Name $connectionSource.name"
        SessionFactory sessionFactory = connectionSource.source
}

12. Multi-Tenancy

Multi-Tenancy, as it relates to software developments, is when a single instance of an application is used to service multiple clients (tenants) in a way that each tenants' data is isolated from the other.

This type of architecture is highly common in Software as a Service (SaaS) and Cloud architectures. There are a variety of approaches to multi-tenancy and GORM tries to be flexible in supporting as many as possible.

12.1. Multi-Tenancy Modes

GORM supports the following different multitenancy modes:

  • DATABASE - A separate database with a separate connection pool is used to store each tenants data.

  • SCHEMA - The same database, but different schemas are used to store each tenants data.

  • DISCRIMINATOR - The same database is used with a discriminator used to partition and isolate data.

The above modes are listed from least probable to leak data between tenants to most probable. When using a DISCRIMINATOR approach much greater care needs to be taken to ensure tenants don’t see each other’s data.

12.2. Multi-Tenancy Transformations

The following transformations can be applied to any class to simplify greatly the development of Multi-Tenant applications. These include:

  • @CurrentTenant - Resolve the current tenant for the context of a class or method

  • @Tenant - Use a specific tenant for the context of a class or method

  • @WithoutTenant - Execute logic without a specific tenant (using the default connection)

For example:

import grails.gorm.multitenancy.*

// resolve the current tenant for every method
@CurrentTenant
class TeamService {

    // execute the countPlayers method without a tenant id
    @WithoutTenant
    int countPlayers() {
        Player.count()
    }

    // use the tenant id "another" for all GORM logic within the method
    @Tenant({"another"})
    List<Team> allTwoTeams() {
        Team.list()
    }

    List<Team> listTeams() {
        Team.list(max:10)
    }

    @Transactional
    void addTeam(String name) {
        new Team(name:name).save(flush:true)
    }
}

12.3. Database Per Tenant

Using a database per tenant is the most secure way to isolate each tenants data and builds upon GORM’s existing support for Multiple Data Sources.

12.3.1. Configuration

In order to activate database-per-tenant multi-tenancy you need to set the multi-tenancy mode to DATABASE in your configuration and supply a TenantResolver:

grails:
    gorm:
        multiTenancy:
            mode: DATABASE
            tenantResolverClass: org.grails.datastore.mapping.multitenancy.web.SubDomainTenantResolver
dataSource:
    dbCreate: create-drop
    url: jdbc:h2:mem:books
dataSources:
    moreBooks:
        url: jdbc:h2:mem:moreBooks
    evenMoreBooks:
        url: jdbc:h2:mem:evenMoreBooks

The above example uses a built-in TenantResolver implementation that works with Grails or Spring Boot and evaluates the current tenant id from the DNS sub-domain in a web application. However, you can implement whatever tenant resolving strategy you choose.

12.3.2. Multi Tenant Domain Classes

With the above configuration in place you then to need to implement the MultiTenant trait in the domain classes you want to be regarded as multi tenant:

class Book implements MultiTenant<Book> {
    String title
}

With that done whenever you attempt to execute a method on a domain class the tenant id will be resolved via the TenantResolver. So for example if the TenantResolver returns moreBooks then the moreBooks connection will be used when calling GORM methods such as save(), list() and so on.

12.3.3. Multi Tenancy and the Session Factory

Note that if you reference the default SessionFactory or PlatformTransactionManager in your classes that are injected via Spring, these will not be tenant aware and will point directly to default data source.

If you wish to obtain a specific SessionFactory or PlatformTransactionManager then you can use the getDatastoreForConnection(name) method of the HibernateDatastore class:

@Autowired
HibernateDatastore hibernateDatastore
...
Serializable tenantId = Tenants.currentId(HibernateDatastore)
SessionFactory sessionFactory = hibernateDatastore
                                    .getDatastoreForConnection(tenantId.toString())
                                    .getSessionFactory()

12.3.4. Multi Tenancy with Sessions

When working with GORM typically you need a session bound to the current thread in order for GORM and transactions to work consistently. If you switch to a different tenant then it may be that the session bound to the current thread no longer matches the underlying SessionFactory being used by the tenant. With this in mind you may wants to use the Tenants class to ensure the correct session is bound for the current tenant:

import static grails.gorm.multitenancy.Tenants.*

List<Book> books = withCurrent {
    Book.list()
}

You can also use a specify tenant id using the withId method:

import static grails.gorm.multitenancy.Tenants.*

List<Book> books = withId("moreBooks") {
    Book.list()
}

Note that if you are using more than one GORM implementation, it may be necessary to specify the implementation type:

import static grails.gorm.multitenancy.Tenants.*
import org.grails.orm.hibernate.*

List<Book> books = withId(HibernateDatastore, "moreBooks") {
    Book.list()
}

12.3.5. Adding Tenants at Runtime

Provisioning and creating SQL databases at runtime is non-trivial and beyond the scope of what GORM offers, however the ConnectionSources API does provide a way to hook into GORM to make this possible.

By default an InMemoryConnectionSources object is used which will read the connection sources from the application configuration and store them in-memory.

However, it is possible to configure an alternate implementation:

grails:
    gorm:
        connectionSourcesClass: com.example.MyConnectionSources
        multiTenancy:
            mode: DATABASE

The implementation could read the connection sources at startup from another database table and implement logic by overriding the addConnectionSource method to provision a new databases at runtime.

If you are interested in more examples, an implementation exists for MongoDB that reads connection sources from a MongoDB collection. However, it doesn’t implement support for provision MongoDB instances at runtime.

12.4. Schema Per Tenant

Schema-per-tenant is when a single database is used, but a different database schema is used for each tenant.

12.4.1. Configuration

In order to activate schema-per-tenant multi-tenancy you need to set the multi-tenancy mode to SCHEMA in your configuration and supply a TenantResolver:

grails:
    gorm:
        multiTenancy:
            mode: SCHEMA
            tenantResolverClass: foo.bar.MySchemaResolver
dataSource:
    dbCreate: create-drop
    url: jdbc:h2:mem:books

The TenantResolver can optionally implement the AllTenantsResolver interface and return the schema names of all tenants. This gives you the option to hard code these, place them in configuration or read them from the default schema dynamically.

If the AllTenantsResolver resolver is not implemented then GORM will use the configured SchemaHandler to resolve all the schema names from the database which it will use for the tenants.

12.4.2. Runtime Schema Creation

On startup, if dataSource.dbCreate is set to create the database at runtime, then GORM will attempt to create the schemas if they are missing.

To do this it uses an instance SchemaHandler which by default uses the CREATE SCHEMA [name] syntax, but can be overridden and customized for other database dialects as necessary.

If you want to use this feature and create schemas at runtime depending on the database you may need to configure an alternate implementation:

dataSource:
    dbCreate: create-drop
    schemaHandler: foo.bar.MySchemaHandler

You can disable completely runtime schema creation by removing the dbCreate option or setting it to none.

If you wish to add a schema whilst the application is running then you can use the addTenantForSchema of the HibernateDatastore class:

HibernateDatastore datastore = ...
datastore.addTenantForSchema("myNewSchema")
If dbCreate is disabled then you will have to create the schema manually prior to invoking this method

12.4.3. Schema-Per-Tenant Caveats

In order to support a schema-per-tenant, just like the DATABASE Multi-Tenancy mode, GORM uses a unique SessionFactory per tenant. So all of the same considerations regarding session management apply.

12.5. Partitioned Multi-Tenancy

Partitioned multi-tenancy is when a discriminator column is used in each multi-tenant class in order to partition the data.

When using a discriminator all data for all tenants is stored in a single database. This is means that, as a developer, you have to take much greater care to ensure that each tenants' data is correctly isolated.

12.5.1. Configuration

In order to activate discriminator-based multi-tenancy you need to set the multi-tenancy mode to DISCRIMINATOR in your configuration and supply a TenantResolver:

grails:
    gorm:
        multiTenancy:
            mode: DISCRIMINATOR
            tenantResolverClass: foo.bar.MyTenantResolver
dataSource:
    dbCreate: create-drop
    url: jdbc:h2:mem:books
The specified TenantResolver will also need to implement the AllTenantsResolver interface, which has an additional method to resolve all known tenants.

12.5.2. Mapping Domain Classes

Like the other forms of multi-tenancy you then need to implement the MultiTenant trait in the domain classes that are multi-tenant. In addition, you also need to specify a discriminator column. The default discriminator column is called tenantId, for example:

class Book implements MultiTenant<Book> {
    Long tenantId
    String title
}

However, the tenant identifier can be any type or name. For example:

class Book implements MultiTenant<Book> {
    String publisher
    String title

    static mapping = {
        tenantId name:'publisher'
    }
}

12.5.3. Querying and Caveats

The discriminator-based multi-tenancy transparently adds a Hibernate filter that is activated when you query the domain class and you can also use the Tenants API to switch between tenants:

import static grails.gorm.multitenancy.Tenants.*

List<Book> books = withCurrent {
    Book.list()
}
...
List<Book> books = withId("moreBooks") {
    Book.list()
}

Note that this automatic activation of the Hibernate filter using GORM eventing and therefore only works when you use GORM methods, if you perform any raw Hibernate queries on the session factory you will need to activate the filter manually:

import static grails.gorm.multitenancy.Tenants.*
import org.grails.orm.hibernate.*

// get a reference to the datastore and sessionFactory, probably from Spring
HibernateDatastore datastore = ..
SessionFactory sessionFactory = ..
datastore.enableMultiTenancyFilter()

// do work with the session factory
sessionFactory.openSession()

12.6. Understanding Tenant Resolvers

As mentioned previously the TenantResolver interface is how you define the value of the current tenant.

This section will cover a few more details about implementing TenantResolver instances.

12.6.1. Specifying the Tenant Resolver

As already mentioned you can specify the TenantResolver via configuration using the grails.gorm.tenantResolverClass setting:

grails:
    gorm:
        multiTenancy:
            mode: DATABASE
            tenantResolverClass: org.grails.datastore.mapping.multitenancy.web.SubDomainTenantResolver

However, if you are using Grails or Spring Boot then the TenantResolver can also be specified as a Spring bean and it will automatically be injected, this allows you to use dependency injection to configure the dependencies of the resolver.

12.6.2. Built-in Tenant Resolvers

The following table contains all of the TenantResolver implementations that ship with GORM and are usable out of the box. The package name has been shorted from org.grails.datastore.mapping to o.g.d.m for brevity:

name description

o.g.d.m.multitenancy.resolvers.FixedTenantResolver

Resolves against a fixed tenant id

o.g.d.m.multitenancy.resolvers.SystemPropertyTenantResolver

Resolves the tenant id from a system property called gorm.tenantId

o.g.d.m.multitenancy.web.SubDomainTenantResolver

Resolves the tenant id from the subdomain via DNS

o.g.d.m.multitenancy.web.CookieTenantResolver

Resolves the current tenant from an HTTP cookie named gorm.tenantId by default

o.g.d.m.multitenancy.web.SessionTenantResolver

Resolves the current tenant from the HTTP session using the attribute gorm.tenantId by default

o.g.d.m.multitenancy.web.HttpHeaderTenantResolver

Resolves the current tenant from the request HTTP Header using the header name gorm.tenantId by default

The tenant resolvers in the org.grails.datastore.mapping.multitenancy.web package require the grails-datastore-web dependency:

build.gradle
compile "org.grails:grails-datastore-web:8.1.1"

12.6.3. The AllTenantsResolver interface

If you are using discriminator-based multi-tenancy then you may need to implement the AllTenantsResolver interface in your TenantResolver implementation if you want to at any point iterate over all available tenants.

Typically with discriminator-based multi-tenancy the tenants are identified by some other domain class property. So for example an implementation would look like:

Iterable<Serializable> resolveTenantIds() {
    new DetachedCriteria(Company)
            .distinct('name')
            .list()
}

The above example uses the distinct names of each Company domain class to resolve all of the tenant identifiers.

12.6.4. Implementing Web Tenant Resolvers

If you wish to implement your own tenant resolver for Grails or Spring Boot then it is possible do so using the RequestContextHolder class without needing to inject any dependencies. For example the SubDomainTenantResolver implementation is as follows:

Serializable resolveTenantIdentifier() {

    RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes()
    if(requestAttributes instanceof ServletWebRequest) {

        String subdomain = ((ServletWebRequest)requestAttributes).getRequest().getRequestURL().toString();
        subdomain = subdomain.substring(subdomain.indexOf("/") + 2);
        if( subdomain.indexOf(".") > -1 ) {
            return subdomain.substring(0, subdomain.indexOf("."))
        }
        else {
            return ConnectionSource.DEFAULT
        }
    }
    throw new TenantNotFoundException("Tenant could not be resolved outside a web request")
}
If the tenant id is not found a TenantNotFoundException should be thrown.

13. Validation and Constraints

Constraints are how you define validation when using GORM entities.

13.1. Applying Constraints

Within a domain class constraints are defined with the constraints property that is assigned a code block:

class User {
    String login
    String password
    String email
    Integer age

    static constraints = {
      ...
    }
}

You then use method calls that match the property name for which the constraint applies in combination with named parameters to specify constraints:

class User {
    ...

    static constraints = {
        login size: 5..15, blank: false, unique: true
        password size: 5..15, blank: false
        email email: true, blank: false
        age min: 18
    }
}

In this example we’ve declared that the login property must be between 5 and 15 characters long, it cannot be blank and must be unique. We’ve also applied other constraints to the password, email and age properties.

By default, all domain class properties are not nullable (i.e. they have an implicit nullable: false constraint).

Note that constraints are only evaluated once which may be relevant for a constraint that relies on a value like an instance of java.util.Date.

class User {
    ...

    static constraints = {
        // this Date object is created when the constraints are evaluated, not
        // each time an instance of the User class is validated.
        birthDate max: new Date()
    }
}

13.2. Referencing Instances in Constraints

It’s very easy to attempt to reference instance variables from the static constraints block, but this isn’t legal in Groovy (or Java). If you do so, you will get a MissingPropertyException for your trouble. For example, you may try the following:

class Response {
    Survey survey
    Answer answer

    static constraints = {
        survey blank: false
        answer blank: false, inList: survey.answers
    }
}

See how the inList constraint references the instance property survey? That won’t work. Instead, use a custom validator constraint:

class Response {
    ...
    static constraints = {
        survey blank: false
        answer blank: false, validator: { val, Response obj -> val in obj.survey.answers }
    }
}

In this example, 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.

13.3. Cascade constraints validation

If GORM entity references some other entities, then during its constraints evaluation (validation) the constraints of the referenced entity could be evaluated also, if needed. There is a special parameter cascadeValidate in the entity mappings section, which manage the way of this cascaded validation happens.

class Author {
    Publisher publisher

    static mapping = {
        publisher(cascadeValidate: "dirty")
    }
}

class Publisher {
    String name

    static constraints = {
        name blank: false
    }
}

The following table presents all options, which can be used:

Option Description

none

Will not do any cascade validation at all for the association.

default

The DEFAULT option. GORM performs cascade validation in some cases.

dirty

Only cascade validation if the referenced object is dirty via the DirtyCheckable trait. If the object doesn’t implement DirtyCheckable, this will fall back to default.

owned

Only cascade validation if the entity owns the referenced object.

It is possible to set the global option for the cascadeValidate:

grails-app/conf/application.groovy
grails.gorm.default.mapping = {
    '*'(cascadeValidate: 'dirty')
}

13.4. Constraints Reference

The following table summarizes the available constraints with a brief example:

Constraint Description Example

blank

Validates that a String value is not blank

login(blank:false)

creditCard

Validates that a String value is a valid credit card number

cardNumber(creditCard: true)

email

Validates that a String value is a valid email address.

homeEmail(email: true)

inList

Validates that a value is within a range or collection of constrained values.

name(inList: ["Joe"])

matches

Validates that a String value matches a given regular expression.

login(matches: "[a-zA-Z]+")

max

Validates that a value does not exceed the given maximum value.

age(max: new Date()) price(max: 999F)

maxSize

Validates that a value’s size does not exceed the given maximum value.

children(maxSize: 25)

min

Validates that a value does not fall below the given minimum value.

age(min: new Date()) price(min: 0F)

minSize

Validates that a value’s size does not fall below the given minimum value.

children(minSize: 25)

notEqual

Validates that that a property is not equal to the specified value

login(notEqual: "Bob")

nullable

Allows a property to be set to null - defaults to false.

age(nullable: true)

range

Uses a Groovy range to ensure that a property’s value occurs within a specified range

age(range: 18..65)

scale

Set to the desired scale for floating point numbers (i.e. the number of digits to the right of the decimal point).

salary(scale: 2)

size

Uses a Groovy range to restrict the size of a collection or number or the length of a String.

children(size: 5..15)

unique

Constrains a property as unique at the database level

login(unique: true)

url

Validates that a String value is a valid URL.

homePage(url: true)

validator

Adds custom validation to a field.

See documentation

13.5. Constraints and Database Mapping

Although constraints are primarily for validation, it is important to understand that constraints can affect the way in which the database schema is generated.

Where feasible, GORM uses a domain class’s constraints to influence the database columns generated for the corresponding domain class properties.

Consider the following example. Suppose we have a domain model with the following properties:

String name
String description

By default, in MySQL, GORM would define these columns as

Column Data Type

name

varchar(255)

description

varchar(255)

But perhaps the business rules for this domain class state that a description can be up to 1000 characters in length. If that were the case, we would likely define the column as follows if we were creating the table with an SQL script.

Column Data Type

description

TEXT

Chances are we would also want to have some application-based validation to make sure we don’t exceed that 1000 character limit before we persist any records. In GORM, we achieve this validation with constraints. We would add the following constraint declaration to the domain class.

static constraints = {
    description maxSize: 1000
}

This constraint would provide both the application-based validation we want and it would also cause the schema to be generated as shown above. Below is a description of the other constraints that influence schema generation.

13.5.1. Constraints Affecting String Properties

  • inList

  • maxSize

  • size

If either the maxSize or the size constraint is defined, Grails sets the maximum column length based on the constraint value.

In general, it’s not advisable to use both constraints on the same domain class property. However, if both the maxSize constraint and the size constraint are defined, then GORM sets the column length to the minimum of the maxSize constraint and the upper bound of the size constraint. (GORM uses the minimum of the two, because any length that exceeds that minimum will result in a validation error.)

If the inList constraint is defined (and the maxSize and the size constraints are not defined), then GORM sets the maximum column length based on the length of the longest string in the list of valid values. For example, given a list including values "Java", "Groovy", and "C++", GORM would set the column length to 6 (i.e., the number of characters in the string "Groovy").

13.5.2. Constraints Affecting Numeric Properties

  • min

  • max

  • range

If the max, min, or range constraint is defined, GORM attempts to set the column precision based on the constraint value. (The success of this attempted influence is largely dependent on how Hibernate interacts with the underlying DBMS.)

In general, it’s not advisable to combine the pair min/max and range constraints together on the same domain class property. However, if both of these constraints is defined, then GORM uses the minimum precision value from the constraints. (GORM uses the minimum of the two, because any length that exceeds that minimum precision will result in a validation error.)

  • scale

If the scale constraint is defined, then GORM attempts to set the column scale based on the constraint value. This rule only applies to floating point numbers (i.e., java.lang.Float, java.Lang.Double, java.lang.BigDecimal, or subclasses of java.lang.BigDecimal). The success of this attempted influence is largely dependent on how Hibernate interacts with the underlying DBMS.

The constraints define the minimum/maximum numeric values, and GORM derives the maximum number of digits for use in the precision. Keep in mind that specifying only one of min/max constraints will not affect schema generation (since there could be large negative value of property with max:100, for example), unless the specified constraint value requires more digits than default Hibernate column precision is (19 at the moment). For example:

someFloatValue max: 1000000, scale: 3

would yield:

someFloatValue DECIMAL(19, 3) // precision is default

but

someFloatValue max: 12345678901234567890, scale: 5

would yield:

someFloatValue DECIMAL(25, 5) // precision = digits in max + scale

and

someFloatValue max: 100, min: -100000

would yield:

someFloatValue DECIMAL(8, 2) // precision = digits in min + default scale

14. GORM and Testing

In previous versions of GORM it was much more difficult to setup a unit test to test your GORM logic.

However, since GORM 6.0, this situation has changed and it is relatively trivial to setup GORM for testing.

14.1. Unit Testing with Spock

Spock is the recommended tool for writing unit tests with GORM and is trivial to setup.

14.1.1. GORM with Hibernate and Spock Basics

The following is an example Spock unit test:

import spock.lang.*
import grails.gorm.annotation.Entity
import org.grails.orm.hibernate.HibernateDatastore

class ExampleSpec extends Specification { (1)

    @Shared @AutoCleanup HibernateDatastore hibernateDatastore (2)

    void setupSpec() {
       hibernateDatastore = new HibernateDatastore(Person) (3)
    }

    void "test something"() { (4)
       // your logic here
    }
}

@Entity (5)
class Person {
    ...
}
1 The test should extend spock.lang.Specification
2 The Shared annotation is used to indicate to Spock that the HibernateDatastore is shared across all tests. The AutoCleanup annotation makes sure that HibernateDatastore is shutdown when all tests finish executing.
3 Within the setupSpec method a new HibernateDatastore is constructed with the classes to use as the argument to the constructor.
4 You then write your test logic within each method
5 You can inline domain classes within the unit test if you annotate them with @Entity

14.1.2. Spock and Transactions

Note that in general you have to wrap your test execution logic in a session or transaction. The easiest way to do this is with grails.gorm.transactions.Transactional:

...
import grails.gorm.transactions.*
import org.springframework.transaction.PlatformTransactionManager

class ExampleSpec extends Specification {

    @Shared @AutoCleanup HibernateDatastore hibernateDatastore
    @Shared PlatformTransactionManager transactionManager (1)

    void setupSpec() {
        hibernateDatastore = new HibernateDatastore(Person)
        transactionManager = hibernateDatastore.getTransactionManager() (2)
    }

    @Transactional (3)
    def setup() {
        new Person(firstName:"Fred").save()
    }

    @Rollback (4)
    void "test execute GORM standalone in a unit test"() {
        // your logic here
    }
}
1 The PlatformTransactionManager is defined as a Shared field
2 You can obtain the PlatformTransactionManager from the HibernateDatastore
3 The Transactional annotation is used to setup test data
4 The Rollback annotation is used to rollback any changes made within each test

In the example above, each test method is wrapped in a transaction that rolls back any changes using the grails.gorm.transactions.Rollback annotation.

If you want to setup some test data within the setupSpec method that is shared across all tests then you can use withTransaction:
...
void setupSpec() {
    hibernateDatastore = new HibernateDatastore(Person)
    ...
    Person.withTransaction {
        new Person(firstName:"Fred").save()
    }
}

14.1.3. Configuring GORM in Spock

If you need to configure GORM within a Spock unit test you can pass a map to the constructor of HibernateDatastore. For example to setup multi-tenancy:

...
void setupSpec() {
    Map configuration = [
        'grails.gorm.multiTenancy.mode':'DISCRIMINATOR',
        'grails.gorm.multiTenancy.tenantResolverClass':SystemPropertyTenantResolver
    ]
    hibernateDatastore = new HibernateDatastore(configuration, Person)
    ...
}

14.2. Unit Testing with JUnit

To unit test with JUnit it is largely similar to Spock, just following different idioms.

So instead of setupSpec use @BeforeClass:

import org.junit.*
import grails.gorm.transactions.*
import org.grails.orm.hibernate.HibernateDatastore
import org.springframework.transaction.PlatformTransactionManager

class ExampleTest  {

    static HibernateDatastore hibernateDatastore

    PlatformTransactionManager transactionManager

    @BeforeClass
    void setupGorm() {
       hibernateDatastore = new HibernateDatastore(Person)
    }

    @AfterClass
    void shutdownGorm() {
       hibernateDatastore.close()
    }

    @Before
    void setup() {
        transactionManager = hibernateDatastore.getTransactionManager()
    }

    @Rollback
    @Test
    void testSomething() {
       // your logic here
    }
}
JUnit doesn’t have anything like Spock’s AutoCleanup so you must call close() on the HibernateDatastore manually!