GORM Relationships
Harry
· 13 Sep 2026
· 2 views
Why Relationships Matter
Real data is connected: users have roles, orders have items, employees belong to departments, customers place orders. GORM models these relationships naturally and handles foreign keys, join tables, cascades, and fetch strategies for you.
One-to-One
class User {
String username
static hasOne = [profile: Profile]
}
class Profile {
String fullName
String phone
static belongsTo = [user: User]
}hasOne defines ownership; belongsTo enables cascading deletes.
One-to-Many
class Author {
String name
static hasMany = [books: Book]
}
class Book {
String title
static belongsTo = [author: Author]
}Grails adds the foreign key to the book table and handles cascades. Adding data uses addToBooks:
def author = new Author(name: "John")
author.addToBooks(new Book(title: "Grails Basics"))
author.save()Many-to-Many
class User {
String username
static hasMany = [roles: Role]
}
class Role {
String authority
static hasMany = [users: User]
}Grails creates a join table automatically. For production systems, an explicit join domain is the better choice.
The Join Domain Pattern
class UserRole {
User user
Role role
static mapping = {
id composite: ['user', 'role']
version false
}
}A join domain gives you better control, easier querying, and improved performance.
Lazy vs Eager Fetching
Grails defaults to lazy loading:
static mapping = {
books lazy: true
books fetch: 'join' // eager, use carefully
}Eager fetching hurts performance when misused - prefer joins or explicit queries instead.
Validation in Relationships
class Book {
Author author
static constraints = {
author nullable: false
}
}Querying Relationships
Book.findAllByAuthor(author)
def books = Book.where {
author.name == "John"
}.list()Common Mistakes
- Missing belongsTo and losing cascade behavior.
- Overusing many-to-many when a join domain fits better.
- Ignoring lazy loading and N+1 queries.
- Not indexing foreign-key columns.
Key Points
- hasOne/hasMany belongTo cover the classic relationship shapes.
- Use join domains for many-to-many in production.
- Default to lazy fetching; use joins deliberately.
- Validate relationships to keep data integrity.
