This page last changed on Mar 24, 2006 by [email protected].

Grails Object Relational Mapping (GORM)

Introduction

Domain classes are core to any business application. They hold state about business processes and hopefully also implement behavior. They are linked together through relationships, either one-to-one or one-to-many.

GORM is Grails' object relational mapping (ORM) implementation. Under the hood it uses Hibernate 3 (an extremely popular and flexible open source ORM solution) but because of the dynamic nature of Groovy, the fact that it supports both static and dynamic typing and the convention of Grails there is less configuration involved in creating Grails domain classes.

You can also write Grails domain class in Java see the section on Hibernate Integration for how to write Grails domain classes in Java but still use dynamic persistent methods.

Creating a Domain Class

A domain class in Grails is essentially a normal Groovy class with 2 key characteristics: An "id" property and a "version" property:

Book.groovy
class Book {
    @Property Long id
    @Property Long version

    @Property String title
}

To help you get started you can run the following convenience target from the root of your Grails project:

grails create-domain-class

Creating Relationships

Defining a one-to-many relationship is a simple matter of defining a property of type Set and adding the mapping to the "relatesToMany" map:

Author.groovy
class Author {
    @Property Long id
    @Property Long version

    @Property relatesToMany = [ books : Book ]

    @Property String name
    @Property Set books = new HashSet()
}

One of the main features of Groovy is its support for static typing which means one-to-one and many-to-one relationships require no additions to the "relationships" map:

Book.groovy
class Book {
    @Property Long id
    @Property Long version

    @Property Author author
    @Property String title
}

For one-to-many's the owning side is assumed to be the "one" side of the relationship (in the above example this would be "Author.groovy"), but if you create a one-to-one relationship it is important to define the owning side of the relationship. For example if we had an Author class which had no relationship to the Book class (ie in a one-to-one scenario) it would be assumed that the book class "owned" the author which clearly is not the case!

The implications of this is that if you delete an instance of Book the delete operation will also delete the Author, not exactly the desired effect. To define the belongings side of one-to-one or many-to-one relationship we use the "belongsTo" property:

Book.groovy
class Book {
    @Property Long id
    @Property Long version

    @Property belongsTo = Author

    @Property Author author
    @Property String title
}

A class can also belong to multiple related classes like this:

@Property belongsTo = [Author,Publisher]

In other words:

  • an owner can have belongings
  • belongings use the 'belongsTo' property to refer to their owner(s)
  • if an owner dies, all his belongings vanish.

Relationship Summary

Here is a tabular summary of possible relationships between two classes A and B.

Relationships may be unidirectional (->) or bidirectional (<->) and have cardinalities one-to-one (1:1), one-to-many (1:m), many-to-one (m:1), and many-to-many (n:m).
The owner is marked as bold.

A:B unidirectional bidirectional
1:1 A -> B A <-> B ; B.belongsTo = [A]
1:m A -> Set ; A.relatesToMany= [B] A -> Set ; A.relatesToMany = [B]; B -> A
m:1 A -> B A -> B ; B -> Set ; B.relatesToMany = [A]
n:m n/a n/a

no belongsTo needed since in one-to-many always one is the owner.
opposite of

Optional and Transient properties

By default all properties are both persistent and required, to change that you can define List properties called "optionals" and "transients":

Book.groovy
class Book {
    @Property Long id
    @Property Long version

    @Property optionals = [ "releaseDate" ]
    @Property transients = [ "digitalCopy" ]

    @Property Author author
    @Property String title
    @Property String author
    @Property Date releaseDate
    @Property File digitalCopy
}

CRUD Operations

Grails domain classes use dynamic persistent methods to facilitate CRUD (Create/Read/Update/Delete) operations on persistent classes:

Create

To create entries in the database, domain class instances support a "save" method which cascades to the instance relationships. In the example below we only call "save" on the author and both the Author and Book instances are persisted:

def a = new Author(name:"Stephen King")
def b = new Book(title:"The Shining",author:b)
a.books.add(b)

// persist
a.save()

Note that in general it is recommended that you add logic to your domain classes to manage relationships and not add instances directly to the 'books' Set as done above. For example in this case it would be a appropriate to add a 'addBook' method:

def addBook(book) {
   if(!books)books = new HashSet()
   book.author = this
   books.add(book)
   return this
}

GORM will manage the persistence of your object model to the database, but won't manage the relationships for you so this kind of logic is good practice. The code would then become:

def a = new Author(name:"Stephen King")
a.addBook(new Book(title:"The Shining"))
 .addBook(new Book(title:"IT"))
 .save()

Read

Grails supports a number of ways of retrieving domain class instances, for more detail on querying see the section on Domain Class Querying, however to retrieve an instance if the "id" is known you can use the "get" static method:

Book.get(1)

Or to find all books the "findAll", "list" or "listOrderByX" static methods can be used:

Book.findAll() // retrieve all
   Book.list(10) // lists first 10 instances
   Book.listOrderByTitle() // lists all the instances ordered by the "title" property

Update

Updating is similar to creation in that the "save" method is used on existing domain class instances:

def b = Book.get(1)
b.releaseDate = new Date()
b.save()

Delete

Domain class instances can be removed from the database by using the "delete" instance method:

def b = Book.get(1)
b.delete()

Domain Class Querying

There are several ways to query for domain class instances, one of them being via Grails dynamic methods, from more details see DomainClass Dynamic Methods:

def results = Book.findByTitle("The Stand")
results = Book.findByTitleLike("Harry Pot%")
results = Book.findByReleaseDateBetween( firstDate, secondDate )
results = Book.findByReleaseDateGreaterThan( someDate )
results = Book.findByTitleLikeOrReleaseDateLessThan( "%Something%", someDate )

For more advanced queries or querying across objects graphs you can use Criteria (for a full reference see the section on Builders):

def c = Book.createCriteria()
def results = c {
     like("author.name", "Stephen%")
     between("releaseDate", firstDate, secondDate )
}

Otherwise, as Grails uses Hibernate internally you can use an HQL query:

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

Or with positional parameters:

def results = Book.find("from Book as b where b.title like ?", ["The Shi%"])
Document generated by Confluence on Mar 29, 2006 08:46