关联操作

定义好关联关系后,需要操作这些关联。GORM 提供了 Association 方法来管理关联数据。

Association 方法

获取关联模式:

var user User
db.First(&user, 1)
association := db.Model(&user).Association("Roles")

添加关联

添加单个关联:

var role Role
db.First(&role, 1)
db.Model(&user).Association("Roles").Append(&role)

添加多个关联:

var roles []Role
db.Find(&roles, []int{1, 2, 3})
db.Model(&user).Association("Roles").Append(roles)

一对多关联同样适用:

var article Article
db.First(&article, 1)
db.Model(&user).Association("Articles").Append(&article)

替换关联

替换所有关联:

var roles []Role
db.Find(&roles, []int{4, 5, 6})
db.Model(&user).Association("Roles").Replace(roles)

用户原来的角色会被删除,替换为新角色。

删除关联

删除单个关联:

var role Role
db.First(&role, 1)
db.Model(&user).Association("Roles").Delete(&role)

删除多个关联:

var roles []Role
db.Find(&roles, []int{1, 2})
db.Model(&user).Association("Roles").Delete(roles)

注意:只删除关联关系,不删除关联对象本身。

清空关联

清空所有关联:

db.Model(&user).Association("Roles").Clear()

统计关联数量

count := db.Model(&user).Association("Roles").Count()
fmt.Printf("用户有 %d 个角色\n", count)

查询关联

var roles []Role
db.Model(&user).Association("Roles").Find(&roles)

带条件查询:

var roles []Role
db.Model(&user).Association("Roles").Find(&roles, "name LIKE ?", "%admin%")

检查关联

检查是否有关联:

var role Role
db.First(&role, 1)
hasRole := db.Model(&user).Association("Roles").Contains(&role)
fmt.Println(hasRole)

批量模式

操作多个对象的关联:

var users []User
db.Find(&users, []int{1, 2, 3})

var role Role
db.First(&role, 1)

db.Model(&users).Association("Roles").Append(&role)

给所有用户添加同一个角色。

关联模式方法总结

方法说明
Append添加关联
Replace替换关联
Delete删除关联
Clear清空关联
Count统计数量
Find查询关联
Contains检查关联

关联标签

在模型定义中配置关联行为:

type User struct {
    ID    uint
    Roles []Role `gorm:"many2many:user_roles;constraint:OnDelete:CASCADE;"`
}

常用标签:

  • foreignKey - 外键字段
  • references - 引用字段
  • many2many - 多对多中间表
  • polymorphic - 多态关联
  • polymorphicValue - 多态类型值
  • constraint - 外键约束
  • OnDelete - 删除行为
  • OnUpdate - 更新行为

级联操作

创建时级联:

user := User{
    Name: "张三",
    Roles: []Role{
        {Name: "admin"},
        {Name: "editor"},
    },
}
db.Create(&user)

删除时级联:

db.Select("Roles").Delete(&user)

删除用户同时删除关联的角色(多对多会删除中间表记录)。

删除关联对象:

db.Select("Articles").Delete(&user)

删除用户同时删除用户的文章。

关联更新

更新关联对象:

var user User
db.Preload("Profile").First(&user, 1)
user.Profile.Bio = "新简介"
db.Save(&user)

或者:

db.Model(&user).Association("Profile").Update(&Profile{Bio: "新简介"})

关联创建

只创建关联:

var user User
db.First(&user, 1)

article := Article{Title: "新文章"}
db.Model(&user).Association("Articles").Append(&article)

实际案例

用户角色管理

func AssignRole(db *gorm.DB, userID uint, roleIDs []uint) error {
    var user User
    if err := db.First(&user, userID).Error; err != nil {
        return err
    }

    var roles []Role
    if err := db.Find(&roles, roleIDs).Error; err != nil {
        return err
    }

    return db.Model(&user).Association("Roles").Replace(roles)
}

func AddRole(db *gorm.DB, userID uint, roleID uint) error {
    var user User
    var role Role
    
    if err := db.First(&user, userID).Error; err != nil {
        return err
    }
    if err := db.First(&role, roleID).Error; err != nil {
        return err
    }

    return db.Model(&user).Association("Roles").Append(&role)
}

func RemoveRole(db *gorm.DB, userID uint, roleID uint) error {
    var user User
    var role Role
    
    if err := db.First(&user, userID).Error; err != nil {
        return err
    }
    if err := db.First(&role, roleID).Error; err != nil {
        return err
    }

    return db.Model(&user).Association("Roles").Delete(&role)
}

文章标签管理

func UpdateTags(db *gorm.DB, articleID uint, tagNames []string) error {
    var article Article
    if err := db.First(&article, articleID).Error; err != nil {
        return err
    }

    var tags []Tag
    for _, name := range tagNames {
        var tag Tag
        db.FirstOrCreate(&tag, Tag{Name: name})
        tags = append(tags, tag)
    }

    return db.Model(&article).Association("Tags").Replace(tags)
}

常见问题

关联不存在

操作不存在的关联会报错:

var user User
db.Model(&user).Association("Roles").Append(&role)

用户没有 ID,无法操作关联。确保对象已存在。

关联名称错误

db.Model(&user).Association("Role")

模型定义是 Roles,写错会报错。

零值问题

var role Role
db.Model(&user).Association("Roles").Delete(&role)

role 是零值,删除操作无效。确保关联对象有效。

小结

Association 方法提供了灵活的关联操作能力。掌握添加、删除、替换、查询这些操作,能有效管理模型间的关联关系。