关联关系
2026/1/15大约 5 分钟GORM关联Relation
关联关系
一、Belongs To(属于)
1.1 定义关系
// 用户属于公司
type User struct {
ID uint
Name string
CompanyID uint
Company Company // 外键
}
type Company struct {
ID uint
Name string
}1.2 自定义外键
type User struct {
ID uint
Name string
CompanyID uint
Company Company `gorm:"foreignKey:CompanyID"`
}
// 自定义引用
type User struct {
ID uint
Name string
CompanyRefer string
Company Company `gorm:"foreignKey:CompanyRefer;references:Code"`
}
type Company struct {
ID uint
Code string
Name string
}1.3 预加载
// 预加载公司信息
var users []User
db.Preload("Company").Find(&users)
// 条件预加载
db.Preload("Company", "status = ?", 1).Find(&users)二、Has One(拥有一个)
2.1 定义关系
// 用户拥有一个信用卡
type User struct {
ID uint
Name string
CreditCard CreditCard
}
type CreditCard struct {
ID uint
Number string
UserID uint
}2.2 自定义外键
type User struct {
ID uint
CreditCard CreditCard `gorm:"foreignKey:UserRefer"`
}
type CreditCard struct {
ID uint
Number string
UserRefer uint
}2.3 操作示例
// 创建用户和信用卡
user := User{
Name: "张三",
CreditCard: CreditCard{Number: "1234567890"},
}
db.Create(&user)
// 查询
var user User
db.Preload("CreditCard").First(&user, 1)三、Has Many(拥有多个)
3.1 定义关系
// 用户拥有多个信用卡
type User struct {
ID uint
Name string
CreditCards []CreditCard
}
type CreditCard struct {
ID uint
Number string
UserID uint
}3.2 操作示例
// 创建
user := User{
Name: "张三",
CreditCards: []CreditCard{
{Number: "1111"},
{Number: "2222"},
},
}
db.Create(&user)
// 查询
var user User
db.Preload("CreditCards").First(&user, 1)
// 添加关联
db.Model(&user).Association("CreditCards").Append(&CreditCard{Number: "3333"})
// 替换关联
db.Model(&user).Association("CreditCards").Replace(&CreditCard{Number: "4444"})
// 删除关联
db.Model(&user).Association("CreditCards").Delete(&card)
// 清空关联
db.Model(&user).Association("CreditCards").Clear()
// 统计数量
count := db.Model(&user).Association("CreditCards").Count()四、Many To Many(多对多)
4.1 定义关系
// 用户和语言是多对多关系
type User struct {
ID uint
Name string
Languages []Language `gorm:"many2many:user_languages;"`
}
type Language struct {
ID uint
Name string
}
// 自动创建中间表 user_languages
// user_id, language_id4.2 自定义中间表
type User struct {
ID uint
Languages []Language `gorm:"many2many:user_languages;"`
}
type Language struct {
ID uint
Name string
}
// 自定义中间表结构
type UserLanguage struct {
UserID uint `gorm:"primaryKey"`
LanguageID uint `gorm:"primaryKey"`
Level int // 额外字段
CreatedAt time.Time
}4.3 指定中间表字段
type User struct {
ID uint
Languages []Language `gorm:"many2many:user_languages;joinForeignKey:UserID;joinReferences:LanguageID"`
}4.4 操作示例
// 创建
user := User{
Name: "张三",
Languages: []Language{
{Name: "Go"},
{Name: "Python"},
},
}
db.Create(&user)
// 查询
var user User
db.Preload("Languages").First(&user, 1)
// 添加关联
db.Model(&user).Association("Languages").Append(&Language{Name: "Java"})
// 查找关联
var languages []Language
db.Model(&user).Association("Languages").Find(&languages)五、预加载
5.1 基本预加载
// 预加载单个关联
db.Preload("Company").Find(&users)
// 预加载多个关联
db.Preload("Company").Preload("CreditCards").Find(&users)
// 预加载所有关联
db.Preload(clause.Associations).Find(&users)5.2 嵌套预加载
type User struct {
ID uint
Name string
Articles []Article
}
type Article struct {
ID uint
Title string
UserID uint
Comments []Comment
}
type Comment struct {
ID uint
Content string
ArticleID uint
}
// 嵌套预加载
db.Preload("Articles.Comments").Find(&users)5.3 条件预加载
// 带条件的预加载
db.Preload("Articles", "status = ?", 1).Find(&users)
// 使用函数
db.Preload("Articles", func(db *gorm.DB) *gorm.DB {
return db.Where("status = ?", 1).Order("created_at DESC")
}).Find(&users)5.4 自定义预加载
db.Preload("Articles", func(db *gorm.DB) *gorm.DB {
return db.Select("id", "title", "user_id").
Where("status = ?", 1).
Order("created_at DESC").
Limit(10)
}).Find(&users)六、Joins 预加载
6.1 基本 Joins
// 使用 Joins 预加载(一次查询)
db.Joins("Company").Find(&users)
// SELECT users.*, companies.* FROM users
// LEFT JOIN companies ON companies.id = users.company_id6.2 Joins 条件
db.Joins("Company", db.Where(&Company{Status: 1})).Find(&users)
// 或
db.InnerJoins("Company", db.Where(&Company{Status: 1})).Find(&users)6.3 Preload vs Joins
// Preload: 两次查询
db.Preload("Company").Find(&users)
// SELECT * FROM users;
// SELECT * FROM companies WHERE id IN (1,2,3);
// Joins: 一次查询
db.Joins("Company").Find(&users)
// SELECT users.*, companies.* FROM users
// LEFT JOIN companies ON companies.id = users.company_id七、关联模式
7.1 Association 方法
var user User
db.First(&user, 1)
// 查找关联
var cards []CreditCard
db.Model(&user).Association("CreditCards").Find(&cards)
// 添加关联
db.Model(&user).Association("CreditCards").Append(&CreditCard{Number: "1111"})
// 替换关联
db.Model(&user).Association("CreditCards").Replace(&CreditCard{Number: "2222"})
// 删除关联
db.Model(&user).Association("CreditCards").Delete(&card)
// 清空关联
db.Model(&user).Association("CreditCards").Clear()
// 统计数量
count := db.Model(&user).Association("CreditCards").Count()八、完整示例
8.1 博客系统
// 用户
type User struct {
ID uint
Name string
Articles []Article
Profile Profile
}
// 个人资料(一对一)
type Profile struct {
ID uint
UserID uint
Bio string
Avatar string
}
// 文章(一对多)
type Article struct {
ID uint
Title string
Content string
UserID uint
User User
Comments []Comment
Tags []Tag `gorm:"many2many:article_tags;"`
}
// 评论(一对多)
type Comment struct {
ID uint
Content string
ArticleID uint
UserID uint
User User
}
// 标签(多对多)
type Tag struct {
ID uint
Name string
Articles []Article `gorm:"many2many:article_tags;"`
}8.2 查询示例
// 查询用户及其文章和评论
var user User
db.Preload("Articles.Comments").
Preload("Articles.Tags").
Preload("Profile").
First(&user, 1)
// 查询文章及作者和标签
var article Article
db.Preload("User").
Preload("Comments.User").
Preload("Tags").
First(&article, 1)8.3 创建示例
// 创建文章并关联标签
article := Article{
Title: "Go 语言入门",
Content: "这是内容",
UserID: 1,
Tags: []Tag{
{Name: "Go"},
{Name: "编程"},
},
}
db.Create(&article)
// 为文章添加评论
comment := Comment{
Content: "写得不错",
ArticleID: article.ID,
UserID: 2,
}
db.Create(&comment)九、自引用关系
9.1 树形结构
type Category struct {
ID uint
Name string
ParentID *uint
Parent *Category `gorm:"foreignKey:ParentID"`
Children []Category `gorm:"foreignKey:ParentID"`
}
// 查询分类及子分类
var category Category
db.Preload("Children").First(&category, 1)
// 查询分类及父分类
db.Preload("Parent").First(&category, 1)9.2 好友关系
type User struct {
ID uint
Name string
Friends []User `gorm:"many2many:user_friends;"`
}十、性能优化
10.1 选择合适的预加载方式
// 一对一、一对多:使用 Preload
db.Preload("Profile").Find(&users)
// 需要条件过滤:使用 Joins
db.Joins("Company").Where("companies.status = ?", 1).Find(&users)10.2 限制预加载数据
// 只加载需要的字段
db.Preload("Articles", func(db *gorm.DB) *gorm.DB {
return db.Select("id", "title", "user_id")
}).Find(&users)
// 限制数量
db.Preload("Articles", func(db *gorm.DB) *gorm.DB {
return db.Order("created_at DESC").Limit(10)
}).Find(&users)