GORM supports four main types of associations, each with its specific use cases and configuration methods:
1. Belongs To
Definition: A model belongs to another model, with the foreign key in the current model.
Example: User belongs to a department
gotype Department struct { ID uint Name string } type User struct { gorm.Model Name string DepartmentID uint Department Department `gorm:"foreignKey:DepartmentID"` } // Usage var user User db.Preload("Department").First(&user, 1)
Features:
- Foreign key in the child model
- The "many" side of a one-to-many relationship
- Use
foreignKeytag to specify the foreign key field
2. Has One
Definition: A model has one associated model, with the foreign key in the associated model.
Example: User has one credit card
gotype CreditCard struct { gorm.Model Number string UserID uint User User `gorm:"foreignKey:UserID"` } type User struct { gorm.Model Name string CreditCard CreditCard } // Usage var user User db.Preload("CreditCard").First(&user, 1)
Features:
- Foreign key in the associated model
- One-to-one relationship
- Usually used for strong association scenarios
3. Has Many
Definition: A model has multiple associated models, with the foreign key in the associated models.
Example: User has multiple orders
gotype Order struct { gorm.Model UserID uint Amount float64 User User `gorm:"foreignKey:UserID"` } type User struct { gorm.Model Name string Orders []Order } // Usage var user User db.Preload("Orders").First(&user, 1) // Conditional preload db.Preload("Orders", "amount > ?", 100).First(&user, 1)
Features:
- Foreign key in the associated model
- One-to-many relationship
- Most commonly used association type
4. Many To Many
Definition: A many-to-many relationship between two models, implemented through an intermediate table (join table).
Example: Many-to-many relationship between users and roles
gotype User struct { gorm.Model Name string Roles []Role `gorm:"many2many:user_roles;"` } type Role struct { gorm.Model Name string Users []User `gorm:"many2many:user_roles;"` } // Usage var user User db.Preload("Roles").First(&user, 1) // Add association db.Model(&user).Association("Roles").Append(&Role{Name: "Admin"}) // Delete association db.Model(&user).Association("Roles").Delete(&Role{Name: "Admin"}) // Replace association db.Model(&user).Association("Roles").Replace(&Role{Name: "User"}) // Clear association db.Model(&user).Association("Roles").Clear() // Count count := db.Model(&user).Association("Roles").Count()
Features:
- Requires an intermediate table (join table)
- Default join table name: table1_table2
- Can customize join table name and fields
Custom Association Configuration
Custom foreign key
gotype User struct { gorm.Model CreditCards []CreditCard `gorm:"foreignKey:UserRefer"` } type CreditCard struct { gorm.Model Number string UserRefer uint }
Custom reference key
gotype User struct { gorm.Model Name string `gorm:"index"` CreditCard CreditCard `gorm:"foreignKey:UserName;references:Name"` } type CreditCard struct { gorm.Model Number string UserName string }
Custom many-to-many join table
gotype User struct { gorm.Model Roles []Role `gorm:"many2many:user_roles;joinForeignKey:UserID;joinReferences:RoleID"` } type Role struct { gorm.Model Name string Users []User `gorm:"many2many:user_roles;"` }
Preloading
Preloading is used to solve the N+1 query problem:
go// Basic preload db.Preload("Orders").Find(&users) // Nested preload db.Preload("Orders.Items").Find(&users) // Conditional preload db.Preload("Orders", "status = ?", "completed").Find(&users) // Multiple preloads db.Preload("Orders").Preload("CreditCard").Find(&users)
Association Operations
Association API
go// Find associations var roles []Role db.Model(&user).Association("Roles").Find(&roles) // Add association db.Model(&user).Association("Roles").Append(&Role{Name: "Admin"}) // Delete association db.Model(&user).Association("Roles").Delete(&Role{Name: "Admin"}) // Replace association db.Model(&user).Association("Roles").Replace([]Role{role1, role2}) // Clear association db.Model(&user).Association("Roles").Clear() // Count count := db.Model(&user).Association("Roles").Count()
Notes
- Foreign key naming: GORM defaults to using
{associated model name}IDas the foreign key name - Performance optimization: Use preloading reasonably to avoid N+1 queries
- Cascade delete: By default, deleting the primary record does not delete associated records, manual configuration is required
- Soft delete: GORM supports soft delete, pay attention to soft deleted records in association queries
- Transactions: Complex association operations are recommended to be executed in transactions